Skip to content

Commit 3127a9e

Browse files
HanSur94claude
andcommitted
fix: run each Octave test in a subprocess to prevent crash from killing suite
Octave 8.x crashes during handle-class cleanup (break_closure_cycles), which was killing the process after ~28 tests, leaving 35 tests unrun. Now each test runs in an isolated subprocess with a success marker so the suite continues even if individual tests crash during cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 42400e3 commit 3127a9e

1 file changed

Lines changed: 47 additions & 8 deletions

File tree

tests/run_all_tests.m

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,28 +72,66 @@
7272

7373
function results = run_octave_tests(test_dir)
7474
%RUN_OCTAVE_TESTS Run function-based tests for Octave compatibility.
75-
add_fastsense_private_path();
75+
% Each test runs in a separate Octave subprocess to survive the known
76+
% Octave 8.x crash during handle-class cleanup (break_closure_cycles).
7677
files = dir(fullfile(test_dir, 'test_*.m'));
78+
repo_root = fileparts(test_dir);
7779

7880
total = 0;
7981
passed = 0;
8082
failed = 0;
8183
failures = {};
84+
marker = '__OCTAVE_TEST_PASSED__';
85+
newline_char = sprintf('\n');
86+
87+
fprintf('=== FastSense Test Suite (Octave – subprocess isolation) ===\n\n');
88+
fprintf('Discovered %d test files.\n\n', numel(files));
8289

8390
for i = 1:numel(files)
8491
[~, name, ~] = fileparts(files(i).name);
8592
fprintf('Running %s...\n', name);
86-
try
87-
feval(name);
93+
94+
% Run each test in an isolated subprocess so that an Octave
95+
% crash (e.g. break_closure_cycles) cannot kill the suite.
96+
eval_str = sprintf( ...
97+
'addpath(''%s''); install(); cd(''%s''); add_fastsense_private_path(); %s(); fprintf(''%s\\n'');', ...
98+
repo_root, test_dir, name, marker);
99+
cmd = sprintf( ...
100+
'octave --no-gui --no-init-file --quiet --eval "%s" 2>&1', ...
101+
eval_str);
102+
[status, output] = system(cmd);
103+
104+
% Parse output: look for success marker, strip noise
105+
lines = strsplit(output, newline_char);
106+
clean = {};
107+
test_ok = false;
108+
for j = 1:numel(lines)
109+
ln = lines{j};
110+
if strcmp(strtrim(ln), marker)
111+
test_ok = true;
112+
continue;
113+
end
114+
if isempty(strtrim(ln)); continue; end
115+
if strncmp(ln, 'octave:', 7); continue; end
116+
clean{end+1} = ln;
117+
end
118+
119+
for j = 1:numel(clean)
120+
fprintf(' %s\n', clean{j});
121+
end
122+
123+
if test_ok
88124
fprintf(' PASSED\n');
89125
passed = passed + 1;
90-
catch e
91-
fprintf(' FAILED: %s\n', e.message);
126+
else
127+
fprintf(' FAILED (exit code %d)\n', status);
92128
failed = failed + 1;
93-
failures{end+1} = sprintf('%s: %s', name, e.message);
129+
failures{end+1} = sprintf('%s: %s', name, ...
130+
strjoin(clean, ' | '));
94131
end
95132
total = total + 1;
96-
% Write results incrementally so they survive Octave crashes
133+
134+
% Write results incrementally so they survive crashes
97135
resultsFile = getenv('FASTSENSE_RESULTS_FILE');
98136
if ~isempty(resultsFile)
99137
fid = fopen(resultsFile, 'w');
@@ -102,7 +140,8 @@
102140
end
103141
end
104142

105-
fprintf('\n=== Results: %d/%d passed, %d failed ===\n', passed, total, failed);
143+
fprintf('\n=== Results: %d/%d passed, %d failed ===\n', ...
144+
passed, total, failed);
106145
if ~isempty(failures)
107146
fprintf('\nFailures:\n');
108147
for i = 1:numel(failures)

0 commit comments

Comments
 (0)