QueryableParityTest.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the Apache 2.0 License.
  3. // See the LICENSE file in the project root for more information.
  4. using System;
  5. using System.ComponentModel;
  6. using System.Linq;
  7. using System.Linq.Expressions;
  8. using System.Reflection;
  9. using System.Runtime.CompilerServices;
  10. using Xunit;
  11. namespace Tests
  12. {
  13. public class QueryableParity : Tests
  14. {
  15. [Fact]
  16. public void Queryable_Enumerable_Parity()
  17. {
  18. var enu = typeof(EnumerableEx).GetRuntimeMethods().Where(m => m.IsStatic && m.IsPublic).ToList();
  19. var qry = typeof(QueryableEx).GetRuntimeMethods().Where(m => m.IsStatic && m.IsPublic).ToList();
  20. var onlyInObs = enu.Select(m => m.Name).Except(qry.Select(m => m.Name)).Except(new[] { "ForEach", "ToEnumerable", "Multicast", "GetAwaiter", "ToEvent", "ToEventPattern", "ForEachAsync" }).ToList();
  21. var onlyInQbs = qry.Select(m => m.Name).Except(enu.Select(m => m.Name)).Except(new[] { "ToQueryable", "get_Provider", "Empty", "Range" }).ToList();
  22. Assert.True(onlyInObs.Count == 0, "Missing Queryable operator: " + string.Join(", ", onlyInObs.ToArray()));
  23. Assert.True(onlyInQbs.Count == 0, "Missing Enumerable operator: " + string.Join(", ", onlyInQbs.ToArray()));
  24. var enus = enu.GroupBy(m => m.Name);
  25. var qrys = qry.GroupBy(m => m.Name);
  26. var mtch = (from o in enus
  27. join q in qrys on o.Key equals q.Key
  28. select new { Name = o.Key, Enumerable = o.ToList(), Queryable = q.ToList() })
  29. .ToList();
  30. bool filterReturn(Type t)
  31. {
  32. if (t.GetTypeInfo().IsGenericType)
  33. {
  34. var gd = t.GetGenericTypeDefinition();
  35. if (gd == typeof(IBuffer<>))
  36. {
  37. return false;
  38. }
  39. }
  40. return true;
  41. }
  42. bool filterHelper(MethodInfo m)
  43. {
  44. return !m.IsDefined(typeof(EditorBrowsableAttribute), false);
  45. }
  46. foreach (var group in mtch)
  47. {
  48. var oss = group.Enumerable
  49. .Where(m => filterReturn(m.ReturnType))
  50. .Select(m => GetSignature(m, false))
  51. .OrderBy(x => x).ToList();
  52. var qss = group.Queryable
  53. .Where(m => filterHelper(m))
  54. .Select(m => GetSignature(m, true))
  55. .OrderBy(x => x).ToList();
  56. if (!group.Name.Equals("Create"))
  57. {
  58. Assert.True(oss.SequenceEqual(qss), "Mismatch between QueryableEx and EnumerableEx for " + group.Name);
  59. }
  60. }
  61. }
  62. public static string GetSignature(MethodInfo m, bool correct)
  63. {
  64. var ps = m.GetParameters();
  65. var pss = ps.AsEnumerable();
  66. if (correct && ps.Length > 0 && ps[0].ParameterType == typeof(IQueryProvider))
  67. {
  68. pss = pss.Skip(1);
  69. }
  70. var gens = m.IsGenericMethod ? string.Format("<{0}>", string.Join(", ", m.GetGenericArguments().Select(a => GetTypeName(a, correct)).ToArray())) : "";
  71. var pars = string.Join(", ", pss.Select(p => (p.IsDefined(typeof(ParamArrayAttribute)) ? "params " : "") + GetTypeName(p.ParameterType, correct) + " " + p.Name).ToArray());
  72. if (m.IsDefined(typeof(ExtensionAttribute)))
  73. {
  74. if (pars.StartsWith("IQbservable") || pars.StartsWith("IQueryable"))
  75. {
  76. pars = "this " + pars;
  77. }
  78. }
  79. return string.Format("{0} {1}{2}({3})", GetTypeName(m.ReturnType, correct), m.Name, gens, pars);
  80. }
  81. public static string GetTypeName(Type t, bool correct)
  82. {
  83. if (t.GetTypeInfo().IsGenericType)
  84. {
  85. var gtd = t.GetGenericTypeDefinition();
  86. if (gtd == typeof(Expression<>))
  87. {
  88. return GetTypeName(t.GenericTypeArguments[0], false);
  89. }
  90. var args = string.Join(", ", t.GenericTypeArguments.Select(a => GetTypeName(a, false)).ToArray());
  91. var len = t.Name.IndexOf('`');
  92. var name = len >= 0 ? t.Name.Substring(0, len) : t.Name;
  93. if (correct && name == "IQbservable")
  94. {
  95. name = "IObservable";
  96. }
  97. if (correct && name == "IQueryable")
  98. {
  99. name = "IEnumerable";
  100. }
  101. return string.Format("{0}<{1}>", name, args);
  102. }
  103. if (t.IsArray)
  104. {
  105. return GetTypeName(t.GetElementType(), correct) + "[]";
  106. }
  107. return t.Name;
  108. }
  109. [Fact]
  110. public void QueryableRetarget1()
  111. {
  112. var res = QueryableEx.Provider.Empty<int>().AsEnumerable().ToList();
  113. Assert.True(res.SequenceEqual(new int[0]));
  114. }
  115. [Fact]
  116. public void QueryableRetarget2()
  117. {
  118. var res = QueryableEx.Provider.Return(42).AsEnumerable().ToList();
  119. Assert.True(res.SequenceEqual(new[] { 42 }));
  120. }
  121. [Fact]
  122. public void QueryableRetarget3()
  123. {
  124. #if NETCOREAPP2_1 || WINDOWS_UWP
  125. var res = QueryableEx.TakeLast(Enumerable.Range(0, 10).AsQueryable(), 2).AsEnumerable().ToList();
  126. #else
  127. var res = Enumerable.Range(0, 10).AsQueryable().TakeLast(2).AsEnumerable().ToList();
  128. #endif
  129. Assert.True(res.SequenceEqual(new[] { 8, 9 }));
  130. }
  131. [Fact]
  132. public void QueryableRetarget4()
  133. {
  134. var res = QueryableEx.Provider.Range(0, 10).AsEnumerable().ToList();
  135. Assert.True(res.SequenceEqual(Enumerable.Range(0, 10)));
  136. }
  137. }
  138. }