diff --git a/+eui/AlyxPanel.m b/+eui/AlyxPanel.m index 41241600..5c5c8242 100644 --- a/+eui/AlyxPanel.m +++ b/+eui/AlyxPanel.m @@ -411,12 +411,12 @@ function recordWeight(obj, weight, subject) ai = obj.AlyxInstance; % determine whether there is a session for this subj and date thisDate = ai.datestr(now); - sessions = ai.getData(['sessions?type=Base&subject=' obj.Subject]); + sessions = ai.getSessions('subject', obj.Subject, 'date', now); stat = -1; url = []; % If the date of this latest base session is not the same date % as today, then create a new one for today - if isempty(sessions) || ~strcmp(sessions(end).start_time(1:10), thisDate(1:10)) + if isempty(sessions) % Ask user whether he/she wants to create new session % Construct a questdlg with three options choice = questdlg('Would you like to create a new base session?', ... @@ -478,7 +478,7 @@ function recordWeight(obj, weight, subject) % See also LAUNCHSESSIONURL ai = obj.AlyxInstance; s = ai.getData(ai.makeEndpoint(['subjects/' obj.Subject])); - url = fullfile(ai.BaseURL, 'admin', 'subjects', 'subject', s.id, 'change'); % this is wrong - need uuid + url = sprintf('%s/admin/subjects/subject/%s/change', ai.BaseURL, s.id); stat = web(url, '-browser'); end diff --git a/alyx-matlab b/alyx-matlab index 85b5915a..8e93baa4 160000 --- a/alyx-matlab +++ b/alyx-matlab @@ -1 +1 @@ -Subproject commit 85b5915a15eb833fff9b304bf068a0766a1fe8b9 +Subproject commit 8e93baa4fb29c986985e6320e89d4a94997cc568 diff --git a/cortexlab/+git/changes.m b/cortexlab/+git/changes.m deleted file mode 100644 index d9e9f013..00000000 --- a/cortexlab/+git/changes.m +++ /dev/null @@ -1,6 +0,0 @@ -disp('Updating queued Alyx posts...') -posts = dirPlus(getOr(dat.paths, 'localAlyxQueue', 'C:/localAlyxQueue')); -posts = posts(endsWith(posts, 'put')); -newPosts = cellfun(@(str)[str(1:end-3) 'patch'], posts, 'uni', 0); -status = cellfun(@movefile, posts, newPosts); -assert(all(status), 'Unable to rename queued Alyx files, please do this manually') \ No newline at end of file diff --git a/tests/AlyxPanel_test.m b/tests/AlyxPanel_test.m index 5a4dbb6b..416465be 100644 --- a/tests/AlyxPanel_test.m +++ b/tests/AlyxPanel_test.m @@ -29,6 +29,12 @@ GraphData end + properties (ClassSetupParameter) + % Alyx base URL. test is for the main branch, testDev is for the dev + % code + BaseURL = cellsprintf('https://%s.alyx.internationalbrainlab.org', {'test', 'testDev'}); + end + methods (TestClassSetup) function killFigures(testCase) testCase.FigureVisibleDefault = get(0,'DefaultFigureVisible'); @@ -46,7 +52,7 @@ function loadData(testCase) testCase.GraphData = graphData; end - function setupPanel(testCase) + function setupPanel(testCase, BaseURL) % Check paths file assert(endsWith(which('dat.paths'), fullfile('fixtures','+dat','paths.m'))); % Check temp mainRepo folder is empty. An extra safe measure as we @@ -59,6 +65,13 @@ function setupPanel(testCase) localRepo = dat.reposPath('main','local'); if exist(localRepo, 'dir') == 0; mkdir(localRepo); end + % Create config directory + assert(mkdir(getOr(dat.paths,'rigConfig')), 'Failed to create config directory') + + % Set the database url + paths.databaseURL = BaseURL; + save(fullfile(getOr(dat.paths,'rigConfig'), 'paths'), 'paths') + clearCBToolsCache % Create figure for panel testCase.hPanel = figure('Name', 'alyx GUI',... 'MenuBar', 'none',... @@ -87,11 +100,13 @@ function setupPanel(testCase) % MControl using this as a panel. testCase.SubjectUI.addlistener('SelectionChanged', ... @(src, evt)testCase.Panel.dispWaterReq(src, evt)); - + % Set Alyx Instance and log in testCase.Panel.login('test_user', 'TapetesBloc18'); testCase.fatalAssertTrue(testCase.Panel.AlyxInstance.IsLoggedIn,... 'Failed to log into Alyx'); + testCase.fatalAssertEqual(testCase.Panel.AlyxInstance.BaseURL, BaseURL,... + 'Failed to correctly set database url'); % Verify subject folders created present = ismember([{'default'}; testCase.Subjects(1:end-1)], dat.listSubjects); @@ -117,12 +132,11 @@ function restoreFigures(testCase) idx = cellfun(@(n)any(strcmp(n, testCase.Subjects)),{figHandles.Name}); close(figHandles(idx)) end - % Remove subject directories + % Remove directories + repos = [{getOr(dat.paths,'localAlyxQueue', ['fixtures' filesep 'alyxQ'])};... + dat.reposPath('main'); {getOr(dat.paths, 'globalConfig')}]; rm = @(repo)assert(rmdir(repo, 's'), 'Failed to remove test repo %s', repo); - cellfun(@(repo)iff(exist(repo,'dir') == 7, @()rm(repo), @()nop), dat.reposPath('main')); - % Remove Alyx queue - alyxQ = getOr(dat.paths,'localAlyxQueue', ['fixtures' filesep 'alyxQ']); - assert(rmdir(alyxQ, 's'), 'Failed to remove test Alyx queue') + cellfun(@(repo)iff(exist(repo,'dir') == 7, @()rm(repo), @()nop), repos); end end @@ -214,8 +228,10 @@ function test_dispWaterReq(testCase) testCase.verifyTrue(~strcmp(prev, new), 'Failed to retrieve new data') end - function test_launchSessionURL(testCase) + function test_launchSessionURL(testCase, BaseURL) % Test the launch of the session page in the admin Web interface + % TODO Use DELETE to test both creating new session and viewing + % existing p = testCase.Panel; testCase.Mock.InTest = true; testCase.Mock.UseDefaults = false; @@ -226,16 +242,16 @@ function test_launchSessionURL(testCase) % Add mock user response key = 'Would you like to create a new base session?'; - testCase.Mock.Dialogs(key) = iff(isempty(todaySession), 'Yes', 'No'); + noSub = isempty(todaySession)||~isempty(todaySession.number); + testCase.Mock.Dialogs(key) = iff(noSub, 'Yes', 'No'); - [failed, url] = testCase.assertWarningFree(@()p.launchSessionURL); - testCase.verifyTrue(~failed, 'Failed to launch subject page in browser') + [status, url] = testCase.assertWarningFree(@()p.launchSessionURL); + testCase.verifyTrue(status > -1, 'Failed to launch subject page in browser') if isempty(todaySession) expected = url; else uuid = todaySession.url(find(todaySession.url=='/', 1, 'last')+1:end); - expected = ['https://test.alyx.internationalbrainlab.org/admin/', ... - 'actions/session/', uuid, '/change']; + expected = [BaseURL '/admin/actions/session/', uuid, '/change']; end testCase.verifyEqual(url, expected, 'Unexpected url') @@ -243,15 +259,15 @@ function test_launchSessionURL(testCase) % todo: close tab after opening? (for `test_launchSubjectURL` as well) end - function test_launchSubjectURL(testCase) + function test_launchSubjectURL(testCase, BaseURL) % Test the launch of the subject page in the admin Web interface p = testCase.Panel; % Set new subject testCase.SubjectUI.Selected = testCase.SubjectUI.Option{2}; [failed, url] = p.launchSubjectURL; testCase.verifyTrue(~failed, 'Failed to launch subject page in browser') - expected = ['https:\\test.alyx.internationalbrainlab.org\admin\'... - 'subjects\subject\bcefd268-68c2-4ea8-9b60-588ee4e99ebb\change']; + expected = [BaseURL '/admin/subjects/subject/'... + 'bcefd268-68c2-4ea8-9b60-588ee4e99ebb/change']; testCase.verifyEqual(url, expected, 'unexpected subject page url') end @@ -274,7 +290,7 @@ function test_recordWeight(testCase) 'Failed to update weight label color') % Post weight < 80 - weight = 28 + rand; + weight = 16 + rand; testCase.Panel.recordWeight(weight) expected = sprintf('Weight today: %.2f (< 80%%)', weight); testCase.verifyTrue(startsWith(strip(weight_text.String(2,:)), expected),... @@ -481,41 +497,27 @@ function test_activeFlag(testCase) button = findobj(testCase.Parent, 'String', str); testCase.assertEqual(button.Enable, 'on', 'AlyxPanel not enabled') - % Comment out the databaseURL field in the paths file - fid = fopen(which('dat.paths')); - data = cellflat(textscan(fid, '%s', 'Delimiter', '\n', 'CollectOutput', true)); - fclose(fid); - - data{startsWith(data,'p.databaseURL')} = ['%' data{startsWith(data,'p.databaseURL')}]; - - fid = fopen(which('dat.paths'), 'w'); - cellfun(@(ln)fprintf(fid, '%s\n', ln), data); - fclose(fid); + % Set invalid database URL + baseURL = testCase.Panel.AlyxInstance.BaseURL; + paths.databaseURL = ''; + save(fullfile(getOr(dat.paths,'rigConfig'), 'paths'), 'paths') + testCase.assertEmpty(getOr(dat.paths,'databaseURL'),... + 'Failed to create custom paths file') testCase.Figure = figure('Name', testCase.Subjects{end}); eui.AlyxPanel(testCase.Figure); - testCase.assertEmpty(getOr(dat.paths, 'databaseURL'), ... + % Reset URL + paths.databaseURL = baseURL; + save(fullfile(getOr(dat.paths,'rigConfig'), 'paths'), 'paths') + clearCBToolsCache % Ensure paths are reloaded + testCase.fatalAssertEqual(getOr(dat.paths, 'databaseURL'), baseURL, ... 'Failed to remove databaseURL field in paths') button = findobj(testCase.Figure, 'String', 'Login'); testCase.verifyEqual(button.Enable, 'off', ... 'AlyxPanel enabled while databaseURL undefined') close(testCase.Figure) - % Restore paths - fid = fopen(which('dat.paths')); - data = cellflat(textscan(fid, '%s', 'Delimiter', '\n', 'CollectOutput', true)); - fclose(fid); - - idx = startsWith(data,'%p.databaseURL'); - if any(idx) - data{idx}(1) = []; - fid = fopen(which('dat.paths'), 'w'); - cellfun(@(ln)fprintf(fid, '%s\n', ln), data); - fclose(fid); - testCase.fatalAssertTrue(~isempty(getOr(dat.paths, 'databaseURL')), ... - 'Failed to restore databaseURL field in paths') - end end function test_round(testCase) diff --git a/tests/dat_test.m b/tests/dat_test.m index 796788a0..f3050093 100644 --- a/tests/dat_test.m +++ b/tests/dat_test.m @@ -112,6 +112,7 @@ function test_paths(testCase) paths.main2Repository = [p.mainRepository '2']; paths.altRepository = [p.mainRepository '3']; save(fullfile(p.rigConfig, 'paths'), 'paths') + clearCBToolsCache p = dat.paths('testRig'); testCase.verifyTrue(ismember('novelRepo', fieldnames(p)), ... @@ -312,6 +313,7 @@ function altMain2Paths(testCase) 'Secondary main repo already in path, expected otherwise') paths.main2Repository = [dat.reposPath('main','m') '2']; save(fullfile(getOr(dat.paths,'rigConfig'), 'paths'), 'paths') + clearCBToolsCache % Ensure paths are reloaded testCase.assertEqual(paths.main2Repository, getOr(dat.paths,'main2Repository'),... 'Failed to create custom paths file') end diff --git a/tests/fixtures/data/viewSubjectData.mat b/tests/fixtures/data/viewSubjectData.mat index bf68d0d2..0f356031 100644 Binary files a/tests/fixtures/data/viewSubjectData.mat and b/tests/fixtures/data/viewSubjectData.mat differ diff --git a/tests/runall.m b/tests/runall.m index d95cc4d3..a0fad759 100644 --- a/tests/runall.m +++ b/tests/runall.m @@ -3,7 +3,6 @@ % TODO May become a function % TODO May add flags for levels of testing % TODO Method setup in dat_test may become global fixture -% TODO Delete sinusoidLayer_test from this folder % TODO Deal with directory changes main_tests = testsuite; diff --git a/tests/sinusoidLayer_test.m b/tests/sinusoidLayer_test.m deleted file mode 100644 index 41fe9001..00000000 --- a/tests/sinusoidLayer_test.m +++ /dev/null @@ -1,49 +0,0 @@ -%% Test 1: vis.grating default values -azimuth = 0; spatialFreq = 1/15; phase = 0; orientation = 0; -[layer, image] = vis.sinusoidLayer(azimuth, spatialFreq, phase, orientation); -TestAns = [layer.texOffset(1), layer.texAngle, layer.size(1)]; -ExpectedAns = [0 0 15]; -assert(isequal(TestAns,ExpectedAns), 'Test 1 failed.'); - -%% Test 2: Negative Azimuth -azimuth = -90; spatialFreq = 1/15; phase = 0; orientation = 0; -[layer, image] = vis.sinusoidLayer(azimuth, spatialFreq, phase, orientation); -TestAns = [layer.texOffset(1), layer.texAngle, layer.size(1)]; -ExpectedAns = [-90 0 15]; -assert(isequal(TestAns,ExpectedAns), 'Test 2 failed.'); - -%% Test 3: High Spatial Frequency -azimuth = 0; spatialFreq = 2; phase = 0; orientation = 0; -[layer, image] = vis.sinusoidLayer(azimuth, spatialFreq, phase, orientation); -TestAns = round([layer.texOffset(1), layer.texAngle, layer.size(1)],4); -ExpectedAns = [0 0 0.5000]; -assert(isequal(TestAns,ExpectedAns), 'Test 3 failed.'); - - -%% Test 4: Negative Phase -azimuth = 0; spatialFreq = 1/15; phase = -90; orientation = 0; -[layer, image] = vis.sinusoidLayer(azimuth, spatialFreq, phase, orientation); -TestAns = round([layer.texOffset(1), layer.texAngle, layer.size(1)],4); -ExpectedAns = [10.1408 0 15.0000]; -assert(isequal(TestAns,ExpectedAns), 'Test 4 failed.'); - -%% Test 5: Negative Orientation -azimuth = 0; spatialFreq = 1/15; phase = 0; orientation = -90; -[layer, image] = vis.sinusoidLayer(azimuth, spatialFreq, phase, orientation); -TestAns = [layer.texOffset(1), layer.texAngle, layer.size(1)]; -ExpectedAns = [0 -90 15]; -assert(isequal(TestAns,ExpectedAns), 'Test 5 failed.'); - -%% Test 6: Non-zero values for all input args -azimuth = 45; spatialFreq = 7/15; phase = 30; orientation = 60; -[layer, image] = vis.sinusoidLayer(azimuth, spatialFreq, phase, orientation); -TestAns = round([layer.texOffset(1), layer.texAngle, layer.size(1)],4); -ExpectedAns = [24.1600 60.0000 2.1429]; -assert(isequal(TestAns,ExpectedAns), 'Test 6 failed.'); - -%% Test 7: Impossible Spatial Frequency -azimuth = 45; spatialFreq = -1/15; phase = 30; orientation = 60; -[layer, image] = vis.sinusoidLayer(azimuth, spatialFreq, phase, orientation); -TestAns = round([layer.texOffset(1), layer.texAngle, layer.size(1)],4); -ExpectedAns = [10.8803 60.0000 -15.0000]; -assert(isequal(TestAns,ExpectedAns), 'Test 7 failed.'); \ No newline at end of file