PrototypeTests.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #nullable enable
  2. using System.Collections.Generic;
  3. using Robust.Shared.Prototypes;
  4. using Robust.Shared.Serialization.Manager;
  5. using Robust.Shared.Serialization.Markdown;
  6. using Robust.Shared.Serialization.Markdown.Validation;
  7. using Robust.UnitTesting;
  8. namespace Content.IntegrationTests.Tests.PrototypeTests;
  9. public sealed class PrototypeTests
  10. {
  11. /// <summary>
  12. /// This test writes all known server prototypes as yaml files, then validates that the result is valid yaml.
  13. /// Can help prevent instances where prototypes have bad C# default values.
  14. /// </summary>
  15. [Test]
  16. public async Task TestAllServerPrototypesAreSerializable()
  17. {
  18. await using var pair = await PoolManager.GetServerClient();
  19. var context = new PrototypeSaveTest.TestEntityUidContext();
  20. await SaveThenValidatePrototype(pair.Server, "server", context);
  21. await pair.CleanReturnAsync();
  22. }
  23. /// <summary>
  24. /// This test writes all known client prototypes as yaml files, then validates that the result is valid yaml.
  25. /// Can help prevent instances where prototypes have bad C# default values.
  26. /// </summary>
  27. [Test]
  28. public async Task TestAllClientPrototypesAreSerializable()
  29. {
  30. await using var pair = await PoolManager.GetServerClient();
  31. var context = new PrototypeSaveTest.TestEntityUidContext();
  32. await SaveThenValidatePrototype(pair.Client, "client", context);
  33. await pair.CleanReturnAsync();
  34. }
  35. public async Task SaveThenValidatePrototype(RobustIntegrationTest.IntegrationInstance instance, string instanceId,
  36. PrototypeSaveTest.TestEntityUidContext ctx)
  37. {
  38. var protoMan = instance.ResolveDependency<IPrototypeManager>();
  39. Dictionary<Type, Dictionary<string, HashSet<ErrorNode>>> errors = default!;
  40. await instance.WaitPost(() => errors = protoMan.ValidateAllPrototypesSerializable(ctx));
  41. if (errors.Count == 0)
  42. return;
  43. Assert.Multiple(() =>
  44. {
  45. foreach (var (kind, ids) in errors)
  46. {
  47. foreach (var (id, nodes) in ids)
  48. {
  49. var msg = $"Error when validating {instanceId} prototype ({kind.Name}, {id}). Errors: \n";
  50. foreach (var errorNode in nodes)
  51. {
  52. msg += $" - {errorNode.ErrorReason}\n";
  53. }
  54. Assert.Fail(msg);
  55. }
  56. }
  57. });
  58. }
  59. /// <summary>
  60. /// This test writes all known prototypes as yaml files, reads them again, then serializes them again.
  61. /// </summary>
  62. [Test]
  63. public async Task ServerPrototypeSaveLoadSaveTest()
  64. {
  65. await using var pair = await PoolManager.GetServerClient();
  66. var context = new PrototypeSaveTest.TestEntityUidContext();
  67. await SaveLoadSavePrototype(pair.Server, context);
  68. await pair.CleanReturnAsync();
  69. }
  70. /// <summary>
  71. /// This test writes all known prototypes as yaml files, reads them again, then serializes them again.
  72. /// </summary>
  73. [Test]
  74. public async Task ClientPrototypeSaveLoadSaveTest()
  75. {
  76. await using var pair = await PoolManager.GetServerClient();
  77. var context = new PrototypeSaveTest.TestEntityUidContext();
  78. await SaveLoadSavePrototype(pair.Client, context);
  79. await pair.CleanReturnAsync();
  80. }
  81. private async Task SaveLoadSavePrototype(
  82. RobustIntegrationTest.IntegrationInstance instance,
  83. PrototypeSaveTest.TestEntityUidContext ctx)
  84. {
  85. var protoMan = instance.ResolveDependency<IPrototypeManager>();
  86. var seriMan = instance.ResolveDependency<ISerializationManager>();
  87. await instance.WaitAssertion(() =>
  88. {
  89. Assert.Multiple(() =>
  90. {
  91. foreach (var kind in protoMan.EnumeratePrototypeKinds())
  92. {
  93. foreach (var proto in protoMan.EnumeratePrototypes(kind))
  94. {
  95. var noException = TrySaveLoadSavePrototype(
  96. seriMan,
  97. protoMan,
  98. kind,
  99. proto,
  100. ctx);
  101. // This will probably throw an exception for each prototype of this kind.
  102. // We want to avoid having tests crash because they run out of time.
  103. if (!noException)
  104. break;
  105. }
  106. }
  107. });
  108. });
  109. }
  110. /// <returns>False if an exception was caught</returns>
  111. private bool TrySaveLoadSavePrototype(
  112. ISerializationManager seriMan,
  113. IPrototypeManager protoMan,
  114. Type kind,
  115. IPrototype proto,
  116. PrototypeSaveTest.TestEntityUidContext ctx)
  117. {
  118. DataNode first;
  119. DataNode second;
  120. try
  121. {
  122. first = seriMan.WriteValue(kind, proto, alwaysWrite: true, context:ctx);
  123. }
  124. catch (Exception e)
  125. {
  126. protoMan.TryGetMapping(kind, proto.ID, out var mapping);
  127. Assert.Fail($"Caught exception while writing {kind.Name} prototype {proto.ID}. Exception:\n{e}");
  128. return false;
  129. }
  130. object? obj;
  131. try
  132. {
  133. obj = seriMan.Read(kind, first, context:ctx);
  134. }
  135. catch (Exception e)
  136. {
  137. protoMan.TryGetMapping(kind, proto.ID, out var mapping);
  138. Assert.Fail($"Caught exception while re-reading {kind.Name} prototype {proto.ID}." +
  139. $"\nException:\n{e}" +
  140. $"\n\nOriginal yaml:\n{mapping}" +
  141. $"\n\nWritten yaml:\n{first}");
  142. return false;
  143. }
  144. Assert.That(obj?.GetType(), Is.EqualTo(proto.GetType()));
  145. var deserialized = (IPrototype) obj!;
  146. try
  147. {
  148. second = seriMan.WriteValue(kind, deserialized, alwaysWrite: true, context:ctx);
  149. }
  150. catch (Exception e)
  151. {
  152. protoMan.TryGetMapping(kind, proto.ID, out var mapping);
  153. Assert.Fail($"Caught exception while re-writing {kind.Name} prototype {proto.ID}." +
  154. $"\nException:\n{e}" +
  155. $"\n\nOriginal yaml:\n{mapping}" +
  156. $"\n\nWritten yaml:\n{first}");
  157. return false;
  158. }
  159. var diff = first.Except(second);
  160. if (diff == null || diff.IsEmpty)
  161. return true;
  162. protoMan.TryGetMapping(kind, proto.ID, out var orig);
  163. Assert.Fail($"Re-written {kind.Name} prototype {proto.ID} differs." +
  164. $"\nYaml diff:\n{diff}" +
  165. $"\n\nOriginal yaml:\n{orig}" +
  166. $"\n\nWritten yaml:\n{first}" +
  167. $"\n\nRe-written Yaml:\n{second}");
  168. return true;
  169. }
  170. }