BindingExpressionTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Globalization;
  6. using System.Reactive.Linq;
  7. using System.Threading;
  8. using Avalonia.Data;
  9. using Avalonia.Markup.Data;
  10. using Avalonia.UnitTests;
  11. using Moq;
  12. using Xunit;
  13. namespace Avalonia.Markup.UnitTests.Data
  14. {
  15. public class BindingExpressionTests : IClassFixture<InvariantCultureFixture>
  16. {
  17. [Fact]
  18. public async void Should_Get_Simple_Property_Value()
  19. {
  20. var data = new Class1 { StringValue = "foo" };
  21. var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(string));
  22. var result = await target.Take(1);
  23. Assert.Equal("foo", result);
  24. }
  25. [Fact]
  26. public void Should_Set_Simple_Property_Value()
  27. {
  28. var data = new Class1 { StringValue = "foo" };
  29. var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(string));
  30. target.OnNext("bar");
  31. Assert.Equal("bar", data.StringValue);
  32. }
  33. [Fact]
  34. public async void Should_Convert_Get_String_To_Double()
  35. {
  36. Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
  37. var data = new Class1 { StringValue = "5.6" };
  38. var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double));
  39. var result = await target.Take(1);
  40. Assert.Equal(5.6, result);
  41. }
  42. [Fact]
  43. public async void Getting_Invalid_Double_String_Should_Return_BindingError()
  44. {
  45. var data = new Class1 { StringValue = "foo" };
  46. var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double));
  47. var result = await target.Take(1);
  48. Assert.IsType<BindingNotification>(result);
  49. }
  50. [Fact]
  51. public async void Should_Coerce_Get_Null_Double_String_To_UnsetValue()
  52. {
  53. var data = new Class1 { StringValue = null };
  54. var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double));
  55. var result = await target.Take(1);
  56. Assert.Equal(AvaloniaProperty.UnsetValue, result);
  57. }
  58. [Fact]
  59. public void Should_Convert_Set_String_To_Double()
  60. {
  61. Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
  62. var data = new Class1 { StringValue = (5.6).ToString() };
  63. var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double));
  64. target.OnNext(6.7);
  65. Assert.Equal((6.7).ToString(), data.StringValue);
  66. }
  67. [Fact]
  68. public async void Should_Convert_Get_Double_To_String()
  69. {
  70. Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
  71. var data = new Class1 { DoubleValue = 5.6 };
  72. var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string));
  73. var result = await target.Take(1);
  74. Assert.Equal((5.6).ToString(), result);
  75. }
  76. [Fact]
  77. public void Should_Convert_Set_Double_To_String()
  78. {
  79. Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
  80. var data = new Class1 { DoubleValue = 5.6 };
  81. var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string));
  82. target.OnNext("6.7");
  83. Assert.Equal(6.7, data.DoubleValue);
  84. }
  85. [Fact]
  86. public async void Should_Return_BindingNotification_With_FallbackValue_For_NonConvertibe_Target_Value()
  87. {
  88. Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
  89. var data = new Class1 { StringValue = "foo" };
  90. var target = new BindingExpression(
  91. new ExpressionObserver(data, "StringValue"),
  92. typeof(int),
  93. 42,
  94. DefaultValueConverter.Instance);
  95. var result = await target.Take(1);
  96. Assert.Equal(
  97. new BindingNotification(
  98. new InvalidCastException("'foo' is not a valid number."),
  99. BindingErrorType.Error,
  100. 42),
  101. result);
  102. }
  103. [Fact]
  104. public async void Should_Return_BindingNotification_With_FallbackValue_For_NonConvertibe_Target_Value_With_Data_Validation()
  105. {
  106. Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
  107. var data = new Class1 { StringValue = "foo" };
  108. var target = new BindingExpression(
  109. new ExpressionObserver(data, "StringValue", true),
  110. typeof(int),
  111. 42,
  112. DefaultValueConverter.Instance);
  113. var result = await target.Take(1);
  114. Assert.Equal(
  115. new BindingNotification(
  116. new InvalidCastException("'foo' is not a valid number."),
  117. BindingErrorType.Error,
  118. 42),
  119. result);
  120. }
  121. [Fact]
  122. public async void Should_Return_BindingNotification_For_Invalid_FallbackValue()
  123. {
  124. Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
  125. var data = new Class1 { StringValue = "foo" };
  126. var target = new BindingExpression(
  127. new ExpressionObserver(data, "StringValue"),
  128. typeof(int),
  129. "bar",
  130. DefaultValueConverter.Instance);
  131. var result = await target.Take(1);
  132. Assert.Equal(
  133. new BindingNotification(
  134. new AggregateException(
  135. new InvalidCastException("Could not convert 'foo' to 'System.Int32'"),
  136. new InvalidCastException("Could not convert FallbackValue 'bar' to 'System.Int32'")),
  137. BindingErrorType.Error),
  138. result);
  139. }
  140. [Fact]
  141. public async void Should_Return_BindingNotification_For_Invalid_FallbackValue_With_Data_Validation()
  142. {
  143. Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
  144. var data = new Class1 { StringValue = "foo" };
  145. var target = new BindingExpression(
  146. new ExpressionObserver(data, "StringValue", true),
  147. typeof(int),
  148. "bar",
  149. DefaultValueConverter.Instance);
  150. var result = await target.Take(1);
  151. Assert.Equal(
  152. new BindingNotification(
  153. new AggregateException(
  154. new InvalidCastException("Could not convert 'foo' to 'System.Int32'"),
  155. new InvalidCastException("Could not convert FallbackValue 'bar' to 'System.Int32'")),
  156. BindingErrorType.Error),
  157. result);
  158. }
  159. [Fact]
  160. public void Setting_Invalid_Double_String_Should_Not_Change_Target()
  161. {
  162. var data = new Class1 { DoubleValue = 5.6 };
  163. var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string));
  164. target.OnNext("foo");
  165. Assert.Equal(5.6, data.DoubleValue);
  166. }
  167. [Fact]
  168. public void Setting_Invalid_Double_String_Should_Use_FallbackValue()
  169. {
  170. var data = new Class1 { DoubleValue = 5.6 };
  171. var target = new BindingExpression(
  172. new ExpressionObserver(data, "DoubleValue"),
  173. typeof(string),
  174. "9.8",
  175. DefaultValueConverter.Instance);
  176. target.OnNext("foo");
  177. Assert.Equal(9.8, data.DoubleValue);
  178. }
  179. [Fact]
  180. public void Should_Coerce_Setting_Null_Double_To_Default_Value()
  181. {
  182. var data = new Class1 { DoubleValue = 5.6 };
  183. var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string));
  184. target.OnNext(null);
  185. Assert.Equal(0, data.DoubleValue);
  186. }
  187. [Fact]
  188. public void Should_Coerce_Setting_UnsetValue_Double_To_Default_Value()
  189. {
  190. var data = new Class1 { DoubleValue = 5.6 };
  191. var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string));
  192. target.OnNext(AvaloniaProperty.UnsetValue);
  193. Assert.Equal(0, data.DoubleValue);
  194. }
  195. [Fact]
  196. public void Should_Pass_ConverterParameter_To_Convert()
  197. {
  198. var data = new Class1 { DoubleValue = 5.6 };
  199. var converter = new Mock<IValueConverter>();
  200. var target = new BindingExpression(
  201. new ExpressionObserver(data, "DoubleValue"),
  202. typeof(string),
  203. converter.Object,
  204. converterParameter: "foo");
  205. target.Subscribe(_ => { });
  206. converter.Verify(x => x.Convert(5.6, typeof(string), "foo", CultureInfo.CurrentUICulture));
  207. }
  208. [Fact]
  209. public void Should_Pass_ConverterParameter_To_ConvertBack()
  210. {
  211. var data = new Class1 { DoubleValue = 5.6 };
  212. var converter = new Mock<IValueConverter>();
  213. var target = new BindingExpression(
  214. new ExpressionObserver(data, "DoubleValue"),
  215. typeof(string),
  216. converter.Object,
  217. converterParameter: "foo");
  218. target.OnNext("bar");
  219. converter.Verify(x => x.ConvertBack("bar", typeof(double), "foo", CultureInfo.CurrentUICulture));
  220. }
  221. [Fact]
  222. public void Should_Handle_DataValidation()
  223. {
  224. var data = new Class1 { DoubleValue = 5.6 };
  225. var converter = new Mock<IValueConverter>();
  226. var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue", true), typeof(string));
  227. var result = new List<object>();
  228. target.Subscribe(x => result.Add(x));
  229. target.OnNext(1.2);
  230. target.OnNext("3.4");
  231. target.OnNext("bar");
  232. Assert.Equal(
  233. new[]
  234. {
  235. new BindingNotification("5.6"),
  236. new BindingNotification("1.2"),
  237. new BindingNotification("3.4"),
  238. new BindingNotification(
  239. new InvalidCastException("'bar' is not a valid number."),
  240. BindingErrorType.Error)
  241. },
  242. result);
  243. }
  244. private class Class1 : NotifyingBase
  245. {
  246. private string _stringValue;
  247. private double _doubleValue;
  248. public string StringValue
  249. {
  250. get { return _stringValue; }
  251. set { _stringValue = value; RaisePropertyChanged(); }
  252. }
  253. public double DoubleValue
  254. {
  255. get { return _doubleValue; }
  256. set { _doubleValue = value; RaisePropertyChanged(); }
  257. }
  258. }
  259. }
  260. }