AvaloniaPropertyTests.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. using System;
  2. using System.Collections.Generic;
  3. using Avalonia.Data;
  4. using Avalonia.Data.Core;
  5. using Avalonia.PropertyStore;
  6. using Xunit;
  7. namespace Avalonia.Base.UnitTests
  8. {
  9. public class AvaloniaPropertyTests
  10. {
  11. [Fact]
  12. public void Constructor_Sets_Properties()
  13. {
  14. var target = new TestProperty<string>("test", typeof(Class1));
  15. Assert.Equal("test", target.Name);
  16. Assert.Equal(typeof(string), target.PropertyType);
  17. Assert.Equal(typeof(Class1), target.OwnerType);
  18. }
  19. [Fact]
  20. public void Name_Cannot_Contain_Periods()
  21. {
  22. Assert.Throws<ArgumentException>(() => new TestProperty<string>("Foo.Bar", typeof(Class1)));
  23. }
  24. [Fact]
  25. public void GetMetadata_Returns_Supplied_Value()
  26. {
  27. var metadata = new TestMetadata();
  28. var target = new TestProperty<string>("test", typeof(Class1), metadata);
  29. Assert.Same(metadata, target.GetMetadata<Class1>());
  30. }
  31. [Fact]
  32. public void GetMetadata_Returns_Supplied_Value_For_Derived_Class()
  33. {
  34. var metadata = new TestMetadata();
  35. var target = new TestProperty<string>("test", typeof(Class1), metadata);
  36. Assert.Same(metadata, target.GetMetadata<Class2>());
  37. }
  38. [Fact]
  39. public void GetMetadata_Returns_TypeSafe_Metadata_For_Unrelated_Class()
  40. {
  41. var metadata = new TestMetadata(BindingMode.OneWayToSource, true, x => { _ = (StyledElement)x; });
  42. var target = new TestProperty<string>("test", typeof(Class3), metadata);
  43. var targetMetadata = (TestMetadata)target.GetMetadata<Class2>();
  44. Assert.Equal(metadata.DefaultBindingMode, targetMetadata.DefaultBindingMode);
  45. Assert.Equal(metadata.EnableDataValidation, targetMetadata.EnableDataValidation);
  46. Assert.Equal(null, targetMetadata.OwnerSpecificAction);
  47. }
  48. [Fact]
  49. public void GetMetadata_Returns_Overridden_Value()
  50. {
  51. var metadata = new TestMetadata();
  52. var overridden = new TestMetadata();
  53. var target = new TestProperty<string>("test", typeof(Class1), metadata);
  54. target.OverrideMetadata<Class2>(overridden);
  55. Assert.Same(overridden, target.GetMetadata<Class2>());
  56. }
  57. [Fact]
  58. public void OverrideMetadata_Should_Merge_Values()
  59. {
  60. var metadata = new TestMetadata(BindingMode.TwoWay);
  61. var notify = (Action<AvaloniaObject, bool>)((a, b) => { });
  62. var overridden = new TestMetadata();
  63. var target = new TestProperty<string>("test", typeof(Class1), metadata);
  64. target.OverrideMetadata<Class2>(overridden);
  65. var result = target.GetMetadata<Class2>();
  66. Assert.Equal(BindingMode.TwoWay, result.DefaultBindingMode);
  67. }
  68. [Fact]
  69. public void Default_Metadata_Cannot_Be_Changed_After_Property_Initialization()
  70. {
  71. var metadata = new TestMetadata();
  72. var property = new TestProperty<string>("test", typeof(Class1), metadata);
  73. Assert.Throws<InvalidOperationException>(() => metadata.Merge(new TestMetadata(), property));
  74. }
  75. [Fact]
  76. public void Overridden_Metadata_Cannot_Be_Changed_After_OverrideMetadata()
  77. {
  78. var metadata = new TestMetadata(BindingMode.TwoWay);
  79. var overridden = new TestMetadata();
  80. var property = new TestProperty<string>("test", typeof(Class1), metadata);
  81. property.OverrideMetadata<Class2>(overridden);
  82. Assert.Throws<InvalidOperationException>(() => overridden.Merge(new TestMetadata(), property));
  83. }
  84. [Fact]
  85. public void Changed_Observable_Fired()
  86. {
  87. var target = new Class1();
  88. string value = null;
  89. Class1.FooProperty.Changed.Subscribe(x => value = x.NewValue.GetValueOrDefault());
  90. target.SetValue(Class1.FooProperty, "newvalue");
  91. Assert.Equal("newvalue", value);
  92. }
  93. [Fact]
  94. public void Changed_Observable_Fired_Only_On_Effective_Value_Change()
  95. {
  96. var target = new Class1();
  97. var result = new List<string>();
  98. Class1.FooProperty.Changed.Subscribe(x => result.Add(x.NewValue.GetValueOrDefault()));
  99. target.SetValue(Class1.FooProperty, "animated", BindingPriority.Animation);
  100. target.SetValue(Class1.FooProperty, "local");
  101. Assert.Equal(new[] { "animated" }, result);
  102. }
  103. [Fact]
  104. public void Notify_Fired_Only_On_Effective_Value_Change()
  105. {
  106. var target = new Class1();
  107. target.SetValue(Class1.FooProperty, "animated", BindingPriority.Animation);
  108. target.SetValue(Class1.FooProperty, "local");
  109. Assert.Equal(2, target.NotifyCount);
  110. }
  111. [Fact]
  112. public void Property_Equals_Should_Handle_Null()
  113. {
  114. var p1 = new TestProperty<string>("p1", typeof(Class1));
  115. Assert.NotNull(p1);
  116. Assert.NotNull(p1);
  117. Assert.False(p1 == null);
  118. Assert.False(null == p1);
  119. Assert.False(p1.Equals(null));
  120. Assert.True((AvaloniaProperty)null == (AvaloniaProperty)null);
  121. }
  122. [Fact]
  123. public void PropertyMetadata_BindingMode_Default_Returns_OneWay()
  124. {
  125. var data = new TestMetadata(defaultBindingMode: BindingMode.Default);
  126. Assert.Equal(BindingMode.OneWay, data.DefaultBindingMode);
  127. }
  128. private class TestMetadata : AvaloniaPropertyMetadata
  129. {
  130. public Action<AvaloniaObject> OwnerSpecificAction { get; }
  131. public TestMetadata(BindingMode defaultBindingMode = BindingMode.Default,
  132. bool? enableDataValidation = null,
  133. Action<AvaloniaObject> ownerSpecificAction = null)
  134. : base(defaultBindingMode, enableDataValidation)
  135. {
  136. OwnerSpecificAction = ownerSpecificAction;
  137. }
  138. public override AvaloniaPropertyMetadata GenerateTypeSafeMetadata() =>
  139. new TestMetadata(DefaultBindingMode, EnableDataValidation, null);
  140. }
  141. private class TestProperty<TValue> : AvaloniaProperty<TValue>
  142. {
  143. public TestProperty(string name, Type ownerType, TestMetadata metadata = null)
  144. : base(name, ownerType, ownerType, metadata ?? new TestMetadata())
  145. {
  146. }
  147. public void OverrideMetadata<T>(AvaloniaPropertyMetadata metadata)
  148. {
  149. OverrideMetadata(typeof(T), metadata);
  150. }
  151. internal override IDisposable RouteBind(
  152. AvaloniaObject o,
  153. IObservable<object> source,
  154. BindingPriority priority)
  155. {
  156. throw new NotImplementedException();
  157. }
  158. internal override void RouteClearValue(AvaloniaObject o)
  159. {
  160. throw new NotImplementedException();
  161. }
  162. internal override void RouteCoerceDefaultValue(AvaloniaObject o)
  163. {
  164. throw new NotImplementedException();
  165. }
  166. internal override object RouteGetValue(AvaloniaObject o)
  167. {
  168. throw new NotImplementedException();
  169. }
  170. internal override object RouteGetBaseValue(AvaloniaObject o)
  171. {
  172. throw new NotImplementedException();
  173. }
  174. internal override IDisposable RouteSetValue(
  175. AvaloniaObject o,
  176. object value,
  177. BindingPriority priority)
  178. {
  179. throw new NotImplementedException();
  180. }
  181. internal override void RouteSetCurrentValue(AvaloniaObject o, object value)
  182. {
  183. throw new NotImplementedException();
  184. }
  185. internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o)
  186. {
  187. throw new NotImplementedException();
  188. }
  189. }
  190. private class Class1 : AvaloniaObject
  191. {
  192. public static readonly StyledProperty<string> FooProperty =
  193. AvaloniaProperty.Register<Class1, string>("Foo", "default",
  194. inherits: true,
  195. defaultBindingMode: BindingMode.OneWay,
  196. validate: null,
  197. coerce: null,
  198. enableDataValidation: false,
  199. notifying: FooNotifying);
  200. public int NotifyCount { get; private set; }
  201. private static void FooNotifying(AvaloniaObject o, bool n)
  202. {
  203. ++((Class1)o).NotifyCount;
  204. }
  205. }
  206. private class Class2 : Class1
  207. {
  208. }
  209. private class Class3 : AvaloniaObject
  210. {
  211. }
  212. }
  213. }