Skip to content

Commit e6e0e4a

Browse files
Kumar Pallavkrisctl
Kumar Pallav
authored andcommitted
Addresses gaps in unit test coverage for MATLAB files.
1 parent 075bcd9 commit e6e0e4a

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed

.github/workflows/run-unit-tests.yml

+27
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,38 @@ jobs:
2121
- name: Checkout
2222
uses: actions/checkout@v4
2323

24+
- name: Set up virtual display on Ubuntu
25+
if: runner.os == 'Linux'
26+
run: |
27+
sudo apt-get update
28+
sudo apt-get install -y xvfb
29+
# Function to check if a display port is free
30+
is_display_free() {
31+
xvfb-run --auto-servernum --server-num=$1 true 2>/dev/null
32+
}
33+
34+
# Find the first free display port
35+
DISPLAY_PORT=""
36+
for num in $(seq 99 -1 0); do
37+
if is_display_free $num; then
38+
DISPLAY_PORT=$num
39+
break
40+
fi
41+
done
42+
# Start Xvfb with the free port
43+
Xvfb :$DISPLAY_PORT -ac &
44+
# Set the DISPLAY environment variable
45+
echo "DISPLAY=:$DISPLAY_PORT" >> $GITHUB_ENV
46+
# Print the chosen display port for debugging
47+
echo "Using display port :$DISPLAY_PORT"
48+
2449
- name: Set up MATLAB ${{ matrix.matlab-release }}
2550
# Use MATLAB Actions to get running MATLAB in GitHub Actions
2651
uses: matlab-actions/setup-matlab@v2
2752
with:
2853
release: ${{ matrix.matlab-release }}
54+
products: >
55+
Symbolic_Math_Toolbox
2956
3057
- name: Run tests
3158
uses: matlab-actions/run-tests@v2
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
% Copyright 2024 The MathWorks, Inc.
2+
classdef TestExecuteFunction < matlab.unittest.TestCase
3+
% TestExecuteFunction contains unit tests for the execute function
4+
properties
5+
TestPaths
6+
end
7+
8+
methods (TestClassSetup)
9+
function addFunctionPath(testCase)
10+
testCase.TestPaths = cellfun(@(relative_path)(fullfile(pwd, relative_path)), {"../../src/jupyter_matlab_kernel/matlab"}, 'UniformOutput', false);
11+
cellfun(@addpath, testCase.TestPaths)
12+
end
13+
function suppressWarnings(testCase)
14+
warning('off', 'all');
15+
testCase.addTeardown(@() warning('on', 'all'));
16+
end
17+
end
18+
19+
methods (TestClassTeardown)
20+
function removeFunctionPath(testCase)
21+
cellfun(@rmpath, testCase.TestPaths)
22+
end
23+
end
24+
methods (Test)
25+
function testMatrixOutput(testCase)
26+
% Test execution of a code that generates a matrix output
27+
code = 'repmat([1 2 3 4],5,1)';
28+
kernelId = 'test_kernel_id';
29+
result = jupyter.execute(code, kernelId);
30+
testCase.verifyEqual(result{1}.type, 'execute_result', 'Expected execute_result type');
31+
testCase.verifyTrue(any(strcmp(result{1}.mimetype{1}, 'text/html')), 'Expected HTML output');
32+
testCase.verifyTrue(any(strcmp(result{1}.mimetype{2}, 'text/plain')), 'Expected HTML output');
33+
testCase.verifySubstring(result{1}.value{1}, 'ans = 5');
34+
end
35+
36+
function testVariableOutput(testCase)
37+
% Test execution of a code that generates a variable output
38+
code = 'var x';
39+
kernelId = 'test_kernel_id';
40+
result = jupyter.execute(code, kernelId);
41+
testCase.verifyEqual(result{1}.type, 'execute_result', 'Expected execute_result type');
42+
testCase.verifyTrue(any(strcmp(result{1}.mimetype{1}, 'text/html')), 'Expected HTML output');
43+
testCase.verifyTrue(any(strcmp(result{1}.mimetype{2}, 'text/plain')), 'Expected HTML output');
44+
testCase.verifySubstring(result{1}.value{1}, 'ans = 0');
45+
end
46+
47+
%Skipping the following test as it fails in public github run
48+
% function testSymbolicOutput(testCase)
49+
% %Test execution of a code that generates a symbolic output
50+
% code = 'x = sym(1/3); disp(x);';
51+
% kernelId = 'test_kernel_id';
52+
% result = jupyter.execute(code, kernelId);
53+
% testCase.verifyEqual(result{1}.type, 'execute_result', 'Expected execute_result type');
54+
% testCase.verifyTrue(any(strcmp(result{1}.mimetype{1}, ["text/latex", "text/html"])), 'Expected LaTeX or HTML output');
55+
% end
56+
57+
function testErrorOutput(testCase)
58+
% Test execution of a code that generates an error
59+
code = 'error(''Test error'');';
60+
kernelId = 'test_kernel_id';
61+
result = jupyter.execute(code, kernelId);
62+
testCase.verifyEqual(result{1}.type, 'stream', 'Expected stream type');
63+
testCase.verifyEqual(result{1}.content.name, 'stderr', 'Expected stderr stream');
64+
testCase.verifyTrue(contains(result{1}.content.text, 'Test error'), 'Expected error message');
65+
end
66+
67+
function testFigureOutput(testCase)
68+
% Test execution of a code that generates a figure output
69+
code = 'figure; plot(1:10); title(''Test Figure'');';
70+
kernelId = 'test_kernel_id';
71+
result = jupyter.execute(code, kernelId);
72+
testCase.verifyEqual(result{1}.type, 'execute_result', 'Expected execute_result type');
73+
testCase.verifyTrue(any(strcmp(result{1}.mimetype, 'image/png')), 'Expected PNG image output');
74+
testCase.verifyTrue(~isempty(result{1}.value{1}));
75+
end
76+
end
77+
end
+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
% Copyright 2024 The MathWorks, Inc.
2+
classdef TestIdlerFunction < matlab.unittest.TestCase
3+
% Unit tests for the Idler class
4+
properties
5+
TestPaths
6+
end
7+
8+
methods (TestClassSetup)
9+
function addFunctionPath(testCase)
10+
testCase.TestPaths = cellfun(@(relative_path)(fullfile(pwd, relative_path)), {"../../src/jupyter_matlab_kernel/matlab"}, 'UniformOutput', false);
11+
cellfun(@addpath, testCase.TestPaths)
12+
end
13+
end
14+
15+
methods (TestClassTeardown)
16+
function removeFunctionPath(testCase)
17+
cellfun(@rmpath, testCase.TestPaths)
18+
end
19+
end
20+
21+
properties
22+
Idler
23+
end
24+
25+
methods(TestMethodSetup)
26+
function createIdler(testCase)
27+
testCase.Idler = jupyter.Idler;
28+
end
29+
end
30+
31+
methods(Test)
32+
function testIdlerInitialization(testCase)
33+
%Initially the idler should not be timed out and its status should be false
34+
testCase.verifyFalse(testCase.Idler.TimedOut);
35+
testCase.verifyFalse(testCase.Idler.Status);
36+
end
37+
38+
function testStartIdlingTimeout(testCase)
39+
%Verify that the startIdling function times out after the 1second
40+
maxIdleTime = 1; % 1second
41+
tic;
42+
status = testCase.Idler.startIdling(maxIdleTime);
43+
elapsedTime = toc;
44+
testCase.verifyFalse(status);
45+
testCase.verifyGreaterThanOrEqual(elapsedTime, maxIdleTime);
46+
testCase.verifyTrue(testCase.Idler.TimedOut);
47+
testCase.verifyTrue(testCase.Idler.Status);
48+
end
49+
50+
function testStopIdling(testCase)
51+
%Verify that the stopIdling function stops the idling process before timeout
52+
maxIdleTime = 5;
53+
% Timer function triggering stopIdling after a delay of 0.5s to
54+
% stop the startIdling function
55+
timerObj = timer('StartDelay', 0.5, 'TimerFcn', @(~,~)testCase.Idler.stopIdling());
56+
start(timerObj);
57+
status = testCase.Idler.startIdling(maxIdleTime);
58+
stop(timerObj);
59+
delete(timerObj);
60+
testCase.verifyTrue(status);
61+
testCase.verifyTrue(testCase.Idler.Status);
62+
testCase.verifyFalse(testCase.Idler.TimedOut);
63+
end
64+
end
65+
end

0 commit comments

Comments
 (0)