StreamInfoTests.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using MediaBrowser.Model.Dlna;
  5. using Xunit;
  6. namespace Jellyfin.Model.Tests.Dlna;
  7. public class StreamInfoTests
  8. {
  9. private const string BaseUrl = "/test/";
  10. private const int RandomSeed = 298347823;
  11. /// <summary>
  12. /// Returns a random float.
  13. /// </summary>
  14. /// <param name="random">The <see cref="Random"/> instance.</param>
  15. /// <returns>A random <see cref="float"/>.</returns>
  16. private static float RandomFloat(Random random)
  17. {
  18. var buffer = new byte[4];
  19. random.NextBytes(buffer);
  20. return BitConverter.ToSingle(buffer, 0);
  21. }
  22. /// <summary>
  23. /// Creates a random array.
  24. /// </summary>
  25. /// <param name="random">The <see cref="Random"/> instance.</param>
  26. /// <param name="elementType">The element <see cref="Type"/> of the array.</param>
  27. /// <returns>An <see cref="Array"/> of <see cref="Type"/>.</returns>
  28. private static object? RandomArray(Random random, Type? elementType)
  29. {
  30. if (elementType is null)
  31. {
  32. return null;
  33. }
  34. if (elementType == typeof(string))
  35. {
  36. return RandomStringArray(random);
  37. }
  38. if (elementType == typeof(int))
  39. {
  40. return RandomIntArray(random);
  41. }
  42. if (elementType.IsEnum)
  43. {
  44. var values = Enum.GetValues(elementType);
  45. return RandomIntArray(random, 0, values.Length - 1);
  46. }
  47. throw new ArgumentException("Unsupported array type " + elementType.ToString());
  48. }
  49. /// <summary>
  50. /// Creates a random length string.
  51. /// </summary>
  52. /// <param name="random">The <see cref="Random"/> instance.</param>
  53. /// <param name="minLength">The minimum length of the string.</param>
  54. /// <param name="maxLength">The maximum length of the string.</param>
  55. /// <returns>The string.</returns>
  56. private static string RandomString(Random random, int minLength = 0, int maxLength = 256)
  57. {
  58. var len = random.Next(minLength, maxLength);
  59. var sb = new StringBuilder(len);
  60. while (len > 0)
  61. {
  62. sb.Append((char)random.Next(65, 97));
  63. len--;
  64. }
  65. return sb.ToString();
  66. }
  67. /// <summary>
  68. /// Creates a random long.
  69. /// </summary>
  70. /// <param name="random">The <see cref="Random"/> instance.</param>
  71. /// <param name="min">Min value.</param>
  72. /// <param name="max">Max value.</param>
  73. /// <returns>A random <see cref="long"/> between <paramref name="min"/> and <paramref name="max"/>.</returns>
  74. private static long RandomLong(Random random, long min = -9223372036854775808, long max = 9223372036854775807)
  75. {
  76. long result = random.Next((int)(min >> 32), (int)(max >> 32));
  77. result <<= 32;
  78. result |= (long)random.Next((int)(min >> 32) << 32, (int)(max >> 32) << 32);
  79. return result;
  80. }
  81. /// <summary>
  82. /// Creates a random string array containing between <paramref name="minLength"/> and <paramref name="maxLength"/>.
  83. /// </summary>
  84. /// <param name="random">The <see cref="Random"/> instance.</param>
  85. /// <param name="minLength">The minimum number of elements.</param>
  86. /// <param name="maxLength">The maximum number of elements.</param>
  87. /// <returns>A random <see cref="string[]"/> instance.</returns>
  88. private static string[] RandomStringArray(Random random, int minLength = 0, int maxLength = 9)
  89. {
  90. var len = random.Next(minLength, maxLength);
  91. var arr = new List<string>(len);
  92. while (len > 0)
  93. {
  94. arr.Add(RandomString(random, 1, 30));
  95. len--;
  96. }
  97. return arr.ToArray();
  98. }
  99. /// <summary>
  100. /// Creates a random int array containing between <paramref name="minLength"/> and <paramref name="maxLength"/>.
  101. /// </summary>
  102. /// <param name="random">The <see cref="Random"/> instance.</param>
  103. /// <param name="minLength">The minimum number of elements.</param>
  104. /// <param name="maxLength">The maximum number of elements.</param>
  105. /// <returns>A random <see cref="int[]"/> instance.</returns>
  106. private static int[] RandomIntArray(Random random, int minLength = 0, int maxLength = 9)
  107. {
  108. var len = random.Next(minLength, maxLength);
  109. var arr = new List<int>(len);
  110. while (len > 0)
  111. {
  112. arr.Add(random.Next());
  113. len--;
  114. }
  115. return arr.ToArray();
  116. }
  117. /// <summary>
  118. /// Fills most properties with random data.
  119. /// </summary>
  120. /// <param name="destination">The instance to fill with data.</param>
  121. private static void FillAllProperties<T>(T destination)
  122. {
  123. var random = new Random(RandomSeed);
  124. var objectType = destination!.GetType();
  125. foreach (var property in objectType.GetProperties())
  126. {
  127. if (!(property.CanRead && property.CanWrite))
  128. {
  129. continue;
  130. }
  131. var type = property.PropertyType;
  132. // If nullable, then set it to null, 25% of the time.
  133. if (Nullable.GetUnderlyingType(type) is not null)
  134. {
  135. if (random.Next(0, 4) == 0)
  136. {
  137. // Set it to null.
  138. property.SetValue(destination, null);
  139. continue;
  140. }
  141. }
  142. if (type == typeof(Guid))
  143. {
  144. property.SetValue(destination, Guid.NewGuid());
  145. continue;
  146. }
  147. if (type.IsEnum)
  148. {
  149. Array values = Enum.GetValues(property.PropertyType);
  150. property.SetValue(destination, values.GetValue(random.Next(0, values.Length - 1)));
  151. continue;
  152. }
  153. if (type == typeof(long))
  154. {
  155. property.SetValue(destination, RandomLong(random));
  156. continue;
  157. }
  158. if (type == typeof(string))
  159. {
  160. property.SetValue(destination, RandomString(random));
  161. continue;
  162. }
  163. if (type == typeof(bool))
  164. {
  165. property.SetValue(destination, random.Next(0, 1) == 1);
  166. continue;
  167. }
  168. if (type == typeof(float))
  169. {
  170. property.SetValue(destination, RandomFloat(random));
  171. continue;
  172. }
  173. if (type.IsArray)
  174. {
  175. property.SetValue(destination, RandomArray(random, type.GetElementType()));
  176. continue;
  177. }
  178. }
  179. }
  180. [InlineData(DlnaProfileType.Audio)]
  181. [InlineData(DlnaProfileType.Video)]
  182. [InlineData(DlnaProfileType.Photo)]
  183. [Theory]
  184. public void Test_Blank_Url_Method(DlnaProfileType type)
  185. {
  186. var streamInfo = new LegacyStreamInfo(Guid.Empty, type)
  187. {
  188. DeviceProfile = new DeviceProfile()
  189. };
  190. string legacyUrl = streamInfo.ToUrl_Original(BaseUrl, "123");
  191. // New version will return and & after the ? due to optional parameters.
  192. string newUrl = streamInfo.ToUrl(BaseUrl, "123", null).Replace("?&", "?", StringComparison.OrdinalIgnoreCase);
  193. Assert.Equal(legacyUrl, newUrl, ignoreCase: true);
  194. }
  195. [Fact]
  196. public void Fuzzy_Comparison()
  197. {
  198. var streamInfo = new LegacyStreamInfo(Guid.Empty, DlnaProfileType.Video)
  199. {
  200. DeviceProfile = new DeviceProfile()
  201. };
  202. for (int i = 0; i < 100000; i++)
  203. {
  204. FillAllProperties(streamInfo);
  205. string legacyUrl = streamInfo.ToUrl_Original(BaseUrl, "123");
  206. // New version will return and & after the ? due to optional parameters.
  207. string newUrl = streamInfo.ToUrl(BaseUrl, "123", null).Replace("?&", "?", StringComparison.OrdinalIgnoreCase);
  208. Assert.Equal(legacyUrl, newUrl, ignoreCase: true);
  209. }
  210. }
  211. }