-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from bonsai-rx/automated-tests
Add support for unit testing
- Loading branch information
Showing
11 changed files
with
596 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
||
- name: Run Tests | ||
run: dotnet test Bonsai.ML.sln |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file was deleted.
Oops, something went wrong.
29 changes: 29 additions & 0 deletions
29
tests/Bonsai.ML.LinearDynamicalSystems.Tests/Bonsai.ML.LinearDynamicalSystems.Tests.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
195 changes: 195 additions & 0 deletions
195
....ML.LinearDynamicalSystems.Tests/ReceptiveFieldSimpleCell/ReceptiveFieldSimpleCellTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
string jsonState = JsonConvert.SerializeObject(state); | ||
return jsonState; | ||
} | ||
|
||
private State GetStateFromJson(string jsonFileName) | ||
{ | ||
string jsonString = File.ReadAllText(jsonFileName); | ||
State state = JsonConvert.DeserializeObject<State>(jsonString); | ||
return state; | ||
} | ||
|
||
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); | ||
} | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
...sai.ML.LinearDynamicalSystems.Tests/ReceptiveFieldSimpleCell/original-receptivefield.json
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.