Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for unit testing #15

Merged
merged 25 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b8d3ffb
Added workflow to run tests from submodule automatically
ncguilbeault May 10, 2024
8c3d103
Added tests
ncguilbeault May 10, 2024
adde542
Updated Bonsai.ML solution file to outside src folder and into main
ncguilbeault May 10, 2024
d53ca8c
Renamed run-tests workflow to just test
ncguilbeault May 28, 2024
ec09951
Updated package versions in csproj
ncguilbeault May 28, 2024
5d428c5
Updated package versions in bonsai config
ncguilbeault May 28, 2024
5f068c9
Added original receptive field JSON file
ncguilbeault May 28, 2024
2c6d854
Updated test to new data folder and check against oiginal RF
ncguilbeault May 28, 2024
d20e844
Updated argument parameter to double
ncguilbeault May 28, 2024
d2e9d03
Updated bonsai workflow to new data folder
ncguilbeault May 28, 2024
00421f4
Updated python script to use data directory
ncguilbeault May 28, 2024
c9b6868
Updated scripts for new data directory
ncguilbeault May 28, 2024
4cf87e6
Updated csproj to include all files in test folder
ncguilbeault May 28, 2024
44c35df
Renamed folder to tests and updated test names
ncguilbeault May 28, 2024
0ff75f5
Updated workflow to target correct solution file
ncguilbeault May 28, 2024
2fcae28
Updated build workflow to target correct solution file
ncguilbeault May 28, 2024
816f429
Updated test workflow to use build workflow
ncguilbeault May 28, 2024
2af7d25
Updated base directory name
ncguilbeault May 28, 2024
7a20fec
Run workflow directly from the test folder
glopesdev Jun 14, 2024
a80e254
Merge pull request #16 from bonsai-rx/automated-tests-slim
ncguilbeault Jun 14, 2024
2ff9df0
Avoid shutdown exception due to deprecated API
glopesdev Jun 21, 2024
c524079
Added test cleanup procedure
ncguilbeault Jun 26, 2024
da2eb66
Exclude cs files from copying in test project
ncguilbeault Jun 26, 2024
3ad5e99
Removed deployment item reference to deprecated files.
ncguilbeault Jun 26, 2024
ebff6af
Updated test workflow to properly reuse build steps
ncguilbeault Jun 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ jobs:
with:
submodules: true

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- name: Setup .NET Core SDK
uses: actions/[email protected]
with:
dotnet-version: 8.x

- name: Restore NuGet Packages
run: msbuild -t:restore src/Bonsai.ML.sln
run: dotnet restore Bonsai.ML.sln

- name: Build Solution
run: msbuild src/Bonsai.ML.sln /p:Configuration=Release
run: dotnet build Bonsai.ML.sln -c Release
20 changes: 20 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Builds and runs unit tests for the examples
name: Test Examples

on:
workflow_dispatch:

jobs:
test:
runs-on: windows-latest
steps:
- name: Setup Python 3.10
uses: actions/setup-python@v3
with:
python-version: 3.10

- name: Build Solution
uses: ./.github/workflows/build.yml
glopesdev marked this conversation as resolved.
Show resolved Hide resolved

- name: Run Tests
run: dotnet test Bonsai.ML.sln
50 changes: 50 additions & 0 deletions Bonsai.ML.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{12312384-8828-4786-AE19-EFCEDF968290}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.ML", "src\Bonsai.ML\Bonsai.ML.csproj", "{AA6BE73F-1E15-49A5-AC3C-CD069035C940}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.ML.LinearDynamicalSystems", "src\Bonsai.ML.LinearDynamicalSystems\Bonsai.ML.LinearDynamicalSystems.csproj", "{17AABD18-E275-4409-9E33-3D755B809FF6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.ML.Visualizers", "src\Bonsai.ML.Visualizers\Bonsai.ML.Visualizers.csproj", "{196AA5C7-AE8A-477B-B01A-B94676EC60EE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{461FE3E2-21C4-47F9-8405-DF72326AAB2B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.ML.LinearDynamicalSystems.Tests", "tests\Bonsai.ML.LinearDynamicalSystems.Tests\Bonsai.ML.LinearDynamicalSystems.Tests.csproj", "{81DB65B3-EA65-4947-8CF1-0E777324C082}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AA6BE73F-1E15-49A5-AC3C-CD069035C940}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA6BE73F-1E15-49A5-AC3C-CD069035C940}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA6BE73F-1E15-49A5-AC3C-CD069035C940}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA6BE73F-1E15-49A5-AC3C-CD069035C940}.Release|Any CPU.Build.0 = Release|Any CPU
{17AABD18-E275-4409-9E33-3D755B809FF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17AABD18-E275-4409-9E33-3D755B809FF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17AABD18-E275-4409-9E33-3D755B809FF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17AABD18-E275-4409-9E33-3D755B809FF6}.Release|Any CPU.Build.0 = Release|Any CPU
{196AA5C7-AE8A-477B-B01A-B94676EC60EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{196AA5C7-AE8A-477B-B01A-B94676EC60EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{196AA5C7-AE8A-477B-B01A-B94676EC60EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{196AA5C7-AE8A-477B-B01A-B94676EC60EE}.Release|Any CPU.Build.0 = Release|Any CPU
{81DB65B3-EA65-4947-8CF1-0E777324C082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81DB65B3-EA65-4947-8CF1-0E777324C082}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81DB65B3-EA65-4947-8CF1-0E777324C082}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81DB65B3-EA65-4947-8CF1-0E777324C082}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{AA6BE73F-1E15-49A5-AC3C-CD069035C940} = {12312384-8828-4786-AE19-EFCEDF968290}
{17AABD18-E275-4409-9E33-3D755B809FF6} = {12312384-8828-4786-AE19-EFCEDF968290}
{196AA5C7-AE8A-477B-B01A-B94676EC60EE} = {12312384-8828-4786-AE19-EFCEDF968290}
{81DB65B3-EA65-4947-8CF1-0E777324C082} = {461FE3E2-21C4-47F9-8405-DF72326AAB2B}
EndGlobalSection
EndGlobal
42 changes: 0 additions & 42 deletions src/Bonsai.ML.sln

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bonsai.System" Version="2.8.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>
<ItemGroup>
<Content Include="ReceptiveFieldSimpleCell/*" Exclude="ReceptiveFieldSimpleCell/*.cs">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Bonsai.ML.LinearDynamicalSystems\Bonsai.ML.LinearDynamicalSystems.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
using System;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Reflection;
using Newtonsoft.Json;

namespace Bonsai.ML.LinearDynamicalSystems.Tests.ReceptiveFieldSimpleCell;

[TestClass]
public class ReceptiveFieldSimpleCellTest
{
private string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ReceptiveFieldSimpleCell");
private int nSamples = 10000;

private void RunProcess(string fileName, string fmtArg)
{
var start = new ProcessStartInfo
{
FileName = fileName,
Arguments = fmtArg,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};

using (var process = new Process {StartInfo = start})
{
process.Start();
var output = process.StandardOutput.ReadToEnd();
var error = process.StandardError.ReadToEnd();
process.WaitForExit();

if (!string.IsNullOrEmpty(output))
{
Console.WriteLine("Standard Output: ");
Console.WriteLine(output);
}

if (!string.IsNullOrEmpty(error))
{
Console.WriteLine("Standard Error: ");
Console.WriteLine(error);
}
}
}

private void DownloadData(string basePath)
{
string zipFileUrl = "https://zenodo.org/records/10879253/files/ReceptiveFieldSimpleCell.zip";
string outputPath = Path.Combine(basePath, "data");
string tempFilePath = Path.Combine(Path.GetTempPath(), "tempfile.zip");

try
{
using (var httpClient = new HttpClient())
{
var responseBytes = httpClient.GetByteArrayAsync(zipFileUrl).Result;
File.WriteAllBytes(tempFilePath, responseBytes);
Console.WriteLine("File downloaded successfully.");
}

ZipFile.ExtractToDirectory(tempFilePath, outputPath, true);
Console.WriteLine("File extracted successfully.");
}

catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}

finally
{
if (File.Exists(tempFilePath))
{
File.Delete(tempFilePath);
Console.WriteLine("Temporary file deleted.");
}
}
}

private void RunPythonScript(string basePath)
{
var pythonExec = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "python"
: "python3";
var scriptPath = Path.Combine(basePath, "run_python_test.py");
RunProcess(pythonExec, $"\"{scriptPath}\" {basePath} {nSamples}");

Console.WriteLine("Run python script finished.");
}

private async Task RunBonsaiWorkflow(string basePath)
{
var currentDirectory = Environment.CurrentDirectory;
Environment.CurrentDirectory = basePath;
try
{
var workflowPath = Path.Combine(basePath, "receptive_field.bonsai");
await WorkflowHelper.RunWorkflow(
workflowPath,
properties: [("NSamples", nSamples)]);
Console.WriteLine("Run bonsai workflow finished.");
}
finally { Environment.CurrentDirectory = currentDirectory; }
}

private string GetJsonData(string jsonFileName)
{
string jsonString = File.ReadAllText(jsonFileName);
State state = JsonConvert.DeserializeObject<State>(jsonString);

Check warning on line 113 in tests/Bonsai.ML.LinearDynamicalSystems.Tests/ReceptiveFieldSimpleCell/ReceptiveFieldSimpleCellTest.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.
string jsonState = JsonConvert.SerializeObject(state);
return jsonState;
}

private State GetStateFromJson(string jsonFileName)
{
string jsonString = File.ReadAllText(jsonFileName);
State state = JsonConvert.DeserializeObject<State>(jsonString);

Check warning on line 121 in tests/Bonsai.ML.LinearDynamicalSystems.Tests/ReceptiveFieldSimpleCell/ReceptiveFieldSimpleCellTest.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.
return state;

Check warning on line 122 in tests/Bonsai.ML.LinearDynamicalSystems.Tests/ReceptiveFieldSimpleCell/ReceptiveFieldSimpleCellTest.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference return.
}

private bool CompareJSONData(string basePath, double tolerance = 1e-9)
{
var originalFileName = Path.Combine(basePath, "original-receptivefield.json");
var bonsaiFileName = Path.Combine(basePath, "bonsai-receptivefield.json");
var pythonFileName = Path.Combine(basePath, "python-receptivefield.json");

var originalOutput = GetStateFromJson(originalFileName);
var bonsaiOutput = GetStateFromJson(bonsaiFileName);
var pythonOutput = GetStateFromJson(pythonFileName);

try
{
for (int i = 0; i < bonsaiOutput.X.GetLength(0); i++)
{
for (int j = 0; j < bonsaiOutput.X.GetLength(1); j++)
{
if (Math.Abs(bonsaiOutput.X[i,j] - pythonOutput.X[i,j]) > tolerance || Math.Abs(originalOutput.X[i,j] - pythonOutput.X[i,j]) > tolerance)
{
Console.WriteLine($"Discrepency found comparing X at index ({i},{j}) with tolerance {tolerance}: bonsaiOutput = {bonsaiOutput.X[i,j]}, pythonOutput = {pythonOutput.X[i,j]}, originalOutput = {originalOutput.X[i,j]}.");
return false;
}
}
}
for (int i = 0; i < bonsaiOutput.P.GetLength(0); i++)
{
for (int j = 0; j < bonsaiOutput.P.GetLength(1); j++)
{
if (Math.Abs(bonsaiOutput.P[i,j] - pythonOutput.P[i,j]) > tolerance || Math.Abs(originalOutput.P[i,j] - pythonOutput.P[i,j]) > tolerance)
{
Console.WriteLine($"Discrepency found comparing P at index ({i},{j}) with tolerance {tolerance}: bonsaiOutput = {bonsaiOutput.P[i,j]}, pythonOutput = {pythonOutput.P[i,j]}, originalOutput = {originalOutput.P[i,j]}.");
return false;
}
}
}
}
catch
{
return false;
}
return true;
}

[TestInitialize]
[DeploymentItem("run_python_test.py")]
[DeploymentItem("receptive_field.py")]
[DeploymentItem("receptive_field.bonsai")]
[DeploymentItem("original-receptivefield.json")]
public async Task TestSetup()
{
Directory.CreateDirectory(basePath);
DownloadData(basePath);
RunPythonScript(basePath);
await RunBonsaiWorkflow(basePath);
}

[TestMethod]
public void CompareResults()
{
var result = CompareJSONData(basePath);
Assert.IsTrue(result);
}

[TestCleanup]
public void Cleanup()
{
if (Directory.Exists(basePath))
{
Directory.Delete(basePath, true);
}
}
}

Large diffs are not rendered by default.

Loading
Loading