2using System.Collections.Generic;
4using System.Reflection;
5using System.Diagnostics;
6using System.Runtime.CompilerServices;
10using Microsoft.Xna.Framework;
11using Microsoft.Xna.Framework.Input;
12using Microsoft.Xna.Framework.Graphics;
16using System.Threading;
22 public class IgnoreAttribute : System.Attribute { }
23 public class HideFromAutoTestRunnerAttribute : System.Attribute { }
25 public enum TestStatus
32 public record TestReport(Type TestClass, MethodInfo TestMethod, TestStatus Status,
string DeepCompare =
"");
34 public class TestingConfig
36 public static string ConfigPath => Path.Combine(CUITest.IgnoredDataPath,
"Config.xml");
38 public event Action OnChanged;
40 private string autorun =
"";
46 autorun = value ??
"";
54 if (!File.Exists(ConfigPath))
return;
55 XDocument xdoc = XDocument.Load(ConfigPath);
56 XElement root = xdoc.Element(
"Autorun");
57 Autorun = root?.Value;
62 XDocument xdoc =
new XDocument();
63 xdoc.Add(
new XElement(
"Autorun", Autorun ??
""));
64 xdoc.Save(ConfigPath);
68 public class AutomaticTestRunner
71 public static string TestStashPath => Path.Combine(CUITest.IgnoredDataPath,
"Stash");
73 public AutoTestGUI GUI;
74 public TestingConfig Config;
77 public IEnumerable<Type> GetAllTestClasses()
79 Assembly targetAssembly = Assembly.GetAssembly(typeof(FillMethods));
81 return targetAssembly.GetTypes()
82 .Where(t => t.IsSubclassOf(typeof(FillMethods)));
85 public IEnumerable<MethodInfo> GetAllTestMethodsForAClass(Type type)
87 return type.GetMethods(BindingFlags.Public | BindingFlags.Static)
88 .Where(mi => FillMethods.IsTestMethod(mi));
91 public IEnumerable<MethodInfo> GetAllAutoTestMethodsForAClass(Type type)
93 return type.GetMethods(BindingFlags.Public | BindingFlags.Static)
94 .Where(mi => FillMethods.IsTestMethod(mi) && !Attribute.IsDefined(mi, typeof(HideFromAutoTestRunnerAttribute)));
97 public string[] GetClassMethodPairs()
99 List<string> all =
new();
100 foreach (Type type
in GetAllTestClasses())
102 foreach (MethodInfo mi
in GetAllTestMethodsForAClass(type))
104 all.Add($
"{type.Name}.{mi.Name}");
107 return all.ToArray();
110 public static (Type, MethodInfo) DeconstructFullName(
string fullName)
112 string typeName = fullName.Split(
'.').ElementAtOrDefault(0);
113 string methodName = fullName.Split(
'.').ElementAtOrDefault(1);
115 Assembly targetAssembly = Assembly.GetAssembly(typeof(FillMethods));
117 Type type = targetAssembly.GetTypes().Find(T => T.Name == typeName);
118 if (type ==
null)
return (
null,
null);
120 MethodInfo method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
121 return (type, method);
125 public string DeepCompare(
string real,
string stashed)
127 StringBuilder result =
new StringBuilder(
"‖color:gray‖");
129 int minLength = Math.Min(real.Length, stashed.Length);
132 for (
int i = 0; i < minLength; i++)
136 if (real[i] != stashed[i]) { ok =
false; result.Append(
"‖end‖‖color:cyan‖"); }
140 if (real[i] == stashed[i]) { ok =
true; result.Append(
"‖end‖‖color:gray‖"); }
143 result.Append(real[i]);
145 result.Append(
"‖end‖");
147 return result.ToString();
150 private MethodInfo activeTest;
public MethodInfo ActiveTest
155 if (activeTest == value)
157 CUITest.ClearTestChamber();
163 if (activeTest !=
null)
165 if (TestChamber ==
null) CUITest.CreateTestChamber();
166 if (TestChamber.Parent ==
null) CUITest.OpenTestChamber();
168 CUITest.ClearTestChamber();
172 CUITest.ClearTestChamber();
173 if (TestChamber.Parent !=
null) CUITest.CloseTestChamber();
178 public event Action<TestReport> OnTestReport;
180 public void StopTest() => ActiveTest =
null;
182 public void RunAllTests()
184 foreach (Type testClass
in GetAllTestClasses())
186 foreach (MethodInfo testMethod
in GetAllAutoTestMethodsForAClass(testClass))
188 RunTest(testClass, testMethod);
193 public void ClearStash()
195 foreach (Type testClass
in GetAllTestClasses())
197 foreach (MethodInfo testMethod
in GetAllTestMethodsForAClass(testClass))
199 if (!Directory.Exists(Path.Combine(TestStashPath, testClass.Name)))
201 Directory.CreateDirectory(Path.Combine(TestStashPath, testClass.Name));
204 string testPath = Path.Combine(TestStashPath, testClass.Name, $
"{testMethod.Name}.xml");
206 if (File.Exists(testPath))
208 File.Delete(testPath);
209 OnTestReport?.Invoke(
new TestReport(testClass, testMethod, TestStatus.Unknown));
215 public void RunTest(
string fullName)
217 (Type testClass, MethodInfo testMethod) = DeconstructFullName(fullName);
218 RunTest(testClass, testMethod);
221 public void RunTest(Type testClass, MethodInfo testMethod)
223 Action<CUIComponent> test = testMethod.CreateDelegate<Action<CUIComponent>>();
225 ActiveTest = testMethod;
233 if (!Directory.Exists(Path.Combine(TestStashPath, testClass.Name)))
235 Directory.CreateDirectory(Path.Combine(TestStashPath, testClass.Name));
238 string testPath = Path.Combine(TestStashPath, testClass.Name, $
"{testMethod.Name}.xml");
240 if (!File.Exists(testPath))
242 OnTestReport?.Invoke(
new TestReport(testClass, testMethod, TestStatus.Unknown));
247 using (StreamReader reader =
new StreamReader(testPath))
249 stashed = reader.ReadToEnd().Trim();
252 string real = TestChamber.Serialize(CUIAttribute.Calculated).Trim();
255 OnTestReport?.Invoke(
new TestReport(testClass, testMethod, TestStatus.Passed));
259 OnTestReport?.Invoke(
new TestReport(testClass, testMethod, TestStatus.Failed, DeepCompare(real, stashed)));
262 catch (Exception e) { CUITest.Log(e, Color.Orange); }
265 public void StashTest(
string fullName)
267 (Type testClass, MethodInfo testMethod) = DeconstructFullName(fullName);
268 StashTest(testClass, testMethod);
271 public void StashTest(Type testClass, MethodInfo testMethod)
273 Action<CUIComponent> test = testMethod.CreateDelegate<Action<CUIComponent>>();
275 ActiveTest = testMethod;
283 if (!Directory.Exists(Path.Combine(TestStashPath, testClass.Name)))
285 Directory.CreateDirectory(Path.Combine(TestStashPath, testClass.Name));
288 string testPath = Path.Combine(TestStashPath, testClass.Name, $
"{testMethod.Name}.xml");
290 using (StreamWriter writer =
new StreamWriter(testPath,
false))
292 writer.WriteLine(TestChamber.Serialize(CUIAttribute.Calculated));
295 OnTestReport?.Invoke(
new TestReport(testClass, testMethod, TestStatus.Stashed));
297 catch (Exception e) { CUITest.Log(e, Color.Orange); }
300 public void PrintResultsToConsole(TestReport report)
302 string fullName = $
"{report.TestClass.Name}.{report.TestMethod.Name}";
304 if (report.Status is TestStatus.Passed)
306 CUITest.Log($
"{fullName} Passed", Color.Lime);
309 if (report.Status is TestStatus.Unknown)
311 CUITest.Log($
"{fullName} is not stashed yet", Color.Yellow);
314 if (report.Status is TestStatus.Stashed)
316 CUITest.Log($
"{fullName} Stashed", Color.Cyan);
319 if (report.Status is TestStatus.Failed)
321 CUITest.Log($
"{fullName} Failed", Color.Red);
322 CUITest.Log(report.DeepCompare);
328 public AutomaticTestRunner()
330 GUI =
new AutoTestGUI(
this);
331 Config =
new TestingConfig();
332 Config.OnChanged += () => GUI.UseConfig(Config);
335 if (Config.Autorun !=
"") RunTest(Config.Autorun);
338 OnTestReport += PrintResultsToConsole;
Base class for all components.
CUIMainComponent MainComponent
Link to CUIMainComponent, passed to children.
void Step()
Forses 1 layout update step, even when Frozen.