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

update unit tests #1

Merged
merged 56 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
8f8fd03
t2
haoershi Dec 5, 2023
161dec2
update
haoershi Dec 5, 2023
12b96ed
Delete .DS_Store
haoershi Dec 5, 2023
27b350d
Delete matlab/users directory
haoershi Dec 5, 2023
2ccfabb
Update matlab.yml
haoershi Dec 5, 2023
28cb96e
update
haoershi Dec 5, 2023
69bd85c
update
haoershi Dec 5, 2023
9e34166
Update matlab.yml
haoershi Dec 5, 2023
d92689d
Update matlab.yml
haoershi Dec 5, 2023
4f5adfc
update
haoershi Dec 5, 2023
b2c3f5a
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
d7de918
Update matlab.yml
haoershi Dec 5, 2023
9b3ed05
Update matlab.yml
haoershi Dec 5, 2023
69f2d9d
update
haoershi Dec 5, 2023
6137a6a
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
aebff6c
Update matlab.yml
haoershi Dec 5, 2023
e0f9e6f
update
haoershi Dec 5, 2023
8ace299
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
1fd0a2e
Update matlab.yml
haoershi Dec 5, 2023
14e1373
update
haoershi Dec 5, 2023
bb9f794
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
2b2999f
update
haoershi Dec 5, 2023
41df01f
Update matlab.yml
haoershi Dec 5, 2023
d573691
update
haoershi Dec 5, 2023
7503914
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
9057e64
Update matlab.yml
haoershi Dec 5, 2023
ce6a926
update
haoershi Dec 5, 2023
fa30e90
Update matlab.yml
haoershi Dec 5, 2023
f7688e0
update
haoershi Dec 5, 2023
05f6f7b
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
7e65ba2
Update matlab.yml
haoershi Dec 5, 2023
bb271a1
Update matlab.yml
haoershi Dec 5, 2023
1db0c8c
Update matlab.yml
haoershi Dec 5, 2023
a1449b3
Update matlab.yml
haoershi Dec 5, 2023
f3c1fed
Update matlab.yml
haoershi Dec 5, 2023
d93985e
Update matlab.yml
haoershi Dec 5, 2023
a9e892d
update
haoershi Dec 5, 2023
252560f
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
d3e20a7
update
haoershi Dec 5, 2023
1aaf85b
Update python.yml
haoershi Dec 5, 2023
e93d772
update
haoershi Dec 5, 2023
afeeb11
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
bc84861
update
haoershi Dec 5, 2023
5774c67
update
haoershi Dec 5, 2023
f9e980f
Delete python/CNTtools/users directory
haoershi Dec 5, 2023
d686080
update
haoershi Dec 5, 2023
ddb8d0d
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
4b34763
Update test_auth.py
haoershi Dec 5, 2023
1f2ba2b
update
haoershi Dec 5, 2023
590905c
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
2f95dcf
Update python.yml
haoershi Dec 5, 2023
53be3e5
update
haoershi Dec 5, 2023
b238f63
Merge branch 'main' of https://github.com/haoershi/CNT_Preprocessing_…
haoershi Dec 5, 2023
49b8629
update
haoershi Dec 5, 2023
15d700a
update
haoershi Dec 5, 2023
9de8a67
Delete .DS_Store
haoershi Dec 5, 2023
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
28 changes: 22 additions & 6 deletions .github/workflows/matlab.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ name: MATLAB Unit Tests

on:
push:
branches:
- main
branches: [ "main" ]
paths:
- './matlab/**'
- 'matlab/**'
pull_request:
branches: [ "main" ]
paths:
- 'matlab/**'
workflow_dispatch:

jobs:
Expand All @@ -20,10 +23,23 @@ jobs:
uses: matlab-actions/setup-matlab@v1
with:
matlab-version: R2021b # Adjust to your MATLAB version
license-server: your-license-server-address # Optional if using a network license

- name: Run MATLAB Unit Tests
env:
IEEG_USERNAME: ${{ secrets.IEEG_USERNAME }}
IEEG_PASSWORD: ${{ secrets.IEEG_PASSWORD }}
run: |
matlab -batch "runtests('test','IncludeSubfolders',true)"
working-directory: ./matlab
matlab -batch "import matlab.unittest.TestRunner; import matlab.unittest.TestSuite; import matlab.unittest.plugins.XMLPlugin; suite = testsuite('matlab/test','IncludeSubfolders',true); runner = TestRunner.withNoPlugins; xmlFile = 'matlab/test-results.xml'; p = XMLPlugin.producingJUnitFormat(xmlFile); runner.addPlugin(p); result = runner.run(suite); if any([result.Failed]) exit(1); end"

- name: Upload Test Results
uses: actions/upload-artifact@v2
with:
name: test-results
path: matlab/test-results.xml

- name: Display Test Results
uses: mikepenz/action-junit-report@v4
if: success() || failure() # always run even if the previous step fails
with:
report_paths: 'matlab/test-results.xml'

13 changes: 8 additions & 5 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ on:
push:
branches: [ "main" ]
paths:
- './python/**'
- 'python/**'
pull_request:
branches: [ "main" ]
paths:
- './python/**'
# workflow_dispatch:
- 'python/**'
workflow_dispatch:

jobs:
build:
Expand All @@ -34,8 +34,11 @@ jobs:
python -m pip install --upgrade pip
python -m pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
working-directory: ./python
working-directory: python
- name: Test with pytest
env:
IEEG_USERNAME: ${{ secrets.IEEG_USERNAME }}
IEEG_PASSWORD: ${{ secrets.IEEG_PASSWORD }}
run: |
pytest
working-directory: ./python
working-directory: python
23 changes: 16 additions & 7 deletions matlab/iEEGData.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ The ending time (in seconds) of the data clip.
dura
data
fs
nchs
ch_names
ref_chnames
raw
Expand Down Expand Up @@ -94,6 +95,8 @@ The ending time (in seconds) of the data clip.
obj.data = p.Results.data;
obj.fs = p.Results.fs;
obj.ch_names = p.Results.ch_names;
assert(size(obj.data,2)==length(obj.ch_names));
obj.nchs = length(obj.ch_names);
obj.ref_chnames = {};
obj.index = [];
obj.power = struct();
Expand All @@ -108,6 +111,7 @@ function download(obj, user)
obj.data = output.values;
obj.fs = output.fs;
obj.ch_names = output.chLabels;
obj.nchs = length(obj.ch_names);
obj.raw = obj.data;
obj.raw_chs = obj.ch_names;
obj.username = user.usr;
Expand Down Expand Up @@ -142,6 +146,7 @@ function reject_nonieeg(obj)
obj.nonieeg = find_non_ieeg(obj.ch_names);
obj.data = obj.data(:, ~obj.nonieeg);
obj.ch_names = obj.ch_names(~obj.nonieeg);
obj.nchs = length(obj.ch_names);
obj.history{end + 1} = 'reject_nonieeg';
end

Expand All @@ -151,6 +156,7 @@ function reject_artifact(obj)
[obj.bad, obj.reject_details] = identify_bad_chs(obj.data, obj.fs);
obj.data = obj.data(:, ~obj.bad);
obj.ch_names = obj.ch_names(~obj.bad);
obj.nchs = length(obj.ch_names);
obj.history{end + 1} = 'reject_artifact';
end

Expand Down Expand Up @@ -196,9 +202,9 @@ function filter(obj, varargin)
end
end
p = inputParser;
addOptional(p, 'low_freq', 1, @isnumeric);
addOptional(p, 'high_freq', 120, @isnumeric);
addOptional(p, 'notch_freq', 60, @isnumeric);
addOptional(p, 'low_freq', defaults{1}, @isnumeric);
addOptional(p, 'high_freq', defaults{2}, @isnumeric);
addOptional(p, 'notch_freq', defaults{3}, @isnumeric);
parse(p, varargin{:});
low_freq = p.Results.low_freq;
high_freq = p.Results.high_freq;
Expand All @@ -224,6 +230,7 @@ function bipolar(obj)
obj.data(:, inds) = [];
obj.ch_names(inds) = [];
obj.ref_chnames(inds) = [];
obj.nchs = length(obj.ch_names);
obj.history{end + 1} = 'bipolar';
end

Expand Down Expand Up @@ -271,6 +278,7 @@ function laplacian(obj, varargin)
obj.data(:, inds) = [];
obj.ch_names(inds) = [];
obj.ref_chnames(inds) = [];
obj.nchs = length(obj.ch_names);
obj.history{end + 1} = 'laplacian';
end

Expand Down Expand Up @@ -328,6 +336,7 @@ function reref(obj, ref, varargin)
obj.ch_names(inds) = [];
obj.ref_chnames(inds) = [];
end
obj.nchs = length(obj.ch_names);
obj.history{end + 1} = ['reref-', ref];
end

Expand Down Expand Up @@ -356,7 +365,7 @@ If False (default), return the absolute power.
%}
freqs = default_freqs;
p = inputParser;
defaults = {freqs,nan,false};
defaults = {freqs,[],false};
for i = 1:length(varargin)
if isempty(varargin{i})
varargin{i} = defaults{i};
Expand All @@ -373,7 +382,7 @@ If False (default), return the absolute power.
nbands = size(band,1);
for i = 1:nbands
obj.power(i).freq = band(i,:);
obj.power(i).power = bandpower(obj.data, obj.fs, obj.power(i).freq, window, relative);
obj.power(i).power = band_power(obj.data, obj.fs, obj.power(i).freq, window, relative);
end
obj.history{end + 1} = 'bandpower';
end
Expand Down Expand Up @@ -634,7 +643,6 @@ function heatmap_settings(obj,varargin)
% or apply customized settings
% customized color could be 1:colormap of size nX3,
% 2:cell/string array of color hex codes
nchs = size(obj.data,2); % add formula here

% default colors
blue = hex2rgb('#3c5488');red = hex2rgb('#a5474e'); white = [1,1,1];
Expand All @@ -653,7 +661,7 @@ function heatmap_settings(obj,varargin)
h.CellLabelColor = 'none';
% fix figure size
% Calculate approximate figure size
figureHeight = nchs * (h.FontSize) / 0.8;
figureHeight = obj.nchs * (h.FontSize) / 0.8;
figureWidth = figureHeight + 50;
h.Position = [100, 100, figureWidth, figureHeight];
% fix colormap
Expand Down Expand Up @@ -714,6 +722,7 @@ function reverse(obj)
% Reverse by one processing step.
obj.data = obj.rev_data;
obj.ch_names = obj.rev_chs;
obj.nchs = length(obj.ch_names);
obj.ref_chnames = obj.rev_refchs;
obj.history{end + 1} = 'reverse';
end
Expand Down
17 changes: 14 additions & 3 deletions matlab/iEEGPreprocess.m
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,15 @@ function load_data(obj, path, varargin)
>>> session.load_data("/path/to/saved_data.mat", replace = True)
%}
p = inputParser;
defaults = {false,true};
for i = 1:length(varargin)
if isempty(varargin{i})
varargin{i} = defaults{i};
end
end
addRequired(p, 'path', @(x) isstring(x) || ischar(x));
addOptional(p, 'replace',false, @(x) ismember(x,[0,1]));
addOptional(p, 'default_folder',true, @(x) ismember(x,[0,1]));
addOptional(p, 'replace',defaults{1}, @(x) ismember(x,[0,1]));
addOptional(p, 'default_folder',defaults{2}, @(x) ismember(x,[0,1]));
parse(p, path, varargin{:});
path = p.Results.path;
replace = p.Results.replace;
Expand Down Expand Up @@ -205,7 +211,11 @@ The stop time (in seconds) of the data segment to download.
username = p.Results.username;

if isempty(obj.user)
obj.login('username', username);
if ~isempty(username)
obj.login(username);
else
obj.login()
end
end
data = iEEGData(filename, start, stop, select_elecs, ignore_elecs);
data.download(obj.user);
Expand All @@ -224,6 +234,7 @@ function remove_data(obj, dataIndex)
% Remove data instance from current session.
obj.meta(dataIndex, :) = [];
obj.datasets(dataIndex) = [];
obj.num_data = size(obj.meta, 1);
end

function save(obj, filename, varargin)
Expand Down
39 changes: 39 additions & 0 deletions matlab/test/authTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
%% Test Class Definition
classdef authTest < matlab.unittest.TestCase
%% Test Method Block
methods (Test)
% includes unit test functions
function testAuth(testCase)
% see https://www.mathworks.com/help/matlab/matlab_prog/types-of-qualifications.html
% for qualification method
% addpath(genpath('./../..')); % always add to ensure loading of other files/func
paths;
assert(exist(USER_DIR,'dir') == 7);
if ~isempty(getenv('GITHUB_ACTIONS'))
% Use GitHub secrets to retrieve credentials
config.usr = getenv('IEEG_USERNAME');
config.pwd = getenv('IEEG_PASSWORD');
pwd_path = fullfile(USER_DIR, strcat(config.usr(1:3), '_ieeglogin.bin'));
PATH = IEEGSession.createPwdFile(config.usr, config.pwd, pwd_path);
config.pwd = strcat(config.usr(1:3), '_ieeglogin.bin');
file_name = fullfile(USER_DIR, strcat(config.usr(1:3), '_config.json'));
jsonStr = jsonencode(config);
fid = fopen(file_name, 'w');
fprintf(fid, '%s', jsonStr);
fclose(fid);
fprintf('-- -- IEEG user config file saved -- --\n');
user_data_dir = fullfile(DATA_DIR, config.usr(1:3));
end
files = dir(fullfile(USER_DIR,'*.json'));
if isempty(files)
error('Login info unavailable.')
else
for i = 1:length(files)
f = fullfile(files(i).folder, files(i).name);
login = jsondecode(fileread(f));
assert(exist(fullfile(USER_DIR,login.pwd)))
end
end
end
end
end
74 changes: 74 additions & 0 deletions matlab/test/bandPowerTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
classdef bandPowerTest < matlab.unittest.TestCase

methods (Test)
function testBandPowerAbsolute(tc)
addpath(genpath('./..'));
% Generate example data with known band power in the alpha range
fs = 250;
duration = 10; % seconds
time = [0:1/fs:duration-1/fs]';
alpha_band = [8, 12];
alpha_power = 0.5; % Known alpha band power

data = zeros(length(time), 1);
data = data + sin(2*pi*10*time) + 0.1 * randn(size(time)); % Alpha band (10 Hz)

% Call the function under test
bp = band_power(data, fs, alpha_band);

% Assertions
tc.verifyEqual(bp, alpha_power, 'Alpha band power mismatch', 'AbsTol', 0.1); % Allow some tolerance

% Generate example data
data = randn(100, 5);
fs = 250;
band = [1, 30];

% Call the function under test
bp = band_power(data, fs, band);

% Assertions
tc.verifyEqual(size(bp), [1, size(data, 2)], 'Band power size mismatch');
tc.verifyGreaterThan(bp, 0, 'Band power should be greater than zero');
end

function testBandPowerRelative(tc)
addpath(genpath('./..'));
% Generate example data with known band power in the alpha range
fs = 250;
duration = 10; % seconds
time = [0:1/fs:duration-1/fs]';

% Simulate data with power in two frequency bands
alpha_band = [8, 12];
beta_band = [15, 30];
alpha_power = 0.6; % Known power ratio
beta_power = 0.4; % Known power ratio

data_alpha = sin(2*pi*10*time); % Alpha band (10 Hz)
data_beta = sin(2*pi*20*time); % Beta band (20 Hz)

data = alpha_power * data_alpha + beta_power * data_beta + 0.1 *randn(size(time));
% Call the function under test with relative power
bp = band_power(data, fs, alpha_band, [], true);

% Assertions
tc.verifyEqual(bp, alpha_power, 'Relative alpha band power mismatch', 'AbsTol', 0.1); % Allow some tolerance


% Generate example data
data = randn(100, 5);
fs = 250;
band = [1, 30];

% Call the function under test with relative power
bp = band_power(data, fs, band, [], true);

% Assertions
tc.verifyEqual(size(bp), [1, size(data, 2)], 'Relative band power size mismatch');
tc.verifyGreaterThanOrEqual(bp, 0, 'Relative band power should be greater than or equal to zero');
tc.verifyLessThanOrEqual(bp, 1, 'Relative band power should be less than or equal to one');
end
end

end
17 changes: 17 additions & 0 deletions matlab/test/bandpassFilterTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
classdef bandpassFilterTest < matlab.unittest.TestCase

methods (Test)
function test_BandpassFilter(tc)
% Test with default parameters

addpath(genpath('./..')); % always add to ensure loading of other files/func
paths;
load(fullfile(TESTDATA_DIR,'sampleData.mat'));
values = bandpass_filter(old_values, fs);
tc.verifyEqual(size(values), size(old_values), 'Output size mismatch');
values = bandpass_filter(old_values, fs, 5, 50, 6);
tc.verifyEqual(size(values), size(old_values), 'Output size mismatch');
end
end

end
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
%% Test Class Definition
classdef refBipolarTest < matlab.unittest.TestCase
classdef bipolarTest < matlab.unittest.TestCase
% This is a test class defined for the decompose_labels function
% Import test cases from decompLabel_testInput.csv file, which include
% input, expected output, and notes describes the test scenairo of the
Expand All @@ -9,14 +9,19 @@
%% Test Method Block
methods (Test)
% includes unit test functions
function testrefBipolar(testCase)
function testBipolar(testCase)
% This part test for wrong input types
% see https://www.mathworks.com/help/matlab/matlab_prog/types-of-qualifications.html
% for qualification method
addpath(genpath('./../..')); % always add to ensure loading of other files/func
load refBipolar_testInput.mat;
addpath(genpath('./..')); % always add to ensure loading of other files/func
paths;
load(fullfile(TESTDATA_DIR,'reref_testInput.mat'));
f = @() bipolar(old_values,labels);
testCase.verifyWarningFree(f)
[out_values,out_labels] = bipolar(old_values,labels);
n = length(find(strcmp('-',out_labels)));
testCase.verifyEqual(n,15);

end
end
end
Loading