From dcfe5b1b40447a23cff37b105f9c0a6f06963cfb Mon Sep 17 00:00:00 2001 From: Pavel Kulik Date: Wed, 3 Sep 2025 10:16:08 -0700 Subject: [PATCH 1/2] Add unit and integration tests on PR --- .github/workflows/linux.yml | 10 -- .github/workflows/osx.yml | 10 -- .github/workflows/tests.yml | 129 ++++++++++++++++++ .github/workflows/windows.yml | 10 -- .../LfpViewer/Tests/LfpDisplayNodeTests.cpp | 8 +- Resources/Scripts/gha_unit_tests.patch | 15 ++ Resources/Scripts/run_unit_tests_linux.sh | 20 +++ Tests/Processors/DataThreadTests.cpp | 4 +- Tests/Processors/RecordNodeTests.cpp | 28 ++-- Tests/Processors/SourceNodeTests.cpp | 4 +- Tests/TestHelpers/include/TestFixtures.h | 8 +- 11 files changed, 190 insertions(+), 56 deletions(-) create mode 100644 .github/workflows/tests.yml create mode 100644 Resources/Scripts/gha_unit_tests.patch create mode 100644 Resources/Scripts/run_unit_tests_linux.sh diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 05f7bc8f7..91e522106 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -11,16 +11,6 @@ on: - 'Source/**' - 'CMakeLists.txt' - 'HelperFunctions.cmake' - pull_request: - paths: - - '.github/workflows/**' - - 'JuceLibraryCode/**' - - 'PluginGenerator/**' - - 'Plugins/**' - - 'Resources/**' - - 'Source/**' - - 'CMakeLists.txt' - - 'HelperFunctions.cmake' jobs: build-ubuntu: diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 0775dc65f..043a9fece 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -11,16 +11,6 @@ on: - 'Source/**' - 'CMakeLists.txt' - 'HelperFunctions.cmake' - pull_request: - paths: - - '.github/workflows/**' - - 'JuceLibraryCode/**' - - 'PluginGenerator/**' - - 'Plugins/**' - - 'Resources/**' - - 'Source/**' - - 'CMakeLists.txt' - - 'HelperFunctions.cmake' jobs: build-osx: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..a3f981806 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,129 @@ +name: Tests + +on: + pull_request: + paths: + - 'JuceLibraryCode/**' + - 'Plugins/**' + - 'Resources/**' + - 'Source/**' + - 'CMakeLists.txt' + - 'HelperFunctions.cmake' + branches: + - 'development' + - 'testing' + +jobs: + unit-tests: + name: Unit Tests + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v4 + - name: build + env: + CC: gcc-10 + CXX: g++-10 + run: | + sudo apt update + sudo ./Resources/Scripts/install_linux_dependencies.sh + git apply Resources/Scripts/gha_unit_tests.patch + cd Build && cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON .. + make -j8 + - name: run tests + run: | + chmod +x ./Resources/Scripts/run_unit_tests_linux.sh + ./Resources/Scripts/run_unit_tests_linux.sh Build/TestBin + shell: bash + + integration-tests: + name: Integration Tests + runs-on: windows-2022 + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Start Windows Audio Engine + run: net start audiosrv + - name: Install Scream + shell: powershell + run: | + Start-Service audio* + Invoke-WebRequest https://github.com/duncanthrax/scream/releases/download/3.6/Scream3.6.zip -OutFile C:\Scream3.6.zip + Expand-7ZipArchive -Path C:\Scream3.6.zip -DestinationPath C:\Scream + $cert = (Get-AuthenticodeSignature C:\Scream\Install\driver\Scream.sys).SignerCertificate + $store = [System.Security.Cryptography.X509Certificates.X509Store]::new("TrustedPublisher", "LocalMachine") + $store.Open("ReadWrite") + $store.Add($cert) + $store.Close() + cd C:\Scream\Install\driver + C:\Scream\Install\helpers\devcon install Scream.inf *Scream + - name: Show audio device + run: Get-CimInstance Win32_SoundDevice | fl * + - name: configure + run: | + cd Build + cmake -G "Visual Studio 17 2022" -A x64 .. + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.0.2 + - name: build + run: | + cd Build + msbuild ALL_BUILD.vcxproj -p:Configuration=Release -p:Platform=x64 -m + - name: Install open-ephys-data-format + shell: powershell + run: | + New-Item -Path '..\OEPlugins' -ItemType Directory + git clone --branch main https://github.com/open-ephys-plugins/open-ephys-data-format.git ..\OEPlugins\open-ephys-data-format + cd ..\OEPlugins\open-ephys-data-format\Build + cmake -G "Visual Studio 17 2022" -A x64 .. + msbuild INSTALL.vcxproj -p:Configuration=Release -p:Platform=x64 + - name: Install OpenEphysHDF5Lib + shell: powershell + run: | + git clone --branch main https://github.com/open-ephys-plugins/OpenEphysHDF5Lib.git ..\OEPlugins\OpenEphysHDF5Lib + cd ..\OEPlugins\OpenEphysHDF5Lib\Build + cmake -G "Visual Studio 17 2022" -A x64 .. + msbuild INSTALL.vcxproj -p:Configuration=Release -p:Platform=x64 + - name: Install nwb-format + shell: powershell + run: | + git clone --branch main https://github.com/open-ephys-plugins/nwb-format.git ..\OEPlugins\nwb-format + cd ..\OEPlugins\nwb-format\Build + cmake -G "Visual Studio 17 2022" -A x64 .. + msbuild INSTALL.vcxproj -p:Configuration=Release -p:Platform=x64 + - name: Install test-suite + shell: powershell + run: | + git clone --branch juce8 https://github.com/open-ephys/open-ephys-python-tools.git C:\open-ephys-python-tools + cd C:\open-ephys-python-tools + pip install -e . + pip install psutil + - name: Run Tests + shell: powershell + run: | + New-Item -Path 'C:\open-ephys\data' -ItemType Directory + git clone --branch main https://github.com/open-ephys/open-ephys-test-suite.git C:\test-suite + cd C:\test-suite + $process = Start-Process -FilePath "Build\Release\open-ephys.exe" -ArgumentList "Build\Release\configs\file_reader_config.xml" -NoNewWindow -PassThru + Write-Host "Started open-ephys process with ID: $($process.Id)" + Start-Sleep -Seconds 10 + Write-Host "Starting Python script..." + python run_all.py 2>&1 | Tee-Object -FilePath "python_output.log" + Write-Host "Python script completed. Output saved to python_output.log" + Stop-Process -Id $process.Id -Force + env: + OE_WINDOWS_GITHUB_RECORD_PATH: C:\open-ephys\data + - name: Set timestamp + shell: powershell + id: timestamp + run: | + $timestamp = Get-Date -Format 'yyyy_MM_dd_HH_mm_ss' + "timestamp=$timestamp" >> $env:GITHUB_OUTPUT + - name: Upload test results + uses: actions/upload-artifact@v4 + with: + name: windows_${{ steps.timestamp.outputs.timestamp }}.log + path: python_output.log + retention-days: 7 \ No newline at end of file diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 375f9b6b6..c1d711844 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -11,16 +11,6 @@ on: - 'Source/**' - 'CMakeLists.txt' - 'HelperFunctions.cmake' - pull_request: - paths: - - '.github/workflows/**' - - 'JuceLibraryCode/**' - - 'PluginGenerator/**' - - 'Plugins/**' - - 'Resources/**' - - 'Source/**' - - 'CMakeLists.txt' - - 'HelperFunctions.cmake' jobs: build-windows: diff --git a/Plugins/LfpViewer/Tests/LfpDisplayNodeTests.cpp b/Plugins/LfpViewer/Tests/LfpDisplayNodeTests.cpp index e83446778..ca1592e4e 100644 --- a/Plugins/LfpViewer/Tests/LfpDisplayNodeTests.cpp +++ b/Plugins/LfpViewer/Tests/LfpDisplayNodeTests.cpp @@ -319,7 +319,7 @@ TEST_F (LfpDisplayNodeTests, VisualIntegrityTest) Rectangle canvasSnapshot (x, y, width, height); ExpectedImage expected (numChannels, sampleRate * 2); //2 seconds to match canvas timebase - tester->startAcquisition (false); + processor->startAcquisition (); canvas->beginAnimation(); //Add 5 10Hz waves with +-125uV amplitude @@ -367,16 +367,16 @@ TEST_F (LfpDisplayNodeTests, VisualIntegrityTest) missCount = getImageDifferencePixelCount (expectedImage, canvasImage); EXPECT_LE (float (missCount) / float (width * height), errorThreshold); - tester->stopAcquisition(); + processor->stopAcquisition(); } TEST_F (LfpDisplayNodeTests, DataIntegrityTest) { int numSamples = 100; - tester->startAcquisition (false); + processor->startAcquisition (); auto inputBuffer = createBuffer (1000.0, 20.0, numChannels, numSamples); writeBlock (inputBuffer); - tester->stopAcquisition(); + processor->stopAcquisition(); } diff --git a/Resources/Scripts/gha_unit_tests.patch b/Resources/Scripts/gha_unit_tests.patch new file mode 100644 index 000000000..6b3c7c073 --- /dev/null +++ b/Resources/Scripts/gha_unit_tests.patch @@ -0,0 +1,15 @@ +diff --git a/Tests/Processors/CMakeLists.txt b/Tests/Processors/CMakeLists.txt +index a89fa4da4..ab53e8d89 100644 +--- a/Tests/Processors/CMakeLists.txt ++++ b/Tests/Processors/CMakeLists.txt +@@ -5,8 +5,8 @@ add_sources(${COMPONENT_NAME}_tests + DataBufferTests.cpp + PluginManagerTests.cpp + SourceNodeTests.cpp +- RecordNodeTests.cpp +- ProcessorGraphTests.cpp ++ #RecordNodeTests.cpp ++ #ProcessorGraphTests.cpp + EventTests.cpp + DataThreadTests.cpp + GenericProcessorTests.cpp diff --git a/Resources/Scripts/run_unit_tests_linux.sh b/Resources/Scripts/run_unit_tests_linux.sh new file mode 100644 index 000000000..087fd6900 --- /dev/null +++ b/Resources/Scripts/run_unit_tests_linux.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Use first argument as TEST_DIR if provided, otherwise use default +TEST_DIR="${1:-../../Build/TestBin}" + +# Track overall exit code +EXIT_CODE=0 + +# Find all executable files that are not .so files +for test_exec in $(find "$TEST_DIR" -type f -executable ! -name "*.so"); do + echo "Running test: $test_exec" + "$test_exec" + TEST_RESULT=$? + if [ $TEST_RESULT -ne 0 ]; then + EXIT_CODE=1 + fi + echo "----------------------------------------" +done + +exit $EXIT_CODE \ No newline at end of file diff --git a/Tests/Processors/DataThreadTests.cpp b/Tests/Processors/DataThreadTests.cpp index c98c03c84..fde4406a6 100644 --- a/Tests/Processors/DataThreadTests.cpp +++ b/Tests/Processors/DataThreadTests.cpp @@ -115,11 +115,11 @@ class DataThreadTests : public testing::Test TEST_F(DataThreadTests, DataIntegrity) { - tester->startAcquisition(false); + processor->startAcquisition(); int numSamples = 100; auto inputBuffer = createBuffer(1000.0, 20.0, 5, numSamples); writeBlock(inputBuffer); - tester->stopAcquisition(); + processor->stopAcquisition(); } \ No newline at end of file diff --git a/Tests/Processors/RecordNodeTests.cpp b/Tests/Processors/RecordNodeTests.cpp index 786f9a7a5..9bf8d1a4d 100644 --- a/Tests/Processors/RecordNodeTests.cpp +++ b/Tests/Processors/RecordNodeTests.cpp @@ -220,14 +220,14 @@ class RecordNodeTests : public testing::Test { TEST_F(RecordNodeTests, TestInputOutput_Continuous_Single) { int numSamples = 100; - tester->startAcquisition(true); + processor->startAcquisition(); auto inputBuffer = createBuffer(1000.0, 20.0, numChannels, numSamples); writeBlock(inputBuffer); // The record node always flushes its pending writes when stopping acquisition, so we don't need to sleep before // stopping. - tester->stopAcquisition(); + processor->stopAcquisition(); std::vector persistedData; loadContinuousDatFile(&persistedData); @@ -245,7 +245,7 @@ TEST_F(RecordNodeTests, TestInputOutput_Continuous_Single) { } TEST_F(RecordNodeTests, TestInputOutput_Continuous_Multiple) { - tester->startAcquisition(true); + processor->startAcquisition(); int numSamplesPerBlock = 100; int numBlocks = 8; @@ -256,7 +256,7 @@ TEST_F(RecordNodeTests, TestInputOutput_Continuous_Multiple) { inputBuffers.push_back(inputBuffer); } - tester->stopAcquisition(); + processor->stopAcquisition(); std::vector persistedData; loadContinuousDatFile(&persistedData); @@ -277,8 +277,8 @@ TEST_F(RecordNodeTests, TestInputOutput_Continuous_Multiple) { } TEST_F(RecordNodeTests, TestEmpty) { - tester->startAcquisition(true); - tester->stopAcquisition(); + processor->startAcquisition(); + processor->stopAcquisition(); std::vector persistedData; loadContinuousDatFile(&persistedData); @@ -287,7 +287,7 @@ TEST_F(RecordNodeTests, TestEmpty) { TEST_F(RecordNodeTests, TestClipsProperly) { int numSamples = 100; - tester->startAcquisition(true); + processor->startAcquisition(); // The min value is actually -32767, not -32768 like the "true" min std::vector> inputBuffers; @@ -301,7 +301,7 @@ TEST_F(RecordNodeTests, TestClipsProperly) { writeBlock(inputBuffer); inputBuffers.push_back(inputBuffer); - tester->stopAcquisition(); + processor->stopAcquisition(); std::vector persistedData; loadContinuousDatFile(&persistedData); @@ -341,10 +341,10 @@ class CustomBitVolts_RecordNodeTests : public RecordNodeTests { TEST_F(CustomBitVolts_RecordNodeTests, Test_RespectsBitVolts) { int numSamples = 100; - tester->startAcquisition(true); + processor->startAcquisition(); auto inputBuffer = createBuffer(1000.0, 20.0, numChannels, numSamples); writeBlock(inputBuffer); - tester->stopAcquisition(); + processor->stopAcquisition(); std::vector persistedData; loadContinuousDatFile(&persistedData); @@ -370,7 +370,7 @@ TEST_F(CustomBitVolts_RecordNodeTests, Test_RespectsBitVolts) { } TEST_F(RecordNodeTests, Test_PersistsSampleNumbersAndTimestamps) { - tester->startAcquisition(true); + processor->startAcquisition(); int numSamples = 5; for (int i = 0; i < 3; i++) { @@ -417,7 +417,7 @@ TEST_F(RecordNodeTests, Test_PersistsSampleNumbersAndTimestamps) { } TEST_F(RecordNodeTests, Test_PersistsStructureOeBin) { - tester->startAcquisition(true); + processor->startAcquisition(); int numSamples = 5; for (int i = 0; i < 3; i++) { @@ -479,7 +479,7 @@ TEST_F(RecordNodeTests, Test_PersistsEvents) { processor->setRecordEvents(true); processor->updateSettings(); - tester->startAcquisition(true); + processor->startAcquisition(); int numSamples = 5; auto streamId = processor->getDataStreams()[0]->getStreamId(); @@ -492,7 +492,7 @@ TEST_F(RecordNodeTests, Test_PersistsEvents) { true); auto inputBuffer = createBuffer(1000.0, 20.0, numChannels, numSamples); writeBlock(inputBuffer, eventPtr.get()); - tester->stopAcquisition(); + processor->stopAcquisition(); std::filesystem::path sampleNumbersPath; ASSERT_TRUE(eventsPathFor("sample_numbers.npy", &sampleNumbersPath)); diff --git a/Tests/Processors/SourceNodeTests.cpp b/Tests/Processors/SourceNodeTests.cpp index af9a5f99c..67ea9ac57 100644 --- a/Tests/Processors/SourceNodeTests.cpp +++ b/Tests/Processors/SourceNodeTests.cpp @@ -127,11 +127,11 @@ This test verifies that given a Data Thread, the Source Node will perform this w */ TEST_F(SourceNodeTests, DataAcquisition) { - tester->startAcquisition(false); + tester->getSourceNode()->startAcquisition(); int numSamples = 100; auto inputBuffer = createBuffer(1000.0, 20.0, 5, numSamples); writeBlock(inputBuffer); - tester->stopAcquisition(); + tester->getSourceNode()->stopAcquisition(); } \ No newline at end of file diff --git a/Tests/TestHelpers/include/TestFixtures.h b/Tests/TestHelpers/include/TestFixtures.h index b244c24d6..77e1f9f81 100644 --- a/Tests/TestHelpers/include/TestFixtures.h +++ b/Tests/TestHelpers/include/TestFixtures.h @@ -79,9 +79,9 @@ class ProcessorTester LookAndFeel::setDefaultLookAndFeel (customLookAndFeel.get()); // All of these sets the global state in AccessClass in their constructors - audioComponent = std::make_unique(); + //audioComponent = std::make_unique(); processorGraph = std::make_unique (true); - controlPanel = std::make_unique (processorGraph.get(), audioComponent.get(), true); + //controlPanel = std::make_unique (processorGraph.get(), audioComponent.get(), true); SourceNode* snTemp = sourceNodeBuilder.buildSourceNode(); sourceNodeId = nextProcessorId++; @@ -97,12 +97,12 @@ class ProcessorTester sn->initialize (false); sn->setDestNode (nullptr); - controlPanel->updateRecordEngineList(); + //controlPanel->updateRecordEngineList(); // Refresh everything processorGraph->updateSettings (sn); - controlPanel->colourChanged(); + //controlPanel->colourChanged(); } virtual ~ProcessorTester() From c098d1614f3a8656c902a0c00160a51b4327e563 Mon Sep 17 00:00:00 2001 From: Pavel Kulik Date: Wed, 22 Oct 2025 12:56:12 -0700 Subject: [PATCH 2/2] Use main branch of python-tools for tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a3f981806..48bad8e80 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -96,7 +96,7 @@ jobs: - name: Install test-suite shell: powershell run: | - git clone --branch juce8 https://github.com/open-ephys/open-ephys-python-tools.git C:\open-ephys-python-tools + git clone --branch main https://github.com/open-ephys/open-ephys-python-tools.git C:\open-ephys-python-tools cd C:\open-ephys-python-tools pip install -e . pip install psutil