using System.Runtime.CompilerServices; using System.Text.RegularExpressions; namespace YKanBan.Tests.Conventions; /// /// Source-level guards for code-behind conventions (plan ยง8.1): inside control, /// window and application code-behind a bare Resources.Key resolves to /// the StyledElement.Resources instance property, so the generated class /// must be written as YKanBan.Resources (or through a using alias). /// [TestClass] public partial class CodeBehindConventionTests { /// A Resources. member access not preceded by a qualifier or identifier character. [GeneratedRegex(@"(? /// Locates the application project's source folder from this file's compile-time path. /// /// Filled in by the compiler. /// The YKanBan project folder. private static string ApplicationSourceFolder([CallerFilePath] string thisFile = "") { DirectoryInfo? directory = new FileInfo(thisFile).Directory; while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "YKanBan", "YKanBan.csproj"))) { directory = directory.Parent; } Assert.IsNotNull(directory, "Repository root not found from the test source path."); return Path.Combine(directory.FullName, "YKanBan"); } [TestMethod] public void CodeBehindNeverUsesBareResourcesClassName() { string root = ApplicationSourceFolder(); string[] files = Directory.GetFiles(root, "*.axaml.cs", SearchOption.AllDirectories) .Where(path => !path.Split(Path.DirectorySeparatorChar).Any(part => part is "obj" or "bin")) .ToArray(); Assert.IsTrue(files.Length > 0, "No code-behind files found."); var offenders = new List(); foreach (string file in files) { string[] lines = File.ReadAllLines(file); for (int index = 0; index < lines.Length; index++) { if (BareResourcesAccess().IsMatch(lines[index])) { offenders.Add($"{Path.GetRelativePath(root, file)}:{index + 1}: {lines[index].Trim()}"); } } } Assert.AreEqual(0, offenders.Count, "Use YKanBan.Resources in code-behind:" + Environment.NewLine + string.Join(Environment.NewLine, offenders)); } }