using System; using System.IO; using System.Reflection; using System.Text.RegularExpressions; using CppSharp.AST; using CppSharp.Generators; using NUnit.Framework; namespace CppSharp.Utils { /// /// The main base class for a generator-based tests project. /// public abstract class GeneratorTest : ILibrary { readonly string name; readonly GeneratorKind kind; protected GeneratorTest(string name, GeneratorKind kind) { this.name = name; this.kind = kind; } public virtual void Setup(Driver driver) { var options = driver.Options; options.LibraryName = name; options.GeneratorKind = kind; options.OutputDir = Path.Combine(GetOutputDirectory(), "gen", name); options.SharedLibraryName = name + ".Native"; options.GenerateLibraryNamespace = true; options.Quiet = true; options.IgnoreParseWarnings = true; driver.Diagnostics.Message(""); driver.Diagnostics.Message("Generating bindings for {0} ({1})", options.LibraryName, options.GeneratorKind.ToString()); // Workaround for CLR which does not check for .dll if the // name already has a dot. if (!Platform.IsMono) options.SharedLibraryName += ".dll"; var path = Path.GetFullPath(GetTestsDirectory(name)); options.addIncludeDirs(path); var headersPaths = new System.Collections.Generic.List { Path.GetFullPath(Path.Combine(path, "../../deps/llvm/tools/clang/lib/Headers")) }; foreach (var header in headersPaths) options.addSystemIncludeDirs(header); driver.Diagnostics.Message("Looking for tests in: {0}", path); var files = Directory.EnumerateFiles(path, "*.h"); foreach (var file in files) options.Headers.Add(Path.GetFileName(file)); } public virtual void Preprocess(Driver driver, ASTContext ctx) { } public virtual void Postprocess(Driver driver, ASTContext ctx) { } public virtual void SetupPasses(Driver driver) { } #region Helpers public static string GetTestsDirectory(string name) { var directory = Directory.GetParent(Directory.GetCurrentDirectory()); while (directory != null) { var path = Path.Combine(directory.FullName, "tests", name); if (Directory.Exists(path)) return path; directory = directory.Parent; } throw new Exception(string.Format( "Tests directory for project '{0}' was not found", name)); } static string GetOutputDirectory() { var directory = Directory.GetParent(Directory.GetCurrentDirectory()); while (directory != null) { var path = Path.Combine(directory.FullName, "obj"); if (Directory.Exists(path)) return directory.FullName; directory = directory.Parent; } throw new Exception("Could not find tests output directory"); } #endregion } /// /// The main NUnit fixture base class for a generator-based tests project. /// Provides support for a text-based test system that looks for lines /// in the native test declarations that match a certain pattern, which /// are used for certain kinds of tests that cannot be done with just /// C# code and using the generated wrappers. /// [TestFixture] public abstract class GeneratorTestFixture { readonly string assemblyName; protected GeneratorTestFixture() { var location = Assembly.GetCallingAssembly().Location; assemblyName = Path.GetFileNameWithoutExtension(location); } static bool GetGeneratorKindFromLang(string lang, out GeneratorKind kind) { kind = GeneratorKind.CSharp; switch(lang) { case "CSharp": case "C#": kind = GeneratorKind.CSharp; return true; case "CLI": kind = GeneratorKind.CLI; return true; } return false; } [Test] public void CheckDirectives() { var name = assemblyName.Substring(0, assemblyName.IndexOf('.')); var kind = assemblyName.Substring(assemblyName.LastIndexOf('.') + 1); GeneratorKind testKind; if (!GetGeneratorKindFromLang(kind, out testKind)) throw new NotSupportedException("Unknown language generator"); var path = Path.GetFullPath(GeneratorTest.GetTestsDirectory(name)); foreach (var header in Directory.EnumerateFiles(path, "*.h")) { var headerText = File.ReadAllText(header); // Parse the header looking for suitable lines to test. foreach (var line in File.ReadAllLines(header)) { var match = Regex.Match(line, @"^\s*///*\s*(\S+)\s*:\s*(.*)"); if (!match.Success) continue; var matchLang = match.Groups[1].Value; GeneratorKind matchKind; if (!GetGeneratorKindFromLang(matchLang.ToUpper(), out matchKind)) continue; if (matchKind != testKind) continue; var matchText = match.Groups[2].Value; if (string.IsNullOrWhiteSpace(matchText)) continue; var matchIndex = headerText.IndexOf(matchText, StringComparison.Ordinal); Assert.IsTrue(matchIndex != -1, string.Format("Could not match '{0}' in file '{1}'", matchText, header)); } } } } }