diff --git a/.github/workflows/build-tests-artifact.yml b/.github/workflows/build-tests-artifact.yml new file mode 100644 index 00000000..a20377f0 --- /dev/null +++ b/.github/workflows/build-tests-artifact.yml @@ -0,0 +1,98 @@ +name: Build Tests Artifact + +on: + workflow_dispatch: + push: + branches: [ develop ] + pull_request: + branches: [ develop ] + +jobs: + collect-and-upload-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Create _Tests_ directory + run: mkdir -p _Tests_ + + # TODO: The toolkit will need to be built before copying files. This will need to be done before this step, including building the dependecies BHoM, BHoM_Engine and BHoM_Adapter.continue-on-error: + # TODO: For now this is just pushing up the files that are already in the Build directory to enable quicker evaluation of overall workflow of test files. + - name: Copy files from Build directory + run: | + # Copy .dll files (only from Build directory, not subdirectories) + find Build -maxdepth 1 -name "*.dll" -type f -exec cp {} _Tests_/ \; + + # Copy .exe files (only from Build directory, not subdirectories) + find Build -maxdepth 1 -name "*.exe" -type f -exec cp {} _Tests_/ \; + + # Copy .json files (only from Build directory, not subdirectories) + find Build -maxdepth 1 -name "*.json" -type f -exec cp {} _Tests_/ \; + + + # List copied files for verification + echo "Files copied to _Tests_:" + ls -la _Tests_/ + + - name: Identify tests + run: | + set -euo pipefail + echo "Searching for test DLLs in _Tests_ folder..." + + tests_dir="_Tests_" + if [ ! -d "$tests_dir" ]; then + echo "✗ $tests_dir folder not found" + exit 1 + fi + + # Prepare output file + out_file="$tests_dir/TestDlls.txt" + : > "$out_file" + + found=0 + shopt -s nullglob + for dll in "$tests_dir"/*.dll; do + base=$(basename "$dll" .dll) + + # Skip known non-test DLLs by name patterns + case "$base" in + NUnit_Engine|TestSetup_Engine|testhost|testcentric.engine.metadata|\ + Microsoft.*|System.*|nunit3.*|nunit.*|NUnit3.*|Newtonsoft.*|NuGet.*) + continue ;; + *_oM|*_Engine) + continue ;; + esac + + # Check for test frameworks by scanning binary strings + if command -v strings >/dev/null 2>&1; then + if strings -a "$dll" 2>/dev/null | grep -qiE '(^|/)nunit\.framework|xunit|mstest\.testframework'; then + echo "$base.dll" >> "$out_file" + echo " ✓ $base.dll" + found=$((found+1)) + fi + else + if grep -a -qiE 'nunit\.framework|xunit|mstest\.testframework' "$dll"; then + echo "$base.dll" >> "$out_file" + echo " ✓ $base.dll" + found=$((found+1)) + fi + fi + done + + if [ "$found" -eq 0 ]; then + echo "✗ No test DLLs found in $tests_dir" + exit 1 + fi + + echo "✓ Wrote $found test DLL name(s) to $out_file" + + - name: Upload Tests artifact + uses: actions/upload-artifact@v4 + with: + name: Tests + path: | + _Tests_/ + StartupHook/*.dll + retention-days: 30 diff --git a/CodeComplianceTest_Engine/CodeComplianceTest_Engine.csproj b/CodeComplianceTest_Engine/CodeComplianceTest_Engine.csproj index be644e6b..9cb5a5f9 100644 --- a/CodeComplianceTest_Engine/CodeComplianceTest_Engine.csproj +++ b/CodeComplianceTest_Engine/CodeComplianceTest_Engine.csproj @@ -1,28 +1,26 @@ - -netstandard2.0 - Library - BH.Engine.Test.CodeCompliance - true - true - ..\Build\ - ComplianceTestBuild;Debug;Release - CodeComplianceTest_Engine - https://github.com/BHoM/Test_Toolkit - CodeComplianceTest_Engine - Copyright � https://github.com/BHoM - 9.0.0.0 - 9.3.0.0 - true - - - - - - true - ..\Build\ - MinimumRecommendedRules.ruleset - + + netstandard2.0 + 8.0.0.0 + https://github.com/BHoM/BHoM_Engine + 5.0.0 + BHoM + Copyright © https://github.com/BHoM + true + BH.Engine.Test.CodeCompliance + 8.2.0.0 + ..\Build\ + Debug;Release + + + + + + + + + + $(ProgramData)\BHoM\Assemblies\Dimensional_oM.dll @@ -64,27 +62,9 @@ $(ProgramData)\BHoM\Assemblies\Test_oM.dll False - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - + + + + + \ No newline at end of file diff --git a/CodeComplianceTest_Engine/Compute/Check.cs b/CodeComplianceTest_Engine/Compute/Check.cs index 009f68bc..cd130a1a 100644 --- a/CodeComplianceTest_Engine/Compute/Check.cs +++ b/CodeComplianceTest_Engine/Compute/Check.cs @@ -59,7 +59,9 @@ public static TestResult Check(this MethodInfo method, SyntaxNode node, string c method.GetCustomAttributes().All(condition => condition.IPasses(node)) && (checkType != null && method.GetCustomAttribute()?.ComplianceType == checkType)) { - Func fn = method.ToFunc(); + //if(fn == null) + Func fn = method.GetFunction(); + Span result = fn(new object[] { node }) as Span; if (result != null) { @@ -90,6 +92,24 @@ public static TestResult Check(this MethodInfo method, IEnumerable n } return finalResult; } + + + private static Func GetFunction(this MethodInfo method) + { + Func fn; + if (m_checkMethods.TryGetValue(method, out fn)) + return fn; + + fn = method.ToFunc(); + lock (m_compileLock) + { + m_checkMethods[method] = fn; + } + return fn; + } + + private static Dictionary> m_checkMethods = new Dictionary>(); + private static object m_compileLock = new object(); } } diff --git a/CodeComplianceTest_Engine/Compute/RunChecks.cs b/CodeComplianceTest_Engine/Compute/RunChecks.cs index 62dd8ae9..a83e8198 100644 --- a/CodeComplianceTest_Engine/Compute/RunChecks.cs +++ b/CodeComplianceTest_Engine/Compute/RunChecks.cs @@ -50,7 +50,7 @@ public static TestResult RunChecks(this SyntaxNode node, string checkType = null return Create.TestResult(TestStatus.Pass); TestResult finalResult = Create.TestResult(TestStatus.Pass); - foreach(MethodInfo method in Query.AllChecks()) + foreach(MethodInfo method in Query.AllChecks(checkType)) { finalResult = finalResult.Merge(method.Check(node, checkType)); } diff --git a/CodeComplianceTest_Engine/Query/AllChecks.cs b/CodeComplianceTest_Engine/Query/AllChecks.cs index 6367d664..df22fb15 100644 --- a/CodeComplianceTest_Engine/Query/AllChecks.cs +++ b/CodeComplianceTest_Engine/Query/AllChecks.cs @@ -21,27 +21,27 @@ */ using BH.oM.Test.CodeCompliance; +using BH.oM.Test.CodeCompliance.Attributes; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Reflection; - namespace BH.Engine.Test.CodeCompliance { public static partial class Query { - public static IEnumerable AllChecks() + public static IEnumerable AllChecks(string checkType = null) { return Assembly.GetExecutingAssembly().DefinedTypes .Where(t => t.IsClass && t.Name == "Query" && t.Namespace == "BH.Engine.Test.CodeCompliance.Checks") .SelectMany(t => t.DeclaredMethods) - .Where(method => method.IsPublic && method.ReturnType == typeof(Span)); + .Where(method => method.IsPublic && method.ReturnType == typeof(Span) && (checkType == null || method.GetCustomAttribute()?.ComplianceType == checkType)); } } } diff --git a/CodeComplianceTest_Engine/app.config b/CodeComplianceTest_Engine/app.config deleted file mode 100644 index f10b4ac0..00000000 --- a/CodeComplianceTest_Engine/app.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/CodeCompliance_oM/CodeComplianceTest_oM.csproj b/CodeCompliance_oM/CodeComplianceTest_oM.csproj index 61355888..8a852655 100644 --- a/CodeCompliance_oM/CodeComplianceTest_oM.csproj +++ b/CodeCompliance_oM/CodeComplianceTest_oM.csproj @@ -1,28 +1,20 @@ - - -netstandard2.0 - Library - BH.oM.Test - ..\Build\ - ComplianceTestBuild;Debug;Release - Test_oM - https://github.com/BHoM/Test_Toolkit - Test_oM - Copyright � https://github.com/BHoM - 9.0.0.0 - 9.3.0.0 - - - - - - true - ..\Build\ - MinimumRecommendedRules.ruleset - + + + netstandard2.0 + 8.0.0.0 + https://github.com/BHoM/BHoM_Engine + 5.0.0 + BHoM + Copyright © https://github.com/BHoM + true + BH.Engine.Test.CodeCompliance + 8.2.0.0 + ..\Build\ + Debug;Release + - False + False $(ProgramData)\BHoM\Assemblies\Analytical_oM.dll False @@ -41,10 +33,9 @@ $(ProgramData)\BHoM\Assemblies\Test_oM.dll False - - - - - - + + + + + \ No newline at end of file diff --git a/Compliance_Tests/AssemblyInfoCompliance.cs b/Compliance_Tests/AssemblyInfoCompliance.cs new file mode 100644 index 00000000..2bf98061 --- /dev/null +++ b/Compliance_Tests/AssemblyInfoCompliance.cs @@ -0,0 +1,56 @@ +using BH.oM.Test.Results; +using BH.oM.Test; +using BH.Engine.Test; +using Microsoft.CodeAnalysis; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Tests.Compliace +{ + public class AssemblyInfoCompliance + { + /***************************************************/ + /**** Test methods ****/ + /***************************************************/ + + [Description("Checks a AssemblyInfoFile file by using the method available in CodeComplianceTest_Engine." + + "Potential to port the content of that method over to this file, split as individual tests, each run on the .csproj test files.")] + [TestCaseSource(nameof(AssemblyInfoFiles))] + public void TestCompliance(string fileName, string assemblyDescriptionOrg) + { + TestResult result = BH.Engine.Test.CodeCompliance.Compute.CheckAssemblyInfo(fileName, assemblyDescriptionOrg); + if (result == null) + Assert.Fail($"{fileName}: No result returned from compliance check."); + + if (result.Status == TestStatus.Error) + Assert.Fail($"{fileName}: {result.FullMessage(5, TestStatus.Warning)}"); + + if (result.Status == TestStatus.Warning) + Assert.Warn($"{fileName}: {result.FullMessage(5, TestStatus.Warning)}"); + } + + /***************************************************/ + /**** Test data methods ****/ + /***************************************************/ + + [Description("Returns the AssemblyInfo cs files as well as assumed link to the repository.")] + private static IEnumerable AssemblyInfoFiles() + { + string organisationUrl = null; + string currentRepo = BH.Tests.Setup.Query.CurrentRepository(); + if (currentRepo != null) + organisationUrl = $"https://github.com/{currentRepo}"; + + foreach (var file in BH.Tests.Setup.Query.TestFilesCs().Where(x => x.EndsWith("AssemblyInfo.cs"))) + yield return new TestCaseData(new string[] { file, organisationUrl }).SetArgDisplayNames(file); + + } + + /***************************************************/ + } +} diff --git a/Compliance_Tests/Compliance_Tests.csproj b/Compliance_Tests/Compliance_Tests.csproj new file mode 100644 index 00000000..4a6352a5 --- /dev/null +++ b/Compliance_Tests/Compliance_Tests.csproj @@ -0,0 +1,45 @@ + + + + net8.0 + enable + disable + ..\Build + false + BH.Tests.Compliace + Debug;Release + + + + + + + + + + + + + + + + + + + $(ProgramData)\BHoM\Assemblies\BHoM.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\BHoM_Engine.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Test_oM.dll + False + False + + + + diff --git a/Compliance_Tests/CsFile.cs b/Compliance_Tests/CsFile.cs new file mode 100644 index 00000000..80223415 --- /dev/null +++ b/Compliance_Tests/CsFile.cs @@ -0,0 +1,216 @@ +using BH.Engine.Base; +using BH.Engine.Test.CodeCompliance; +using BH.oM.Test; +using BH.oM.Test.CodeCompliance; +using BH.oM.Test.CodeCompliance.Attributes; +using BH.oM.Test.Results; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace BH.Tests.Compliance +{ + [Description("Class that executes compliance checks for .cs files. The methods provided to the test fixture are the various check methods available in CodeComplianceTest_Engine." + + "This will isntanciate a test class for each test method, and then the files are all checked against all method")] + [TestFixtureSource(nameof(TestMethods))] + public class CsFile + { + /***************************************************/ + /**** Private fields ****/ + /***************************************************/ + + private MethodInfo m_Method; //Check method + + /***************************************************/ + /**** Constructor ****/ + /***************************************************/ + + public CsFile(MethodInfo method) + { + m_Method = method; + } + + /***************************************************/ + /**** Test methods ****/ + /***************************************************/ + + [Description("Test method being executed for the current test method, and current .cs file.")] + [TestCaseSource(nameof(CsFiles))] + public void TestCompliance(string fileName) + { + SyntaxNode node = GetNode(fileName); + MethodInfo method = m_Method; + Assume.That(method != null); + Assume.That(node != null); + Assume.That(System.IO.Path.GetFileName(node.SyntaxTree.FilePath), Is.Not.EqualTo("AssemblyInfo.cs"), "Skipping AssemblyInfo.cs files."); + + if (method.GetCustomAttributes().All(condition => condition.IPasses(node))) //Prefilter out files that don't match the path condition. Not really required (handled internally as well) but speeds up the execution + { + Assert.Multiple(() => //Allow multiple assertions to be raised for the same file + { + CheckMethod(method, node, node.SyntaxTree.FilePath); + }); + } + } + + /***************************************************/ + + [Description("Main validator method handling checking and assertion raising for the provided method and node.")] + private static void CheckMethod(MethodInfo method, SyntaxNode node, string filePath) + { + Type type = node.GetType(); + + if (method.GetParameters()[0].ParameterType.IsAssignableFrom(type) && //Check that the method can handle this type of node + !(typeof(MemberDeclarationSyntax).IsAssignableFrom(type) // Ignore deprecated members + && ((MemberDeclarationSyntax)node).IsDeprecated()) && + method.GetCustomAttributes().All(condition => condition.IPasses(node))) //Check all conditions are met + { + Func fn = GetFunction(method); //Get compiled function of the compliance test method + + Span result = fn(new object[] { node }) as Span; //Execute the method + if (result != null) //If a result is returned, then raise the appropriate assertion. Null return means no issue found + { + string message = method.GetCustomAttribute()?.Message ?? ""; + string documentation = method.GetCustomAttribute()?.DocumentationLink ?? ""; + TestStatus errLevel = method.GetCustomAttribute()?.Level ?? TestStatus.Error; + var error = BH.Engine.Test.CodeCompliance.Create.Error(message, BH.Engine.Test.CodeCompliance.Create.Location(filePath, result.ToLineSpan(node.SyntaxTree.GetRoot().ToFullString())), documentation, errLevel, method.Name); + + string finalMessage = error.ToText(); + + switch (errLevel) + { + case TestStatus.Pass: + Console.WriteLine(finalMessage); + break; + case TestStatus.Warning: + Assert.Warn(finalMessage); + break; + default: + case TestStatus.Error: + Assert.Fail(finalMessage); + break; + } + } + } + + foreach (var child in node.ChildNodes()) + { + CheckMethod(method, child, filePath); //Recurse through all child nodes + } + + } + + /***************************************************/ + /**** Test data methods ****/ + /***************************************************/ + + [Description("Returns the test methods available in CodeComplianceTest_Engine to be executed as test fixtures.")] + private static IEnumerable TestMethods() + { + bool isBHoMOrg = true; //Used to control if copyright checks are applied + string currentRepo = BH.Tests.Setup.Query.CurrentRepository(); + if (currentRepo != null) + { + string org = currentRepo.Split('/').First(); + if(org.Equals("BHoM", StringComparison.OrdinalIgnoreCase)) + isBHoMOrg = true; + else + isBHoMOrg = false; + } + + var checkMethods = BH.Engine.Test.CodeCompliance.Query.AllChecks(); + if(!isBHoMOrg) //If not BHoM, then remove copyright checks + checkMethods = checkMethods.Where(m => m.Name != nameof(BH.Engine.Test.CodeCompliance.Checks.Query.HasValidCopyright)); + + foreach (var methodGroup in checkMethods.Distinct().GroupBy(x => x.Name)) + { + if (methodGroup.Count() == 1) + { + yield return new TestFixtureData(methodGroup.First()).SetArgDisplayNames(methodGroup.First().Name); + } + else + { + foreach (var method in methodGroup) + { + string key = method.Name + ": " + method.GetParameters().First().ParameterType.Name.Replace("DeclarationSyntax", ""); + yield return new TestFixtureData(method).SetArgDisplayNames(key); + } + } + } + } + + /***************************************************/ + + [Description("Returns the AssemblyInfo cs files as well as assumed link to the repository.")] + private static IEnumerable CsFiles() + { + string repoFolder = Setup.Query.CurrentRepoFolder() ?? ""; + foreach (var file in BH.Tests.Setup.Query.TestFilesCs().Where(x => !x.EndsWith("AssemblyInfo.cs"))) + yield return new TestCaseData(new string[] { file }).SetArgDisplayNames(file.Replace(repoFolder, "")); + + } + + /***************************************************/ + + /***************************************************/ + /**** Extraction, compilation and cashing ****/ + /***************************************************/ + + [Description("Returns the syntax node for the provided file name, using a cache to avoid reloading and reparsing files.")] + private static SyntaxNode GetNode(string fileName) + { + if (m_Nodes.TryGetValue(fileName, out SyntaxNode node)) + return node; + + lock (m_nodeLock) + { + if (m_Nodes.TryGetValue(fileName, out node)) + return node; + fileName = System.IO.Path.GetFullPath(fileName); + string file; + using (StreamReader sr = new StreamReader(fileName)) + { + file = sr.ReadToEnd(); + } + node = BH.Engine.Test.CodeCompliance.Convert.ToSyntaxTree(file, fileName).GetRoot(); + m_Nodes[fileName] = node; + return node; + } + } + + /***************************************************/ + + [Description("Returns a compiled function for the provided method, using a cache to avoid recompiling methods.")] + private static Func GetFunction(MethodInfo method) + { + Func fn; + if (m_checkMethodFunctions.TryGetValue(method, out fn)) + return fn; + + fn = method.ToFunc(); + lock (m_compileLock) + { + m_checkMethodFunctions[method] = fn; + } + return fn; + } + + /***************************************************/ + /**** Caches and lock fields ****/ + /***************************************************/ + + private static Dictionary m_Nodes = new Dictionary(); + private static object m_nodeLock = new object(); + + private static Dictionary> m_checkMethodFunctions = new Dictionary>(); + private static object m_compileLock = new object(); + + /***************************************************/ + } +} diff --git a/Compliance_Tests/ProjectCompliance.cs b/Compliance_Tests/ProjectCompliance.cs new file mode 100644 index 00000000..6ca97611 --- /dev/null +++ b/Compliance_Tests/ProjectCompliance.cs @@ -0,0 +1,56 @@ +using BH.oM.Test.Results; +using BH.oM.Test; +using BH.Engine.Test; +using Microsoft.CodeAnalysis; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Tests.Compliace +{ + public class ProjectCompliance + { + /***************************************************/ + /**** Test methods ****/ + /***************************************************/ + + [Description("Checks a .csproj file by using the method available in CodeComplianceTest_Engine." + + "Potential to port the content of that method over to this file, split as individual tests, each run on the .csproj test files.")] + [TestCaseSource(nameof(ProjectFiles))] + public void TestCompliance(string fileName, string assemblyDescriptionOrg) + { + TestResult result = BH.Engine.Test.CodeCompliance.Compute.CheckProjectFile(fileName, assemblyDescriptionOrg); + if (result == null) + Assert.Fail($"{fileName}: No result returned from compliance check."); + + if (result.Status == TestStatus.Error) + Assert.Fail($"{fileName}: {result.FullMessage(5, TestStatus.Warning)}"); + + if (result.Status == TestStatus.Warning) + Assert.Warn($"{fileName}: {result.FullMessage(5, TestStatus.Warning)}"); + } + + /***************************************************/ + /**** Test data methods ****/ + /***************************************************/ + + [Description("Returns the csproj files as well as assumed link to the repository.")] + private static IEnumerable ProjectFiles() + { + string organisationUrl = null; + string currentRepo = BH.Tests.Setup.Query.CurrentRepository(); + if (currentRepo != null) + organisationUrl = $"https://github.com/{currentRepo}"; + + foreach (var file in BH.Tests.Setup.Query.TestFilesCsproj()) + yield return new TestCaseData(new string[] { file, organisationUrl }).SetArgDisplayNames(System.IO.Path.GetFileName(file)); + + } + + /***************************************************/ + } +} diff --git a/Compliance_Tests/TestFolder.cs b/Compliance_Tests/TestFolder.cs new file mode 100644 index 00000000..f4b9bc35 --- /dev/null +++ b/Compliance_Tests/TestFolder.cs @@ -0,0 +1,19 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Tests.Compliace +{ + + public class TestFolder + { + [Test] + public void FolderTest() + { + Console.WriteLine(BH.Tests.Setup.Query.CurrentRepoFolder()); + } + } +} diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..85fe68a1 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,6 @@ + + + $(MSBuildThisFileDirectory)test.runsettings + false + + \ No newline at end of file diff --git a/InteroperabilityTest_Engine/InteroperabilityTest_Engine.csproj b/InteroperabilityTest_Engine/InteroperabilityTest_Engine.csproj index 48b4eee0..551d3e34 100644 --- a/InteroperabilityTest_Engine/InteroperabilityTest_Engine.csproj +++ b/InteroperabilityTest_Engine/InteroperabilityTest_Engine.csproj @@ -1,24 +1,35 @@ - + + + -netstandard2.0 + Debug + AnyCPU + {DF4047D7-2883-4763-83C0-B1D3B391D454} Library + Properties BH.Engine.Test.Interoperability - ..\Build\ - ComplianceTestBuild;Debug;Release - InteropabilityTest_Engine - https://github.com/BHoM/Test_Toolkit - InteropabilityTest_Engine - Copyright � https://github.com/BHoM - 9.0.0.0 - 9.3.0.0 + InteroperabilityTest_Engine + v4.7.2 + 512 - - - - + true + full + false + ..\Build\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true ..\Build\ + TRACE + prompt + 4 + $(ProgramData)\BHoM\Assemblies\Adapter_Engine.dll @@ -75,20 +86,62 @@ False False + + + + + + + + $(ProgramData)\BHoM\Assemblies\Test_oM.dll False False - - - - - + + + + + + + + + + + + + + + + + + + + + + + {8aa8b55e-0c55-4356-bf8b-98b98f6c346c} + InteroperabilityTest_oM + + + {5fc85409-dbc5-4b0d-a2aa-1d9542f0763b} + Test_Engine + + + {1f163cfa-e407-4c7a-9daf-1c19207a8983} + UnitTest_oM + - + + + C:\Windows\System32\xcopy "$(SolutionDir)DataSets\*.*" "C:\ProgramData\BHoM\DataSets" /Y /I /E +xcopy "$(TargetDir)$(TargetFileName)" "C:\ProgramData\BHoM\Assemblies" /Y + + + \ No newline at end of file diff --git a/LocalRunningRepoFolder.txt b/LocalRunningRepoFolder.txt new file mode 100644 index 00000000..5dd5a4e6 --- /dev/null +++ b/LocalRunningRepoFolder.txt @@ -0,0 +1 @@ +C:\Github\StructuralEngineering_Toolkit \ No newline at end of file diff --git a/NUnit_Engine/NUnit_Engine.csproj b/NUnit_Engine/NUnit_Engine.csproj index 7ff60edd..cee123b1 100644 --- a/NUnit_Engine/NUnit_Engine.csproj +++ b/NUnit_Engine/NUnit_Engine.csproj @@ -6,7 +6,7 @@ 9.3.0.0 9.0.0.0 BH.Engine.Test.NUnit - ..\Build + true Debug;Release;Test diff --git a/NUnit_oM/NUnit_oM.csproj b/NUnit_oM/NUnit_oM.csproj index 9315f2b9..c5be3cbd 100644 --- a/NUnit_oM/NUnit_oM.csproj +++ b/NUnit_oM/NUnit_oM.csproj @@ -6,7 +6,7 @@ 9.3.0.0 9.0.0.0 BH.oM.Test.NUnit - ..\Build + Debug;Release;Test diff --git a/Serialisation_Tests/DataSource.cs b/Serialisation_Tests/DataSource.cs new file mode 100644 index 00000000..e12a3034 --- /dev/null +++ b/Serialisation_Tests/DataSource.cs @@ -0,0 +1,61 @@ +using BH.Engine.Base; +using BH.Engine.Diffing; +using BH.Engine.Reflection; +using BH.Engine.Serialiser; +using BH.Engine.Test; +using BH.oM.Base; +using BH.oM.Base.Attributes; +using BH.oM.Test; +using BH.oM.Test.Results; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Tests.Serialisation +{ + public static class DataSource + { + + public static IEnumerable OmTypes() + { + return OmTypesToTest(Setup.Query.CurrentAssemblies()); + } + + public static IEnumerable EngineMethods() + { + return EngineMethodsToTest(Setup.Query.CurrentAssemblies()); + } + + /*************************************/ + + public static List OmTypesToTest(List assembliesToTest) + { + assembliesToTest = assembliesToTest.Where(x => x.IsOmAssembly()).ToList(); + + // It feels like the BHoMTypeList method should already return a clean list of Type but it doesn't at the moment + return assembliesToTest.SelectMany(a => a.GetTypes().Where(x => { + return typeof(IObject).IsAssignableFrom(x) + && !x.IsAbstract + && !x.IsDeprecated() + && !x.GetProperties().Select(p => p.PropertyType.Namespace).Any(n => !n.StartsWith("BH.") && !n.StartsWith("System")); + })).ToList(); + } + + /*************************************/ + + public static List EngineMethodsToTest(List assembliesToTest) + { + assembliesToTest = assembliesToTest.Where(x => x.IsEngineAssembly()).ToList(); + return BH.Engine.Base.Query.BHoMMethodList().Where(x => assembliesToTest.Any(a => x.DeclaringType.Assembly == a)).ToList(); + } + + /*************************************/ + + } +} diff --git a/Serialisation_Tests/MethodSerialisation.cs b/Serialisation_Tests/MethodSerialisation.cs new file mode 100644 index 00000000..9640d65f --- /dev/null +++ b/Serialisation_Tests/MethodSerialisation.cs @@ -0,0 +1,91 @@ +using BH.Engine.Base; +using BH.Engine.Diffing; +using BH.Engine.Reflection; +using BH.Engine.Serialiser; +using BH.Engine.Test; +using BH.oM.Base; +using BH.oM.Base.Attributes; +using BH.oM.Test; +using BH.oM.Test.Results; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Tests.Serialisation +{ + public class MethodSerialisation + { + + [TestCaseSource(typeof(DataSource), nameof(DataSource.EngineMethods))] + public void ToFromJson(MethodBase method) + { + TestResult result = MethodToFromJson(method); + + Assert.That(result.Status, Is.EqualTo(TestStatus.Pass), result.FullMessage(3, TestStatus.Warning)); + Assert.Pass($"Passing method serialisation test for {method.IToText(true)} from Assembly {method.DeclaringType.Assembly.FullName}"); + } + + /*************************************/ + + + //Below is copy pasted from Verification solution in BHoM_Engine + + public static TestResult MethodToFromJson(MethodBase method) + { + string methodDescription = method.IToText(true); + + // To Json + string json = ""; + try + { + Engine.Base.Compute.ClearCurrentEvents(); + json = method.ToJson(); + } + catch (Exception e) + { + Engine.Base.Compute.RecordError(e.Message); + } + + if (string.IsNullOrWhiteSpace(json)) + return new TestResult + { + Description = methodDescription, + Status = TestStatus.Error, + Message = $"Error: Failed to convert method {methodDescription} to json.", + Information = Engine.Base.Query.CurrentEvents().Select(x => x.ToEventMessage()).ToList() + }; + + // From Json + MethodInfo copy = null; + try + { + Engine.Base.Compute.ClearCurrentEvents(); + copy = Engine.Serialiser.Convert.FromJson(json) as MethodInfo; + } + catch (Exception e) + { + Engine.Base.Compute.RecordError(e.Message); + } + + if (!method.IsEqual(copy)) + return new TestResult + { + Description = methodDescription, + Status = TestStatus.Error, + Message = $"Error: Method {methodDescription} is not equal to the original after serialisation.", + Information = Engine.Base.Query.CurrentEvents().Select(x => x.ToEventMessage()).ToList() + }; + + // All test objects passed the test + return Engine.Test.Create.PassResult(methodDescription); + } + + /*************************************/ + } +} diff --git a/Serialisation_Tests/ObjectSerialisation.cs b/Serialisation_Tests/ObjectSerialisation.cs new file mode 100644 index 00000000..5a1bec86 --- /dev/null +++ b/Serialisation_Tests/ObjectSerialisation.cs @@ -0,0 +1,136 @@ +using BH.Engine.Base; +using BH.Engine.Diffing; +using BH.Engine.Reflection; +using BH.Engine.Serialiser; +using BH.Engine.Test; +using BH.oM.Base; +using BH.oM.Base.Attributes; +using BH.oM.Test; +using BH.oM.Test.Results; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Tests.Serialisation +{ + public class ObjectSerialisation + { + [TestCaseSource(typeof(DataSource), nameof(DataSource.OmTypes))] + public void ToFromJson(Type oMType) + { + TestResult result = ObjectToFromJson(oMType); + + Assert.That(result.Status, Is.EqualTo(TestStatus.Pass), result.FullMessage(3, TestStatus.Warning)); + Assert.Pass($"Passing object serialisation test for {oMType.FullName} from Assembly {oMType.Assembly.FullName}"); + } + + //Below is copy pasted from Verification solution in BHoM_Engine + + public static TestResult ObjectToFromJson(Type type) + { + string typeDescription = type.IToText(true); + + // Create the test objects of the given type + List testObjects = new List(); + if (testObjects.Count == 0) + { + object dummy = null; + try + { + Engine.Base.Compute.ClearCurrentEvents(); + dummy = Engine.Test.Compute.DummyObject(type); + } + catch (Exception e) + { + Engine.Base.Compute.RecordWarning(e.Message); + } + + if (dummy == null) + return new TestResult + { + Description = typeDescription, + Status = TestStatus.Warning, + Message = $"Warning: Failed to create a dummy object of type {typeDescription}.", + Information = Engine.Base.Query.CurrentEvents().Select(x => x.ToEventMessage()).ToList() + }; + else + testObjects.Add(dummy); + } + + // Test each object in the list + foreach (object testObject in testObjects) + { + // To Json + string json = ""; + try + { + Engine.Base.Compute.ClearCurrentEvents(); + json = testObject.ToJson(); + } + catch (Exception e) + { + Engine.Base.Compute.RecordError(e.Message); + } + + if (string.IsNullOrWhiteSpace(json)) + return new TestResult + { + Description = typeDescription, + Status = TestStatus.Error, + Message = $"Error: Failed to convert object of type {typeDescription} to json.", + Information = Engine.Base.Query.CurrentEvents().Select(x => x.ToEventMessage()).ToList() + }; + + // From Json + object copy = null; + try + { + Engine.Base.Compute.ClearCurrentEvents(); + copy = Engine.Serialiser.Convert.FromJson(json); + } + catch (Exception e) + { + Engine.Base.Compute.RecordError(e.Message); + } + + bool isEqual; + + try + { + isEqual = testObject.IsEqual(copy); + } + catch (Exception e) + { + BH.Engine.Base.Compute.RecordWarning(e, $"Crashed when trying to compare objects."); + + return new TestResult + { + Description = typeDescription, + Status = TestStatus.Warning, + Message = $"Warning: Failed to compare objects of type {typeDescription}.", + Information = Engine.Base.Query.CurrentEvents().Select(x => x.ToEventMessage()).ToList() + }; + } + + if (!isEqual) + return new TestResult + { + Description = typeDescription, + Status = TestStatus.Error, + Message = $"Error: Object of type {typeDescription} is not equal to the original after serialisation.", + Information = Engine.Base.Query.CurrentEvents().Select(x => x.ToEventMessage()).ToList() + }; + } + + // All test objects passed the test + return Engine.Test.Create.PassResult(typeDescription); + } + + } +} diff --git a/Serialisation_Tests/Serialisation_Tests.csproj b/Serialisation_Tests/Serialisation_Tests.csproj new file mode 100644 index 00000000..fb177266 --- /dev/null +++ b/Serialisation_Tests/Serialisation_Tests.csproj @@ -0,0 +1,68 @@ + + + + net8.0 + enable + disable + ..\Build + false + BH.Tests.Serialisation + Debug;Release + + + + + + + + + + + + + + + + + $(ProgramData)\BHoM\Assemblies\BHoM.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\BHoM_Engine.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Data_oM.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Diffing_Engine.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Diffing_oM.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Reflection_Engine.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Serialiser_Engine.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Test_oM.dll + False + False + + + + diff --git a/Serialisation_Tests/TypeSerialisation.cs b/Serialisation_Tests/TypeSerialisation.cs new file mode 100644 index 00000000..0043dc83 --- /dev/null +++ b/Serialisation_Tests/TypeSerialisation.cs @@ -0,0 +1,92 @@ +using BH.Engine.Base; +using BH.Engine.Diffing; +using BH.Engine.Reflection; +using BH.Engine.Serialiser; +using BH.Engine.Test; +using BH.oM.Base; +using BH.oM.Base.Attributes; +using BH.oM.Test; +using BH.oM.Test.Results; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Tests.Serialisation +{ + public class TypeSerialisation + { + + [TestCaseSource(typeof(DataSource), nameof(DataSource.OmTypes))] + public void ToFromJson(Type oMType) + { + TestResult result = TypeToFromJson(oMType); + + Assert.That(result.Status, Is.EqualTo(TestStatus.Pass), result.FullMessage(3, TestStatus.Warning)); + } + + /*************************************/ + + + //Below is copy pasted from Verification solution in BHoM_Engine + + + public static TestResult TypeToFromJson(Type type) + { + string typeDescription = type.IToText(true); + + // To Json + string json = ""; + try + { + Engine.Base.Compute.ClearCurrentEvents(); + json = type.ToJson(); + } + catch (Exception e) + { + Engine.Base.Compute.RecordError(e.Message); + } + + if (string.IsNullOrWhiteSpace(json)) + return new TestResult + { + Description = typeDescription, + Status = TestStatus.Error, + Message = $"Error: Failed to convert type {typeDescription} to json.", + Information = Engine.Base.Query.CurrentEvents().Select(x => x.ToEventMessage()).ToList() + }; + + // From Json + Type copy = null; + try + { + Engine.Base.Compute.ClearCurrentEvents(); + copy = Engine.Serialiser.Convert.FromJson(json) as Type; + } + catch (Exception e) + { + Engine.Base.Compute.RecordError(e.Message); + } + + if (!type.IsEqual(copy)) + return new TestResult + { + Description = typeDescription, + Status = TestStatus.Error, + Message = $"Error: Type {typeDescription} is not equal to the original after serialisation.", + Information = Engine.Base.Query.CurrentEvents().Select(x => x.ToEventMessage()).ToList() + }; + + // All test objects passed the test + return Engine.Test.Create.PassResult(typeDescription); + } + + /*************************************/ + + } +} diff --git a/StartupHook/TestStartupHook.deps.json b/StartupHook/TestStartupHook.deps.json new file mode 100644 index 00000000..0983cb21 --- /dev/null +++ b/StartupHook/TestStartupHook.deps.json @@ -0,0 +1,23 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v8.0", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v8.0": { + "TestStartupHook/1.0.0": { + "runtime": { + "TestStartupHook.dll": {} + } + } + } + }, + "libraries": { + "TestStartupHook/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/StartupHook/TestStartupHook.dll b/StartupHook/TestStartupHook.dll new file mode 100644 index 00000000..a45236e0 Binary files /dev/null and b/StartupHook/TestStartupHook.dll differ diff --git a/TestSetup_Engine/Query/CurrentAssemblies.cs b/TestSetup_Engine/Query/CurrentAssemblies.cs new file mode 100644 index 00000000..08a7abfa --- /dev/null +++ b/TestSetup_Engine/Query/CurrentAssemblies.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Tests.Setup +{ + public static partial class Query + { + public static List CurrentAssemblies() + { + List assembliesToTest = Setup.Query.InputParametersAssemblies(); + if (assembliesToTest == null) + { + assembliesToTest = GetProjectFilesAsAssemblies(); + } + return assembliesToTest; + } + + private static List GetProjectFilesAsAssemblies() + { + List files = Setup.Query.GetFiles(System.IO.Path.Combine(Setup.Query.CurrentRepoFolder()), "*.csproj", true).ToList(); + List assemblies = new List(); + foreach (string file in files) + { + string fileName = System.IO.Path.GetFileNameWithoutExtension(file); + string assemblyPath = System.IO.Path.Combine(BH.Engine.Base.Query.BHoMFolder(), fileName + ".dll"); + if (System.IO.File.Exists(assemblyPath)) + assemblies.Add(BH.Engine.Base.Compute.LoadAssembly(assemblyPath)); + } + return assemblies; + } + } +} diff --git a/TestSetup_Engine/Query/CurrentFolder.cs b/TestSetup_Engine/Query/CurrentFolder.cs new file mode 100644 index 00000000..f374d6f7 --- /dev/null +++ b/TestSetup_Engine/Query/CurrentFolder.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace BH.Tests.Setup +{ + public static partial class Query + { + public static string CurrentRepoFolder() + { + string currentDirectory = System.Environment.CurrentDirectory; + + string localRunningPath = Directory.GetParent(currentDirectory).FullName; + localRunningPath = Path.Combine(localRunningPath, "LocalRunningRepoFolder.txt"); + + if(File.Exists(localRunningPath)) + { + string path = File.ReadAllText(localRunningPath); + if(Directory.Exists(path)) + return path; + } + + string endFolder = ""; + int indexAdd = 0; + if (currentDirectory.Contains(".ci")) + endFolder = ".ci"; + else if (currentDirectory.Contains("Build")) + endFolder = "Build"; + else if (currentDirectory.Contains("_Tests_")) + endFolder = "_Tests_"; + else if (currentDirectory.Contains("bin")) + { + endFolder = "bin"; + indexAdd = 1; + } + + string[] split = currentDirectory.Split(Path.DirectorySeparatorChar); + + string folder = ""; + + int i = 0; + while (split.Length > i + indexAdd && split[i + indexAdd] != endFolder) + { + folder = Path.Combine(folder, split[i]); + i++; + } + + return folder; + } + + /***************************************************/ + + public static string CurrentCiFolder() + { + return Path.Combine(CurrentRepoFolder(), ".ci"); + } + + /***************************************************/ + + public static string CurrentDatasetsUTFolder() + { + return Path.Combine(CurrentCiFolder(), "Datasets"); + } + + /***************************************************/ + } +} diff --git a/TestSetup_Engine/Query/CurrentRepository.cs b/TestSetup_Engine/Query/CurrentRepository.cs new file mode 100644 index 00000000..2e9a6955 --- /dev/null +++ b/TestSetup_Engine/Query/CurrentRepository.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace BH.Tests.Setup +{ + public static partial class Query + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + + public static string CurrentRepository() + { + if (m_currentRepo != null) + return m_currentRepo; + + lock (m_currentRepoLock) + { + if (m_currentRepo != null) + return m_currentRepo; + + string currentRepository = InputParametersCurrentRepository(); + + if (currentRepository != null) + m_currentRepo = currentRepository; + else + m_currentRepo = CurrentRepositoryFromGitConfig(); + + } + + return m_currentRepo; + } + + /***************************************************/ + /**** Private Methods ****/ + /***************************************************/ + + + public static string CurrentRepositoryFromGitConfig() + { + + string repoPath = CurrentRepoFolder(); + string gitConfigPath = Path.Combine(repoPath, ".git", "config"); + + if (!File.Exists(gitConfigPath)) + { + Console.WriteLine("Git config not found."); + return null; + } + + string[] configContent = File.ReadAllLines(gitConfigPath); + + foreach (string line in configContent) + { + if (line.Contains("url") && line.Contains("github.com/")) + { + // Found a GitHub URL + int urlIndex = line.IndexOf("github.com/"); + string sub = line.Substring(urlIndex); + return sub.Replace("github.com/", "").Replace(".git", "").Trim(); + } + } + + + return null; + + } + + /***************************************************/ + /**** Private Fields ****/ + /***************************************************/ + + private static object m_currentRepoLock = new object(); + private static string m_currentRepo = null; + } +} diff --git a/TestSetup_Engine/Query/GetFiles.cs b/TestSetup_Engine/Query/GetFiles.cs new file mode 100644 index 00000000..3b530d76 --- /dev/null +++ b/TestSetup_Engine/Query/GetFiles.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BH.Tests.Setup +{ + public static partial class Query + { + public static IEnumerable GetFiles(string folder, string searchPattern = "*.*", bool recursive = false) + { + if (string.IsNullOrEmpty(folder) || !System.IO.Directory.Exists(folder)) + { + Console.WriteLine($"{folder} does not exist!"); + } + else + { + foreach (string file in System.IO.Directory.EnumerateFiles(folder, searchPattern, System.IO.SearchOption.TopDirectoryOnly)) + yield return file; + + if (recursive) + { + foreach (string subFolder in System.IO.Directory.EnumerateDirectories(folder)) + { + if (!m_FolderExcluded.Contains(System.IO.Path.GetFileName(subFolder))) + { + foreach (string file in GetFiles(subFolder, searchPattern, true)) + { + yield return file; + } + } + } + } + } + } + + private static HashSet m_FolderExcluded = new HashSet(new string[] { "bin", "obj", "Build", "_Dependencies_", "_Tests_" }); + } +} diff --git a/TestSetup_Engine/Query/InputParameters.cs b/TestSetup_Engine/Query/InputParameters.cs new file mode 100644 index 00000000..123ebb1a --- /dev/null +++ b/TestSetup_Engine/Query/InputParameters.cs @@ -0,0 +1,79 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; + +namespace BH.Tests.Setup +{ + public static partial class Query + { + + + public static List InputParametersAssemblies() + { + if (!TestContext.Parameters.Exists("UpdatedAssemblies")) + return null; + + if (m_AssembliesToTest != null) + return m_AssembliesToTest; + lock (m_AssemblyLock) + { + if (m_AssembliesToTest != null) + return m_AssembliesToTest; + + m_AssembliesToTest = new List(); + + var assembliesUpdated = TestContext.Parameters.Get("UpdatedAssemblies", ""); + + foreach (var assemblyName in assembliesUpdated.Split(new char[] { ' ', ';' }, StringSplitOptions.RemoveEmptyEntries)) + m_AssembliesToTest.Add(Assembly.LoadFrom(assemblyName)); + + foreach (var assembly in m_AssembliesToTest) + { + TestContext.WriteLine($"Assembly to test: {assembly.FullName}"); + } + + return m_AssembliesToTest; + } + } + + private static List m_AssembliesToTest = null; + private static object m_AssemblyLock = new object(); + /***************************************************/ + + public static List InputParametersUpdatedFiles() + { + List updatedFiles = new List(); + + if (!TestContext.Parameters.Exists("UpdatedFiles")) + return null; + + var assembliesUpdated = TestContext.Parameters.Get("UpdatedFiles", ""); + + foreach (var fileName in assembliesUpdated.Split(new char[] { ' ', ';' }, StringSplitOptions.RemoveEmptyEntries)) + updatedFiles.Add(Path.GetFullPath(Path.GetFullPath(fileName).TrimEnd('\\'))); //Ensure formating is correct + + return updatedFiles; + } + + /***************************************************/ + + public static string InputParametersCurrentRepository() + { + List updatedFiles = new List(); + + if (!TestContext.Parameters.Exists("CurrentRepository")) + return null; + + var assembliesUpdated = TestContext.Parameters.Get("CurrentRepository", ""); + + return assembliesUpdated; + } + + /***************************************************/ + + + } +} diff --git a/TestSetup_Engine/Query/TestFiles.cs b/TestSetup_Engine/Query/TestFiles.cs new file mode 100644 index 00000000..d056d448 --- /dev/null +++ b/TestSetup_Engine/Query/TestFiles.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Tests.Setup +{ + public static partial class Query + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Returns .cs files to be tested. Prioritises files from input parameters. If non available, then all files of the specified format in the currently executing repo are extracted.")] + public static List TestFilesCs() + { + return TestFiles(".cs"); + } + + /***************************************************/ + + [Description("Returns .csproj files to be tested. Prioritises files from input parameters. If non available, then all files of the specified format in the currently executing repo are extracted.")] + public static List TestFilesCsproj() + { + return TestFiles(".csproj"); + } + + /***************************************************/ + + [Description("Returns files of a specific type to be tested. Prioritises files from input parameters. If non available, then all files of the specified format in the currently executing repo are extracted.")] + public static List TestFiles(string fileEnding) + { + if(!fileEnding.StartsWith('.')) + fileEnding = "." + fileEnding; + + if (m_testFiles.TryGetValue(fileEnding, out List files)) + return files; + + lock (m_fileLock) + { + if (m_testFiles.TryGetValue(fileEnding, out files)) + return files; + + files = Setup.Query.InputParametersUpdatedFiles()?.Where(f => Path.GetExtension(f).Equals(fileEnding, StringComparison.OrdinalIgnoreCase)).ToList(); + + if (files == null) + { + files = Setup.Query.GetFiles(Setup.Query.CurrentRepoFolder(), $"*{fileEnding}", true).ToList(); + m_testFiles[fileEnding] = files; + } + return files; + } + + } + + /***************************************************/ + /**** Private Fields ****/ + /***************************************************/ + + private static Dictionary> m_testFiles = new Dictionary>(); + private static object m_fileLock = new object(); + } +} diff --git a/TestSetup_Engine/TestSetup_Engine.csproj b/TestSetup_Engine/TestSetup_Engine.csproj new file mode 100644 index 00000000..6d6b1845 --- /dev/null +++ b/TestSetup_Engine/TestSetup_Engine.csproj @@ -0,0 +1,71 @@ + + + + net8.0 + https://github.com/BHoM/Test_Toolkit + 8.2.0.0 + 8.0.0.0 + BH.Tests.Setup + ..\Build + true + Debug;Release;Test + + + + + + + + + + + + + + + + + + + $(ProgramData)\BHoM\Assemblies\BHoM.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\BHoM_Engine.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Data_oM.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Diffing_Engine.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Diffing_oM.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Reflection_Engine.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Serialiser_Engine.dll + False + False + + + $(ProgramData)\BHoM\Assemblies\Test_oM.dll + False + False + + + + diff --git a/TestStartupHook/StartupHook.cs b/TestStartupHook/StartupHook.cs new file mode 100644 index 00000000..354935dd --- /dev/null +++ b/TestStartupHook/StartupHook.cs @@ -0,0 +1,79 @@ +using System; +using System.IO; +using System.Reflection; +using System.Runtime.Loader; + +public static class StartupHook +{ + // The runtime looks for this exact signature by convention. + public static void Initialize() + { + // Write immediately to ensure we can see if this is called + Console.WriteLine("[StartupHook] *** STARTUP HOOK CALLED ***"); + Console.Error.WriteLine("[StartupHook] *** STARTUP HOOK CALLED TO STDERR ***"); + + try + { + // Central folder with your DLLs (Windows path in your case) + var central = @"C:\ProgramData\BHoM\Assemblies"; + + // Verify the directory exists + if (!Directory.Exists(central)) + { + Console.WriteLine($"[StartupHook] Warning: Directory does not exist: {central}"); + return; + } + + Console.WriteLine($"[StartupHook] Initializing assembly resolver for: {central}"); + + // Managed resolver: only called if default probing fails + AssemblyLoadContext.Default.Resolving += (alc, name) => + { + try + { + var candidate = Path.Combine(central, name.Name + ".dll"); + if (File.Exists(candidate)) + { + Console.WriteLine($"[StartupHook] Loading assembly: {name.Name} from {candidate}"); + return alc.LoadFromAssemblyPath(candidate); + } + else + { + Console.WriteLine($"[StartupHook] Assembly not found: {name.Name} at {candidate}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"[StartupHook] Error loading assembly {name.Name}: {ex.Message}"); + } + return null; + }; + + // Also handle AppDomain assembly resolve for compatibility + AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => + { + try + { + var assemblyName = new AssemblyName(args.Name); + var candidate = Path.Combine(central, assemblyName.Name + ".dll"); + if (File.Exists(candidate)) + { + Console.WriteLine($"[StartupHook] AppDomain loading assembly: {assemblyName.Name} from {candidate}"); + return Assembly.LoadFrom(candidate); + } + } + catch (Exception ex) + { + Console.WriteLine($"[StartupHook] Error in AppDomain assembly resolve for {args.Name}: {ex.Message}"); + } + return null; + }; + + Console.WriteLine("[StartupHook] Assembly resolver initialized successfully"); + } + catch (Exception ex) + { + Console.WriteLine($"[StartupHook] Fatal error during initialization: {ex}"); + } + } +} diff --git a/TestStartupHook/TestStartupHook.csproj b/TestStartupHook/TestStartupHook.csproj new file mode 100644 index 00000000..79d55c37 --- /dev/null +++ b/TestStartupHook/TestStartupHook.csproj @@ -0,0 +1,14 @@ + + + net8.0 + TestStartupHook + disable + false + false + Debug;Release + ..\StartupHook + + + + + diff --git a/Test_Toolkit.sln b/Test_Toolkit.sln index 33624f6d..03b92d40 100644 --- a/Test_Toolkit.sln +++ b/Test_Toolkit.sln @@ -23,76 +23,98 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NUnit_oM", "NUnit_oM\NUnit_ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NUnit_Engine", "NUnit_Engine\NUnit_Engine.csproj", "{54087A4F-BBD2-464A-86E7-110A177770FE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestSetup_Engine", "TestSetup_Engine\TestSetup_Engine.csproj", "{5D87C51F-E2BC-1CBF-36A4-FA03D3A09904}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compliance_Tests", "Compliance_Tests\Compliance_Tests.csproj", "{356A77BF-AE0A-492D-9A77-1CE170F3D93B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serialisation_Tests", "Serialisation_Tests\Serialisation_Tests.csproj", "{0951B695-2F68-4BCC-94D6-D03F3236E75A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTest_Tests", "UnitTest_Tests\UnitTest_Tests.csproj", "{A48688A9-F4EE-48CB-9838-64EBCC5B933C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestStartupHook", "TestStartupHook\TestStartupHook.csproj", "{B637F49E-F6DF-44B7-B921-01A8B4BCBC17}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + LocalRunningRepoFolder.txt = LocalRunningRepoFolder.txt + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - ComplianceTestBuild|Any CPU = ComplianceTestBuild|Any CPU Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DF4047D7-2883-4763-83C0-B1D3B391D454}.ComplianceTestBuild|Any CPU.ActiveCfg = ComplianceTestBuild|Any CPU {DF4047D7-2883-4763-83C0-B1D3B391D454}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF4047D7-2883-4763-83C0-B1D3B391D454}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF4047D7-2883-4763-83C0-B1D3B391D454}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF4047D7-2883-4763-83C0-B1D3B391D454}.Release|Any CPU.Build.0 = Release|Any CPU - {5E17BA6F-F159-47DD-865C-F2DAA235AAC7}.ComplianceTestBuild|Any CPU.ActiveCfg = ComplianceTestBuild|Any CPU - {5E17BA6F-F159-47DD-865C-F2DAA235AAC7}.ComplianceTestBuild|Any CPU.Build.0 = ComplianceTestBuild|Any CPU {5E17BA6F-F159-47DD-865C-F2DAA235AAC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5E17BA6F-F159-47DD-865C-F2DAA235AAC7}.Debug|Any CPU.Build.0 = Debug|Any CPU {5E17BA6F-F159-47DD-865C-F2DAA235AAC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {5E17BA6F-F159-47DD-865C-F2DAA235AAC7}.Release|Any CPU.Build.0 = Release|Any CPU - {5FC85409-DBC5-4B0D-A2AA-1D9542F0763B}.ComplianceTestBuild|Any CPU.ActiveCfg = Release|Any CPU - {5FC85409-DBC5-4B0D-A2AA-1D9542F0763B}.ComplianceTestBuild|Any CPU.Build.0 = Release|Any CPU {5FC85409-DBC5-4B0D-A2AA-1D9542F0763B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5FC85409-DBC5-4B0D-A2AA-1D9542F0763B}.Debug|Any CPU.Build.0 = Debug|Any CPU {5FC85409-DBC5-4B0D-A2AA-1D9542F0763B}.Release|Any CPU.ActiveCfg = Release|Any CPU {5FC85409-DBC5-4B0D-A2AA-1D9542F0763B}.Release|Any CPU.Build.0 = Release|Any CPU - {768934CA-E6CF-44D3-9F74-611D4E58B1AE}.ComplianceTestBuild|Any CPU.ActiveCfg = Release|Any CPU - {768934CA-E6CF-44D3-9F74-611D4E58B1AE}.ComplianceTestBuild|Any CPU.Build.0 = Release|Any CPU {768934CA-E6CF-44D3-9F74-611D4E58B1AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {768934CA-E6CF-44D3-9F74-611D4E58B1AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {768934CA-E6CF-44D3-9F74-611D4E58B1AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {768934CA-E6CF-44D3-9F74-611D4E58B1AE}.Release|Any CPU.Build.0 = Release|Any CPU - {8AA8B55E-0C55-4356-BF8B-98B98F6C346C}.ComplianceTestBuild|Any CPU.ActiveCfg = Debug|Any CPU - {8AA8B55E-0C55-4356-BF8B-98B98F6C346C}.ComplianceTestBuild|Any CPU.Build.0 = Debug|Any CPU {8AA8B55E-0C55-4356-BF8B-98B98F6C346C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8AA8B55E-0C55-4356-BF8B-98B98F6C346C}.Debug|Any CPU.Build.0 = Debug|Any CPU {8AA8B55E-0C55-4356-BF8B-98B98F6C346C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8AA8B55E-0C55-4356-BF8B-98B98F6C346C}.Release|Any CPU.Build.0 = Release|Any CPU - {F758FC9C-CEDF-430D-AEFF-2B1E196F677B}.ComplianceTestBuild|Any CPU.ActiveCfg = ComplianceTestBuild|Any CPU - {F758FC9C-CEDF-430D-AEFF-2B1E196F677B}.ComplianceTestBuild|Any CPU.Build.0 = ComplianceTestBuild|Any CPU {F758FC9C-CEDF-430D-AEFF-2B1E196F677B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F758FC9C-CEDF-430D-AEFF-2B1E196F677B}.Debug|Any CPU.Build.0 = Debug|Any CPU {F758FC9C-CEDF-430D-AEFF-2B1E196F677B}.Release|Any CPU.ActiveCfg = Release|Any CPU {F758FC9C-CEDF-430D-AEFF-2B1E196F677B}.Release|Any CPU.Build.0 = Release|Any CPU - {3F911D8A-55E4-4293-8FCF-DE37B3D79E72}.ComplianceTestBuild|Any CPU.ActiveCfg = Release|Any CPU - {3F911D8A-55E4-4293-8FCF-DE37B3D79E72}.ComplianceTestBuild|Any CPU.Build.0 = Release|Any CPU {3F911D8A-55E4-4293-8FCF-DE37B3D79E72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3F911D8A-55E4-4293-8FCF-DE37B3D79E72}.Debug|Any CPU.Build.0 = Debug|Any CPU {3F911D8A-55E4-4293-8FCF-DE37B3D79E72}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F911D8A-55E4-4293-8FCF-DE37B3D79E72}.Release|Any CPU.Build.0 = Release|Any CPU - {1F163CFA-E407-4C7A-9DAF-1C19207A8983}.ComplianceTestBuild|Any CPU.ActiveCfg = Release|Any CPU - {1F163CFA-E407-4C7A-9DAF-1C19207A8983}.ComplianceTestBuild|Any CPU.Build.0 = Release|Any CPU {1F163CFA-E407-4C7A-9DAF-1C19207A8983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1F163CFA-E407-4C7A-9DAF-1C19207A8983}.Debug|Any CPU.Build.0 = Debug|Any CPU {1F163CFA-E407-4C7A-9DAF-1C19207A8983}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F163CFA-E407-4C7A-9DAF-1C19207A8983}.Release|Any CPU.Build.0 = Release|Any CPU - {5359D5A5-C8F8-430E-98CF-52DCE3F48EFD}.ComplianceTestBuild|Any CPU.ActiveCfg = Debug|Any CPU - {5359D5A5-C8F8-430E-98CF-52DCE3F48EFD}.ComplianceTestBuild|Any CPU.Build.0 = Debug|Any CPU {5359D5A5-C8F8-430E-98CF-52DCE3F48EFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5359D5A5-C8F8-430E-98CF-52DCE3F48EFD}.Debug|Any CPU.Build.0 = Debug|Any CPU {5359D5A5-C8F8-430E-98CF-52DCE3F48EFD}.Release|Any CPU.ActiveCfg = Release|Any CPU {5359D5A5-C8F8-430E-98CF-52DCE3F48EFD}.Release|Any CPU.Build.0 = Release|Any CPU - {54087A4F-BBD2-464A-86E7-110A177770FE}.ComplianceTestBuild|Any CPU.ActiveCfg = Debug|Any CPU - {54087A4F-BBD2-464A-86E7-110A177770FE}.ComplianceTestBuild|Any CPU.Build.0 = Debug|Any CPU {54087A4F-BBD2-464A-86E7-110A177770FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54087A4F-BBD2-464A-86E7-110A177770FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {54087A4F-BBD2-464A-86E7-110A177770FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {54087A4F-BBD2-464A-86E7-110A177770FE}.Release|Any CPU.Build.0 = Release|Any CPU + {5D87C51F-E2BC-1CBF-36A4-FA03D3A09904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D87C51F-E2BC-1CBF-36A4-FA03D3A09904}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D87C51F-E2BC-1CBF-36A4-FA03D3A09904}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D87C51F-E2BC-1CBF-36A4-FA03D3A09904}.Release|Any CPU.Build.0 = Release|Any CPU + {356A77BF-AE0A-492D-9A77-1CE170F3D93B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {356A77BF-AE0A-492D-9A77-1CE170F3D93B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {356A77BF-AE0A-492D-9A77-1CE170F3D93B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {356A77BF-AE0A-492D-9A77-1CE170F3D93B}.Release|Any CPU.Build.0 = Release|Any CPU + {0951B695-2F68-4BCC-94D6-D03F3236E75A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0951B695-2F68-4BCC-94D6-D03F3236E75A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0951B695-2F68-4BCC-94D6-D03F3236E75A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0951B695-2F68-4BCC-94D6-D03F3236E75A}.Release|Any CPU.Build.0 = Release|Any CPU + {A48688A9-F4EE-48CB-9838-64EBCC5B933C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A48688A9-F4EE-48CB-9838-64EBCC5B933C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A48688A9-F4EE-48CB-9838-64EBCC5B933C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A48688A9-F4EE-48CB-9838-64EBCC5B933C}.Release|Any CPU.Build.0 = Release|Any CPU + {B637F49E-F6DF-44B7-B921-01A8B4BCBC17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B637F49E-F6DF-44B7-B921-01A8B4BCBC17}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B637F49E-F6DF-44B7-B921-01A8B4BCBC17}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B637F49E-F6DF-44B7-B921-01A8B4BCBC17}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5D87C51F-E2BC-1CBF-36A4-FA03D3A09904} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {356A77BF-AE0A-492D-9A77-1CE170F3D93B} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {0951B695-2F68-4BCC-94D6-D03F3236E75A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {A48688A9-F4EE-48CB-9838-64EBCC5B933C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {B637F49E-F6DF-44B7-B921-01A8B4BCBC17} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FF840A73-EBBC-456B-A425-AD1AA68D2C2B} EndGlobalSection diff --git a/UnitTest_Engine/Compute/CheckTest.cs b/UnitTest_Engine/Compute/CheckTest.cs index dc00d43b..63e54462 100644 --- a/UnitTest_Engine/Compute/CheckTest.cs +++ b/UnitTest_Engine/Compute/CheckTest.cs @@ -167,12 +167,18 @@ public static TestResult CheckTest(this UT.UnitTest test) /**** Private Methods ****/ /***************************************************/ - private static TestResult CheckTest(MethodBase method, UT.TestData data, int index) + public static TestResult CheckTest(MethodBase method, UT.TestData data, int index) { if (data == null) return new TestResult { Status = oM.Test.TestStatus.Error, Description = "TestData", Message = "The provided TestData was null and could not be evaluated." }; - string description = "TestData: " + (!string.IsNullOrWhiteSpace(data.Name) ? $"name: {data.Name}," : "") + $"index: {index}"; + string description; + if (index < 0) + { + description = "Test data: " + string.Join(", ", data.Inputs.Select(x => x?.ToString() ?? "null")); + } + else + description = "TestData: " + (!string.IsNullOrWhiteSpace(data.Name) ? $"name: {data.Name}," : "") + $"index: {index}"; TestResult testResult = new TestResult { Description = description }; var result = Run(method, data); diff --git a/UnitTest_Tests/UnitTestRunner.cs b/UnitTest_Tests/UnitTestRunner.cs new file mode 100644 index 00000000..27e3f6a6 --- /dev/null +++ b/UnitTest_Tests/UnitTestRunner.cs @@ -0,0 +1,152 @@ +using BH.Engine.Base; +using BH.Engine.Test; +using BH.oM.Base.Attributes; +using BH.oM.Base.Debugging; +using BH.oM.Data.Library; +using BH.oM.Test.Results; +using BH.oM.Test.UnitTests; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Tests.UnitTests +{ + public class UnitTestRunner + { + + /***************************************************/ + + [TestCaseSource(nameof(TestData))] + [Description("Runs a datadriven unit datatest.")] + public void RunDatadrivenUnitTest(string fileName, MethodBase method, TestData data) + { + if (method == null || data == null) + { + Assert.Multiple(() => + { + Assert.That(method, Is.Not.Null, "Test method is null!"); + Assert.That(data, Is.Not.Null, "Test data is null!"); + List events; + if (m_DeserialisationEvents.TryGetValue(fileName, out events)) + { + string error = ""; + string warning = ""; + foreach (Event e in events.GroupBy(x => new { x.Type, x.Message }).Select(x => x.First())) + { + if (e.Type == EventType.Error) + error += e.Message + "\n"; + else + warning += e.Message + "\n"; + } + + if (error != "") + Assert.Fail("Errors raised during deserialisation of the unit tests:\n" + error); + if (warning != "") + Assert.Warn("Warnings raised during deserialisation of the unit tests:\n" + warning); + } + }); + } + else + { + List events; + if (m_DeserialisationEvents.TryGetValue(fileName, out events)) + { + string warning = ""; + foreach (Event e in events.GroupBy(x => new { x.Type, x.Message }).Select(x => x.First())) + { + warning += e.Message + "\n"; + } + if (warning != "") + Assert.Warn("Warnings raised during deserialisation of the unit tests:\n" + warning); + } + } + + TestResult result = BH.Engine.UnitTest.Compute.CheckTest(method, data, -1); + + Assert.That(result.Status, Is.EqualTo(oM.Test.TestStatus.Pass), $"The unit test did not pass {result.FullMessage(3, oM.Test.TestStatus.Error)}"); + + Assert.Pass(result.FullMessage()); + } + + /***************************************************/ + + [Description("Extracts all the unittest datasets from the relative folder in the currently executing repo and deserialises the content of the file and returns an IEnumerable with filename, method and one peice of testdata for each UnitTest and each TestData in the dataset.")] + public static IEnumerable TestData() + { + Setup.Query.CurrentAssemblies(); + + foreach (var item in Setup.Query.GetFiles(Setup.Query.CurrentDatasetsUTFolder(), "*.json", true)) + { + foreach (var test in GetTestData(item)) + { + yield return test; + } + } + } + + /***************************************************/ + + + [Description("Deserialises the content of the file and returns an IEnumerable with filename, method and one peice of testdata for each UnitTest and each TestData in the dataset.")] + public static IEnumerable GetTestData(string fileName) + { + string fileNameNoPath = fileName; + BH.Engine.Base.Compute.ClearCurrentEvents(); + try + { + if (!string.IsNullOrEmpty(fileName)) + { + fileNameNoPath = fileName.Replace(Setup.Query.CurrentDatasetsUTFolder(), ""); + StreamReader sr = new StreamReader(fileName); + string line = sr.ReadToEnd(); + sr.Close(); + + Dataset ds = (Dataset)BH.Engine.Serialiser.Convert.FromJson(line); + return GetTestData(fileNameNoPath, ds); + } + } + catch (Exception e) + { + BH.Engine.Base.Compute.RecordError(e, "Failed to deserialise dataset"); + return new List { new object[] { fileNameNoPath, null, null } }; + } + finally + { + m_DeserialisationEvents[fileNameNoPath] = BH.Engine.Base.Query.CurrentEvents(); + } + + return new List { new object[] { fileNameNoPath, null, null } }; + } + + /***************************************************/ + + [Description("Returns an IEnumerable with filename, method and one peice of testdata for each UnitTest and each TestData in the dataset.")] + public static IEnumerable GetTestData(string fileName, Dataset testDataSet) + { + if (testDataSet != null) + { + List unitTests = testDataSet.Data.OfType().ToList(); + + foreach (UnitTest test in unitTests) + { + foreach (TestData data in test.Data) + { + yield return new object[] { fileName, test.Method, data }; + } + } + } + + } + + /***************************************************/ + + private static Dictionary> m_DeserialisationEvents = new Dictionary>(); + + } +} diff --git a/UnitTest_Tests/UnitTest_Tests.csproj b/UnitTest_Tests/UnitTest_Tests.csproj new file mode 100644 index 00000000..ccd7729f --- /dev/null +++ b/UnitTest_Tests/UnitTest_Tests.csproj @@ -0,0 +1,72 @@ + + + + net8.0 + enable + disable + ..\Build + false + true + BH.Tests.UnitTests + Debug;Release + + + + + + + + + + + + + + + + + + + + + $(ProgramData)\BHoM\Assemblies\BHoM.dll + False + false + + + $(ProgramData)\BHoM\Assemblies\BHoM_Engine.dll + False + false + + + $(ProgramData)\BHoM\Assemblies\Data_oM.dll + False + false + + + $(ProgramData)\BHoM\Assemblies\Diffing_Engine.dll + False + false + + + $(ProgramData)\BHoM\Assemblies\Diffing_oM.dll + False + false + + + $(ProgramData)\BHoM\Assemblies\Reflection_Engine.dll + False + false + + + $(ProgramData)\BHoM\Assemblies\Serialiser_Engine.dll + False + false + + + $(ProgramData)\BHoM\Assemblies\Test_oM.dll + False + false + + + diff --git a/test.runsettings b/test.runsettings new file mode 100644 index 00000000..de1f1b72 --- /dev/null +++ b/test.runsettings @@ -0,0 +1,7 @@ + + + + C:\ProgramData\BHoM\Assemblies\TestStartupHook.dll + + +