public void DoBehavior(BehaviorMode mode, bool rewrite_expectWriteErrors = false, bool rewrite_expectParseErrors = false, bool validation_expectWriteErrors = false, Assembly[] validation_assemblies = null, Func <string, bool> errorValidator = null) { if (mode == BehaviorMode.Bare) { // we actually have nothing to do here, we're good } else if (mode == BehaviorMode.RewrittenBare || mode == BehaviorMode.RewrittenPretty) { string data = null; void RunComposer() { var composer = new Dec.Composer(); data = composer.ComposeXml(mode == BehaviorMode.RewrittenPretty); } if (rewrite_expectWriteErrors) { ExpectErrors(() => RunComposer(), errorValidator: errorValidator); } else { RunComposer(); } Dec.Database.Clear(); // This is a janky hack; it resets the type caches so we also generate errors again properly. Dec.Config.UsingNamespaces = Dec.Config.UsingNamespaces; void RunParser() { var parser = new Dec.Parser(); parser.AddString(data); parser.Finish(); } if (rewrite_expectParseErrors) { ExpectErrors(() => RunParser(), errorValidator: errorValidator); } else { RunParser(); } } else if (mode == BehaviorMode.Validation) { string code = null; void RunComposer() { var composer = new Dec.Composer(); code = composer.ComposeValidation(); } if (validation_expectWriteErrors) { ExpectErrors(() => RunComposer(), errorValidator: errorValidator); } else { RunComposer(); } var assemblies = new Assembly[] { this.GetType().Assembly }; if (validation_assemblies != null) { assemblies = assemblies.Concat(validation_assemblies).ToArray(); } CompileAndRun($"public static void Test() {{\n{code}\n}}", assemblies, "Test", null); } else if (mode == BehaviorMode.Null) { void RunComposer() { var composer = new Dec.Composer(); composer.ComposeNull(); } if (rewrite_expectWriteErrors) { // We don't really insist on an error, but we tolerate one. ExpectErrors(() => { RunComposer(); handledError = true; // good enough, just continue }, errorValidator: errorValidator); } else { RunComposer(); } // may as well just stop here Assert.Pass(); } else { Assert.IsTrue(false, "Bad case for behavior mode!"); } }
private static void Main() { Init(); var env = new Env(); // Generate initial composites for (int i = 0; i < 100; ++i) { var c = new Composite(); c.name = Rand.NextString(); c.type = CompositeTypeDistribution.Distribution.Choose(); if (c.type == Composite.Type.Dec) { c.name += "Dec"; } env.types.Add(c); } // Generate parameters foreach (var c in env.types) { int parameterCount = Rand.WeightedDistribution(); for (int i = 0; i < parameterCount; ++i) { c.members.Add(new Member(env, c, Rand.NextString(), MemberTypeDistribution.Distribution.Choose())); } } // Generate instances foreach (var c in env.types) { if (c.type != Composite.Type.Dec) { continue; } int creations = Rand.WeightedDistribution(); for (int i = 0; i < creations; ++i) { env.instances.Add(new Instance(env, c)); } } string GenerateTestHarness(string testData) { string testHarness = File.ReadAllText("data/TestHarness.cs.template"); var csComposites = new StringBuilder(); foreach (var c in env.types) { csComposites.Append(Util.Indent(c.WriteCsharpDefinition(), 2)); } var types = string.Join(", ", env.types.Select(c => $"typeof({c.name})")); var filename = "data/Fuzzgen.FuzzgenTest.xml"; return(testHarness .Replace("<<COMPOSITES>>", csComposites.ToString()) .Replace("<<TYPES>>", types) .Replace("<<FILENAME>>", $"\"{filename}\"") .Replace("<<TESTS>>", testData)); } string testCode = GenerateTestHarness(""); string xmlCode; // Output xml { var sb = new StringBuilder(); sb.AppendLine("<Decs>"); foreach (var i in env.instances) { sb.AppendLine(Util.Indent(i.WriteXmlDec())); } sb.AppendLine("</Decs>"); xmlCode = sb.ToString(); } // This is a bit janky; we want to use Dec features when doing generation, but now we need to blow it away to generate the .cs code // So I guess maybe it would be nice to have non-global state right now :V Dec.Database.Clear(); var bootstrapAssembly = DecUtilLib.Compilation.Compile(testCode, new System.Reflection.Assembly[] { }); bootstrapAssembly.GetType("DecTest.Harness").GetMethod("Setup").Invoke(null, null); var parser = new Dec.Parser(); parser.AddString(xmlCode); parser.Finish(); var composer = new Dec.Composer(); var tests = composer.ComposeValidation(); string finalCode = GenerateTestHarness(tests); string path = $"../../test/data/golden/parser/{System.DateTimeOffset.Now:yyyyMMddhhmmss}"; System.IO.Directory.CreateDirectory(path); DecUtilLib.Compress.WriteToFile(System.IO.Path.Combine(path, "Harness.cs"), finalCode); DecUtilLib.Compress.WriteToFile(System.IO.Path.Combine(path, "data.xml"), xmlCode); }