public static LineSpan ToLineSpan(this Span span, string context) { if (span == null) { return(Create.LineSpan(1, 1)); } return(Create.LineSpan(ToLineLocation(span.Start, context), ToLineLocation(span.Start + span.Length, context))); }
public static TestResult CheckProjectStructure(this string projectDirectory) { TestResult finalResult = Create.TestResult(TestStatus.Pass); string documentationLink = "Project-References-and-Build-Paths"; List <string> exceptionalRepos = new List <string> { "BHoM", "BHoM_Engine", "BHoM_UI", "BHoM_Adapter", }; string[] directoryParts = projectDirectory.Split('\\'); string toolkit = directoryParts.Last(); string[] toolkitParts = toolkit.Split('_'); if (toolkitParts.Length == 1 && !exceptionalRepos.Contains(toolkit)) { finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error("Project not a valid project name. Project should end in '_Toolkit'", Create.Location(projectDirectory, Create.LineSpan(1, 1)), documentationLink) })); } else if (toolkitParts.Length == 2 && toolkitParts[1] != "Toolkit") { finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error("Project not a valid project name. Project should end in '_Toolkit'", Create.Location(projectDirectory, Create.LineSpan(1, 1)), documentationLink) })); } else if (toolkitParts.Length > 1) { string[] subFolders = Directory.GetDirectories(projectDirectory); bool containsEngine = true; bool containsAdapter = true; bool containsObject = true; if (subFolders.Where(x => x.EndsWith("_Engine")).Count() > 0 && subFolders.Where(x => x.EndsWith("_Engine") && (x.Split('\\').Last()) == toolkitParts[0] + "_Engine").FirstOrDefault() == null) { containsEngine = false; } if (subFolders.Where(x => x.EndsWith("_Adapter")).Count() > 0 && subFolders.Where(x => x.EndsWith("_Adapter") && (x.Split('\\').Last()) == toolkitParts[0] + "_Adapter").FirstOrDefault() == null) { containsAdapter = false; } if (subFolders.Where(x => x.EndsWith("_oM")).Count() > 0 && subFolders.Where(x => x.EndsWith("_oM") && (x.Split('\\').Last()) == toolkitParts[0] + "_oM").FirstOrDefault() == null) { containsObject = false; } if (!containsObject) { finalResult = finalResult.Merge(Create.TestResult(TestStatus.Warning, new List <Error> { Create.Error($"If the project requires an oM, the project should be titled '{toolkitParts[0]}_oM'", Create.Location(projectDirectory, Create.LineSpan(1, 1)), documentationLink) })); } if (!containsAdapter) { finalResult = finalResult.Merge(Create.TestResult(TestStatus.Warning, new List <Error> { Create.Error($"If the project requires an Adapter, the project should be titled '{toolkitParts[0]}_Adapter'", Create.Location(projectDirectory, Create.LineSpan(1, 1)), documentationLink) })); } if (!containsEngine) { finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"If the project requires an Engine, the project should be titled '{toolkitParts[0]}_Engine'", Create.Location(projectDirectory, Create.LineSpan(1, 1)), documentationLink) })); } List <string> allowedEngineFolders = new List <string> { "Compute", "Convert", "Create", "Modify", "Query", "Properties", "obj", "bin", }; if (containsEngine) { try { string[] engineFolders = Directory.GetDirectories(projectDirectory + "\\" + toolkitParts[0] + "_Engine"); if (engineFolders.Length > allowedEngineFolders.Count) { finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"The Engine project should only contain Compute, Convert, Create, Modify, and Query sub-folders", Create.Location(projectDirectory, Create.LineSpan(1, 1)), documentationLink) })); } else { foreach (string st in engineFolders) { string[] pr = st.Split('\\'); if (!allowedEngineFolders.Contains(pr.Last())) { finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"{st} is not a valid Engine sub folder", Create.Location(projectDirectory, Create.LineSpan(1, 1)), documentationLink) })); } } } } catch { } } } return(finalResult); }
public static TestResult HasValidCopyright(this SyntaxTriviaList leadingTrivia, int year = -1, string filePath = "") { if (leadingTrivia == null) { return(Create.TestResult(TestStatus.Pass)); } bool checkAllYears = false; if (year == -1) { checkAllYears = true; year = 2018; //Start } string documentationLink = "HasValidCopyright"; int maxYear = DateTime.Now.Year; //Max string copyrightStatement = $@"/* * This file is part of the Buildings and Habitats object Model (BHoM) * Copyright (c) 2015 - {year}, the respective contributors. All rights reserved. * * Each contributor holds copyright over their respective contributions. * The project versioning (Git) records all such contribution source information. * * * The BHoM is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3.0 of the License, or * (at your option) any later version. * * The BHoM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>. */"; string l = leadingTrivia.ToString(); l = l.Replace('\r', ' '); copyrightStatement = copyrightStatement.Replace('\r', ' '); string[] split = l.Split('\n'); string[] copyrightSplit = copyrightStatement.Split('\n'); if (split.Length < copyrightSplit.Length) { Error e = Create.Error("Copyright message is not accurate at line " + 1, Create.Location(filePath, Create.LineSpan(1, 2)), documentationLink, TestStatus.Error); return(Create.TestResult(TestStatus.Error, new List <Error> { e })); } if (!checkAllYears) { for (int x = 0; x < copyrightSplit.Length; x++) { if (split[x].TrimEnd() != copyrightSplit[x].TrimEnd()) { Error e = Create.Error("Copyright message is not accurate at line " + (x + 1), Create.Location(filePath, Create.LineSpan(x + 1, x + 2)), documentationLink, TestStatus.Error); return(Create.TestResult(TestStatus.Error, new List <Error> { e })); } } } else { List <int> availableYears = new List <int>(); for (int x = 2018; x <= maxYear; x++) { availableYears.Add(x); } for (int x = 0; x < copyrightSplit.Length; x++) { if (x == 2) { continue; //Skip the year line } if (split[x].TrimEnd() != copyrightSplit[x].TrimEnd()) { Error e = Create.Error("Copyright message is not accurate at line " + (x + 1), Create.Location(filePath, Create.LineSpan(x + 1, x + 2)), documentationLink, TestStatus.Error); return(Create.TestResult(TestStatus.Error, new List <Error> { e })); } } bool validOnOneYear = false; foreach (int a in availableYears) { copyrightSplit[2] = $" * Copyright (c) 2015 - {a}, the respective contributors. All rights reserved."; if (split[2].TrimEnd() == copyrightSplit[2].TrimEnd()) { validOnOneYear = true; } } if (!validOnOneYear) { Error e = Create.Error("Copyright message is not accurate at line 3", Create.Location(filePath, Create.LineSpan(3, 4)), documentationLink, TestStatus.Error); return(Create.TestResult(TestStatus.Error, new List <Error> { e })); } } return(Create.TestResult(TestStatus.Pass)); }
private static TestResult CheckAssemblyDescription(this List <string> fileLines, TestResult finalResult, string assemblyInfoPath, string documentationLink, string descriptionUrl) { string searchLine = $"[assembly: AssemblyDescription(\""; string foundLine = fileLines.Where(x => x.StartsWith(searchLine)).FirstOrDefault(); if (string.IsNullOrEmpty(foundLine)) { return(finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"Assembly Description should contain the URL to the GitHub organisation which owns the repository", Create.Location(assemblyInfoPath, Create.LineSpan(1, 1)), documentationLink) }))); } else { string allowedLine = $"{searchLine}{descriptionUrl}\")]"; int line = fileLines.IndexOf(foundLine) + 1; if (!foundLine.Contains(allowedLine)) { return(finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"Assembly Description should contain the URL to the GitHub organisation which owns the repository", Create.Location(assemblyInfoPath, Create.LineSpan(line, line)), documentationLink) }))); } } return(finalResult); }
private static TestResult CheckAssemblyFileVersion(this List <string> fileLines, TestResult finalResult, string assemblyInfoPath, string documentationLink) { string currentAssemblyVersion = Query.FullCurrentAssemblyFileVersion(); string searchLine = $"[assembly: AssemblyFileVersion(\""; string foundLine = fileLines.Where(x => x.StartsWith(searchLine)).FirstOrDefault(); if (string.IsNullOrEmpty(foundLine)) { return(finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"Assembly File Version should be set to {currentAssemblyVersion}", Create.Location(assemblyInfoPath, Create.LineSpan(1, 1)), documentationLink) }))); } else { string allowedLine = $"{searchLine}{Query.FullCurrentAssemblyFileVersion()}\")]"; int line = fileLines.IndexOf(foundLine) + 1; if (!foundLine.Contains(allowedLine)) { return(finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"Assembly File Version should be set to {currentAssemblyVersion}", Create.Location(assemblyInfoPath, Create.LineSpan(line, line)), documentationLink) }))); } } return(finalResult); }
private static TestResult CheckPostBuild(this ProjectFile csProject, List <string> fileLines, TestResult finalResult, string csProjFilePath, string documentationLink) { string postBuildShouldContain = ""; string searchLine = ""; if (csProject.IsOldStyle) { postBuildShouldContain = "xcopy \"$(TargetDir)$(TargetFileName)\" \"C:\\ProgramData\\BHoM\\Assemblies\" /Y"; searchLine = "<PostBuildEvent"; } else { postBuildShouldContain = ""$(TargetDir)$(TargetFileName)" "C:\\ProgramData\\BHoM\\Assemblies" /Y"; searchLine = "<Exec Command=\"xcopy"; } if (!csProject.PostBuildEvent.Any(x => x.Contains(postBuildShouldContain))) { postBuildShouldContain = "xcopy \"$(TargetDir)$(TargetFileName)\" \"C:\\ProgramData\\BHoM\\Assemblies\" /Y"; //Check again with a double spacing if (!csProject.PostBuildEvent.Any(x => x.Contains(postBuildShouldContain))) { postBuildShouldContain = ""$(TargetDir)$(TargetFileName)" "C:\\ProgramData\\BHoM\\Assemblies" /Y"; //Check again with a double spacing if (!csProject.PostBuildEvent.Any(x => x.Contains(postBuildShouldContain))) { int lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(searchLine)).FirstOrDefault()) + 1; //+1 because index is 0 based but line numbers start at 1 for the spans return(finalResult.Merge(Create.TestResult(TestStatus.Warning, new List <Error> { Create.Error($"Post Build event should be correctly set to copy the compiled DLL to the BHoM Assemblies folder", Create.Location(csProjFilePath, Create.LineSpan(lineNumber, lineNumber)), documentationLink, TestStatus.Warning) }))); } } } return(finalResult); }
public static TestResult CheckProjectFile(this string csProjFilePath, string assemblyDescriptionOrg = null) { if ((Path.GetExtension(csProjFilePath) != ".csproj")) { return(Create.TestResult(TestStatus.Pass)); } if (!File.Exists(csProjFilePath)) { return(Create.TestResult(TestStatus.Pass)); } TestResult finalResult = Create.TestResult(TestStatus.Pass); string documentationLink = "Project-References-and-Build-Paths"; List <string> fileLines = ReadFileContents(csProjFilePath); ProjectFile csProject = GetProjectFile(fileLines); if (csProject == null) { return(finalResult); } finalResult = CheckNETTarget(csProject, new List <string>(fileLines), finalResult, csProjFilePath, documentationLink); finalResult = CheckOutputPath(csProject, new List <string>(fileLines), finalResult, csProjFilePath, documentationLink); finalResult = CheckReferences(csProject, new List <string>(fileLines), finalResult, csProjFilePath, documentationLink); finalResult = CheckPostBuild(csProject, new List <string>(fileLines), finalResult, csProjFilePath, documentationLink); if (csProject.IsOldStyle) { finalResult = finalResult.Merge(Create.TestResult(TestStatus.Warning, new List <Error> { Create.Error($"CSProject files should be in the new format as used by core BHoM projects. Upgrading the file is possible for .Net Framework 4.7.2 projects as well. Please speak to a member of the DevOps team for assistance with this.", Create.Location(csProjFilePath, Create.LineSpan(1, 1)), documentationLink, TestStatus.Warning) })); } else { finalResult = CheckAssemblyVersion(csProject, new List <string>(fileLines), finalResult, csProjFilePath, documentationLink); finalResult = CheckAssembyFileVersion(csProject, new List <string>(fileLines), finalResult, csProjFilePath, documentationLink); if (!string.IsNullOrEmpty(assemblyDescriptionOrg)) { finalResult = CheckAssemblyDescription(csProject, new List <string>(fileLines), finalResult, csProjFilePath, documentationLink, assemblyDescriptionOrg); } } return(finalResult); }
private static TestResult CheckAssemblyDescription(this ProjectFile csProject, List <string> fileLines, TestResult finalResult, string csProjFilePath, string documentationLink, string descriptionUrl) { if (csProject.AssemblyDescription.ToLower() != descriptionUrl.ToLower()) { string fullXMLText = $"<Description>{csProject.AssemblyDescription}</Description>"; int lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(fullXMLText)).FirstOrDefault()) + 1; //+1 because index is 0 based but line numbers start at 1 for the spans return(finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"Assembly Description should contain the URL to the GitHub organisation which owns the repository", Create.Location(csProjFilePath, Create.LineSpan(lineNumber, lineNumber)), documentationLink) }))); } return(finalResult); }
private static TestResult CheckAssembyFileVersion(this ProjectFile csProject, List <string> fileLines, TestResult finalResult, string csProjFilePath, string documentationLink) { string currentlyAssemblyFileVersion = Query.FullCurrentAssemblyFileVersion(); if (csProject.AssemblyFileVersion.ToLower() != currentlyAssemblyFileVersion.ToLower()) { string fullXMLText = $"<FileVersion>{csProject.AssemblyFileVersion}</FileVersion>"; int lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(fullXMLText)).FirstOrDefault()) + 1; //+1 because index is 0 based but line numbers start at 1 for the spans return(finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"Assembly File Version should be set to {currentlyAssemblyFileVersion}", Create.Location(csProjFilePath, Create.LineSpan(lineNumber, lineNumber)), documentationLink) }))); } return(finalResult); }
private static TestResult CheckReferences(this ProjectFile csProject, List <string> fileLines, TestResult finalResult, string csProjFilePath, string documentationLink) { foreach (AssemblyReference reference in csProject.References) { string includeName = reference.Name.ToLower(); if (includeName != "bhom" && !includeName.Contains("_om") && !includeName.Contains("_engine") && !includeName.Contains("_adapter") && !includeName.Contains("_ui")) { continue; //Not a BHoM DLL so no point worrying } string includeNameXMLStart = $"<Reference Include=\"{reference.Name}"; int lineNumber = -1; if (!string.IsNullOrEmpty(reference.Version) || !string.IsNullOrEmpty(reference.Culture) || !string.IsNullOrEmpty(reference.ProcessorArchitecture)) { lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(includeNameXMLStart)).FirstOrDefault()) + 1; //+1 because index is 0 based but line numbers start at 1 for the spans finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error("Project references for BHoM DLLs should not include Version, Culture, or Processor Architecture", Create.Location(csProjFilePath, Create.LineSpan(lineNumber, lineNumber)), documentationLink) })); } string hintPath = @"C:\ProgramData\BHoM\Assemblies\" + reference.Name + ".dll"; string hintPathXML = $"<HintPath>{reference.HintPath}</HintPath>"; if (reference.HintPath != hintPath) { lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(hintPathXML)).FirstOrDefault()) + 1; //+1 because index is 0 based but line numbers start at 1 for the spans finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"Project reference for '{reference.Name}' should be set to '{hintPath}'", Create.Location(csProjFilePath, Create.LineSpan(lineNumber, lineNumber)), documentationLink) })); } if (reference.CopyLocal) { lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(hintPathXML)).FirstOrDefault()) + 1; //+1 because index is 0 based but line numbers start at 1 for the spans - searching from hintPathXML to then make sure we get the right line number related to this copy local issue while (!fileLines[lineNumber].Contains("<Private>true</Private>")) { lineNumber++; if (lineNumber >= fileLines.Count) { lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(hintPathXML)).FirstOrDefault()) + 1; //return to this line as the copy local isn't set at all break; //To avoid infinite loop } } finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"Project reference for '{reference.Name}' should be set to not copy local", Create.Location(csProjFilePath, Create.LineSpan(lineNumber, lineNumber)), documentationLink) })); } if (reference.SpecificVersion) { lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(hintPathXML)).FirstOrDefault()) + 1; //+1 because index is 0 based but line numbers start at 1 for the spans - searching from hintPathXML to then make sure we get the right line number related to this copy local issue while (!fileLines[lineNumber].Contains("<SpecificVersion>true</SpecificVersion>")) { lineNumber++; if (lineNumber >= fileLines.Count) { lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(hintPathXML)).FirstOrDefault()) + 1; //return to this line as the specific version isn't set at all break; //To avoid infinite loop } } finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"Project reference for '{reference.Name}' should be set to not be set to a specific version", Create.Location(csProjFilePath, Create.LineSpan(lineNumber, lineNumber)), documentationLink) })); } } return(finalResult); }
private static TestResult CheckOutputPath(this ProjectFile csProject, List <string> fileLines, TestResult finalResult, string csProjFilePath, string documentationLink) { List <string> acceptableOutputPaths = new List <string>() { "..\\Build\\" }; foreach (string outputPath in csProject.OutputPaths) { if (!string.IsNullOrEmpty(outputPath) && !acceptableOutputPaths.Contains(outputPath)) { string fullXMLText = $"<OutputPath>{outputPath}</OutputPath>"; int lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(fullXMLText)).FirstOrDefault()) + 1; //+1 because index is 0 based but line numbers start at 1 for the spans finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"Output path for all build configurations should be set to '..\\Build\\'", Create.Location(csProjFilePath, Create.LineSpan(lineNumber, lineNumber)), documentationLink) })); } } return(finalResult); }
private static TestResult CheckNETTarget(this ProjectFile csProject, List <string> fileLines, TestResult finalResult, string csProjFilePath, string documentationLink) { List <string> acceptableNETTargets = new List <string> { "v4.7.2", "net472", "netstandard2.0", "net5.0", "net6.0" }; bool atLeastOneCorrect = false; foreach (string target in csProject.TargetNETVersions) { if (!string.IsNullOrEmpty(target) && !acceptableNETTargets.Contains(target)) { string fullXMLText = $"<TargetFramework>{target}</TargetFramework>"; int lineNumber = fileLines.IndexOf(fileLines.Where(x => x.Contains(fullXMLText)).FirstOrDefault()) + 1; //+1 because index is 0 based but line numbers start at 1 for the spans finalResult = finalResult.Merge(Create.TestResult(TestStatus.Warning, new List <Error> { Create.Error($"Target frameworks for BHoM projects should either be .Net Framework 4.7.2, .Net Standard 2.0, or .Net 5.0.", Create.Location(csProjFilePath, Create.LineSpan(lineNumber, lineNumber)), documentationLink, TestStatus.Warning) })); } else { atLeastOneCorrect = true; } } if (!atLeastOneCorrect) { finalResult = finalResult.Merge(Create.TestResult(TestStatus.Error, new List <Error> { Create.Error($"At least one of the Target frameworks for BHoM projects must either be .Net Framework 4.7.2, .Net Standard 2.0, or .Net 5.0.", Create.Location(csProjFilePath, Create.LineSpan(1, 1)), documentationLink) })); } return(finalResult); }