From e549bbba7a96f822b8f8475041b4263467d2b609 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Wed, 20 Sep 2023 13:07:30 +0200
Subject: [PATCH] Meson build system: testsuite now fully implemented

By the way, switch the CI to use the Meson testsuite, because some test files
have been modified and no longer work with the old build system.
---
 .gitlab-ci.yml                          | 64 ++++++++++++------------
 meson.build                             | 65 ++++++++++++++++++++++++-
 scripts/test-driver                     | 10 +++-
 tests/contribs.m                        | 48 ++----------------
 tests/cyclereduction.m                  | 55 ++-------------------
 tests/histval_initval_file_unit_tests.m | 42 +++-------------
 tests/logarithmicreduction.m            | 55 ++-------------------
 tests/nonlinearsolvers.m                | 51 +++----------------
 tests/riccatiupdate.m                   | 51 ++-----------------
 tests/run_all_unit_tests.m              | 46 +++++------------
 tests/run_block_byte_tests_matlab.m     | 39 +++++----------
 tests/run_block_byte_tests_octave.m     | 39 +++++----------
 tests/run_kronecker_tests.m             | 28 +++--------
 tests/run_reporting_test_matlab.m       | 31 +++---------
 tests/run_reporting_test_octave.m       | 29 ++---------
 15 files changed, 191 insertions(+), 462 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c1c2836cb5..4617cd49ec 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -25,17 +25,24 @@ stages:
   - sign
   - deploy
 
-build_binaries:
+build_matlab:
   stage: build
   script:
-    - autoreconf -si
-    - ./configure --with-matlab=/opt/MATLAB/$MATLAB_VERSION PACKAGE_VERSION=$VERSION PACKAGE_STRING="dynare $VERSION"
-    - make -j $(nproc) LN_S="cp -p"
+    - meson setup -Dbuild_for=matlab -Dmatlab_path=/opt/MATLAB/$MATLAB_VERSION -Dbuildtype=release build-matlab
+    - meson compile -v -C build-matlab
   artifacts:
     paths:
-      - preprocessor/dynare-preprocessor
-      - mex/octave/
-      - mex/matlab/
+      - build-matlab/
+    expire_in: 3 days
+
+build_octave:
+  stage: build
+  script:
+    - meson setup -Dbuild_for=octave -Dbuildtype=release build-octave
+    - meson compile -v -C build-octave
+  artifacts:
+    paths:
+      - build-octave/
     expire_in: 3 days
 
 build_doc:
@@ -109,32 +116,26 @@ pkg_macOS:
     expire_in: 3 days
   needs: [ "build_doc" ]
 
-.test_matlab_template:
+test_matlab:
   stage: test
+  script:
+    - meson test -C build-matlab --no-rebuild --num-processes $(($(nproc) * 3 / 4))
   artifacts:
     paths:
-      - tests/**/*.m.log
-      - tests/**/*.m.trs
-      - tests/**/*.jnl
-      - tests/run_test_matlab_output.txt
+      - build-matlab/meson-logs/testlog.txt
     when: always
-  needs: [ "build_binaries" ]
-
-test_matlab:
-  extends: .test_matlab_template
-  script:
-    - autoreconf -si
-    - ./configure --disable-octave --with-matlab=/opt/MATLAB/$MATLAB_VERSION
-    - make -j $(($(nproc) * 3 / 4)) -C tests check-matlab
+  needs: [ "build_matlab" ]
 
 test_old_matlab:
-  extends: .test_matlab_template
+  stage: test
   script:
-    - autoreconf -si
-    - ./configure --disable-octave --with-matlab=/opt/MATLAB/$OLD_MATLAB_VERSION
-    - make -C mex/build/matlab clean
-    - make -j $(nproc) -C mex/build/matlab
-    - make -j $(($(nproc) * 3 / 4)) -C tests check-matlab
+    - meson setup -Dbuild_for=matlab -Dmatlab_path=/opt/MATLAB/$OLD_MATLAB_VERSION -Dbuildtype=release build-old-matlab
+    - meson compile -v -C build-old-matlab
+    - meson test -C build-old-matlab --num-processes $(($(nproc) * 3 / 4))
+  artifacts:
+    paths:
+      - build-old-matlab/meson-logs/testlog.txt
+    when: always
   when: manual
 
 test_octave:
@@ -142,17 +143,12 @@ test_octave:
   variables:
     OPENBLAS_NUM_THREADS: 1
   script:
-    - autoreconf -si
-    - ./configure --disable-matlab
-    - make -j $(nproc) -C tests check-octave
+    - meson test -C build-octave --no-rebuild
   artifacts:
     paths:
-      - tests/**/*.o.log
-      - tests/**/*.o.trs
-      - tests/**/*.jnl
-      - tests/run_test_octave_output.txt
+      - build-octave/meson-logs/testlog.txt
     when: always
-  needs: [ "build_binaries" ]
+  needs: [ "build_octave" ]
   when: manual
 
 # For the sign and deploy jobs, we don’t use the “needs” keyword, since we
diff --git a/meson.build b/meson.build
index 60109e20d8..a2d49f9aeb 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,4 @@
 # TODO:
-# - Add tests that are individually listed in {M,O}_TRS_FILES of tests/Makefile.am
 # - Install files in right location
 # - configuration option to disable preprocessor build, and use it in {windows,macOS}/build.sh
 # - add -Wold-style-cast C++ flag except when building flex-generated files
@@ -1677,9 +1676,71 @@ mod_and_m_tests = [
   { 'test' : [ 'var-expectations/1-with-time-shift-b/example1.mod' ],
     'should_fail' : true },
   { 'test' : [ 'estimation/univariate/nls/staticmodelx.mod' ],
-    'should_fail' : true }
+    'should_fail' : true },
+
+  # Misc tests with .m files
+  { 'test' : [ 'run_all_unit_tests.m' ] },
+  { 'test' : [ 'histval_initval_file_unit_tests.m' ],
+    'extra' : [ 'histval_initval_file/my_assert.m' ] },
+  { 'test' : [ 'run_kronecker_tests.m' ],
+    'extra' : [ 'kronecker/test_kron.m',
+                'kronecker/nash_matrices.mat' ] },
+  { 'test' : [ 'nonlinearsolvers.m' ],
+    'extra' : [ 'solver-test-functions/brown.m',
+                'solver-test-functions/broydenbanded.m',
+                'solver-test-functions/broydentridiagonal.m',
+                'solver-test-functions/chebyquad.m',
+                'solver-test-functions/discreteboundaryvalue.m',
+                'solver-test-functions/discreteintegralequation.m',
+                'solver-test-functions/helicalvalley.m',
+                'solver-test-functions/powell1.m',
+                'solver-test-functions/powell2.m',
+                'solver-test-functions/rosenbrock.m',
+                'solver-test-functions/trigonometric.m',
+                'solver-test-functions/variablydimensioned.m',
+                'solver-test-functions/watson.m',
+                'solver-test-functions/wood.m',] },
+  { 'test' : [ 'cyclereduction.m' ] },
+  { 'test' : [ 'logarithmicreduction.m' ] },
+  { 'test' : [ 'riccatiupdate.m' ] },
+  { 'test' : [ 'contribs.m' ],
+    'extra' : [ 'sandbox.mod',
+                'simulateddata.m' ] }
 ]
 
+if get_option('build_for') == 'matlab'
+  mod_and_m_tests += [ { 'test' : [ 'run_block_byte_tests_matlab.m' ],
+                         'extra' : [ 'block_bytecode/run_ls2003.m',
+                                     'block_bytecode/ls2003.mod' ] },
+                       { 'test' : [ 'run_reporting_test_matlab.m' ],
+                         'extra' : [ 'reporting/AnnualTable.m',
+                                     'reporting/CommResidTablePage.m',
+                                     'reporting/CountryGraphPage.m',
+                                     'reporting/CountryTablePage.m',
+                                     'reporting/db_a.csv',
+                                     'reporting/db_q.csv',
+                                     'reporting/dc_a.csv',
+                                     'reporting/dc_q.csv',
+                                     'reporting/ResidTablePage.m',
+                                     'reporting/runDynareReport.m' ] } ]
+else
+  mod_and_m_tests += [ { 'test' : [ 'run_block_byte_tests_octave.m' ],
+                         'extra' : [ 'block_bytecode/run_ls2003.m',
+                                     'block_bytecode/ls2003.mod' ] },
+                       { 'test' : [ 'run_reporting_test_octave.m' ],
+                         'extra' : [ 'reporting/AnnualTable.m',
+                                     'reporting/CommResidTablePage.m',
+                                     'reporting/CountryGraphPage.m',
+                                     'reporting/CountryTablePage.m',
+                                     'reporting/db_a.csv',
+                                     'reporting/db_q.csv',
+                                     'reporting/dc_a.csv',
+                                     'reporting/dc_q.csv',
+                                     'reporting/ResidTablePage.m',
+                                     'reporting/runDynareReport.m' ] } ]
+endif
+
+
 base_test_driver_args = [ get_option('build_for') ]
 if get_option('build_for') == 'matlab'
   base_test_driver_args += [ matlab_exe.full_path(), matlab_version, matlab_arch ]
diff --git a/scripts/test-driver b/scripts/test-driver
index 12531bae37..c95ead0fed 100755
--- a/scripts/test-driver
+++ b/scripts/test-driver
@@ -3,6 +3,7 @@
 # Executes one or several .mod or .m files in a separate directory, for the testsuite
 
 set -e
+shopt -s extglob
 
 if (($# < 7 )); then
     echo "Usage: $0 build_for matlab_octave_exe matlab_octave_version matlab_arch source_root build_root test_file(s) [-- extra_file(s)]" 2>&1
@@ -40,7 +41,11 @@ cleanup ()
 trap cleanup EXIT
 
 for f in "${test_files[@]}" "${extra_files[@]}"; do
-    subdir=${f%/*}
+    # NB: for computing the subdir, we do not use ${f%/*}, because the latter does not
+    # work with files which are not in a subdir (i.e. no slash in the filename).
+    # We rather use pattern substitution (and we use an extended pattern of the
+    # form *(pattern-list), hence the extglob option above).
+    subdir=${f/%*([^\/])/}
     mkdir -p "${tmpdir}/${subdir}"
     cp "${source_root}/tests/${f}" "${tmpdir}/${f}"
 done
@@ -69,7 +74,8 @@ export source_root
 
 for test_file in "${test_files[@]}"; do
     test_basename=${test_file##*/}
-    test_subdir=${test_file%/*}
+    # See the remark above for computing the subdir
+    test_subdir=${test_file/%*([^\/])/}
     if [[ $test_file =~ \.mod$ ]]; then
         export mod_file=$test_basename
         if [[ $build_for == matlab ]]; then
diff --git a/tests/contribs.m b/tests/contribs.m
index 94efddf960..ce5ca305c3 100644
--- a/tests/contribs.m
+++ b/tests/contribs.m
@@ -1,28 +1,15 @@
 debug = false;
 
-if debug
-    [top_test_dir, ~, ~] = fileparts(mfilename('fullpath'));
-else
-    top_test_dir = getenv('TOP_TEST_DIR');
-end
-
-addpath(sprintf('%s/matlab', top_test_dir(1:end-6)))
-
-if ~debug
-    % Test Dynare Version
-    if ~strcmp(dynare_version(), getenv('DYNARE_VERSION'))
-        error('Incorrect version of Dynare is being tested')
-    end
-end
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'matlab']);
 
 dynare_config;
 
-NumberOfTests = 0;
 testFailed = 0;
 
 if ~debug
     skipline()
-    disp('***  TESTING: nonlinearsolvers.m ***');
+    disp('***  TESTING: contribs.m ***');
 end
 
 %
@@ -31,8 +18,6 @@ end
 
 t0 = clock;
 
-NumberOfTests = NumberOfTests+1;
-
 try
     dataset = dseries('simulateddata.m');
 
@@ -51,32 +36,9 @@ end
 
 t1 = clock;
 
+fprintf('\n*** Elapsed time (in seconds): %.1f\n\n', etime(t1, t0));
 
-if ~debug
-    cd(getenv('TOP_TEST_DIR'));
-else
-    dprintf('FAILED tests: %i', testFailed)
-end
-
-if  isoctave
-    fid = fopen('contribs.o.trs', 'w+');
-else
-    fid = fopen('contribs.m.trs', 'w+');
-end
-if testFailed
-    fprintf(fid,':test-result: FAIL\n');
-    fprintf(fid,':list-of-failed-tests: nonlinearsolvers.m\n');
-else
-    fprintf(fid,':test-result: PASS\n');
-end
-fprintf(fid,':number-tests: %i\n', NumberOfTests);
-fprintf(fid,':number-failed-tests: %i\n', testFailed);
-fprintf(fid,':elapsed-time: %f\n', etime(t1, t0));
-fclose(fid);
-
-if ~debug
-    exit;
-end
+quit(testFailed > 0)
 %
 % END OF TEST
 %
diff --git a/tests/cyclereduction.m b/tests/cyclereduction.m
index bb2c70482d..6bf99c004a 100644
--- a/tests/cyclereduction.m
+++ b/tests/cyclereduction.m
@@ -1,23 +1,10 @@
 debug = false;
 
-if debug
-    [top_test_dir, ~, ~] = fileparts(mfilename('fullpath'));
-else
-    top_test_dir = getenv('TOP_TEST_DIR');
-end
-
-addpath([top_test_dir filesep '..' filesep 'matlab']);
-
-if ~debug
-    % Test Dynare Version
-    if ~strcmp(dynare_version(), getenv('DYNARE_VERSION'))
-        error('Incorrect version of Dynare is being tested')
-    end
-end
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'matlab']);
 
 dynare_config;
 
-NumberOfTests = 0;
 testFailed = 0;
 
 if ~debug
@@ -25,17 +12,11 @@ if ~debug
     disp('***  TESTING: cyclereduction.m ***');
 end
 
-matlab_cr_path = [top_test_dir filesep '..' filesep 'matlab' filesep 'missing' filesep 'mex' filesep 'cycle_reduction'];
+matlab_cr_path = [source_dir filesep 'matlab' filesep 'missing' filesep 'mex' filesep 'cycle_reduction'];
 addpath(matlab_cr_path);
 cycle_reduction_matlab = @cycle_reduction;
 rmpath(matlab_cr_path);
 
-if isoctave
-   addpath([top_test_dir filesep '..' filesep 'mex' filesep 'octave']);
-else
-   addpath([top_test_dir filesep '..' filesep 'mex' filesep 'matlab']);
-end
-
 t0 = clock;
 
 % Set the dimension of the problem to be solved.
@@ -49,7 +30,6 @@ X1 = zeros(n,n);
 X2 = zeros(n,n);
 
 % 1. Solve the equation with the Matlab cycle reduction algorithm
-NumberOfTests = NumberOfTests+1;
 tElapsed1 = 0.;
 try
    tic; [X1,info] = cycle_reduction_matlab(C,B,A,cvg_tol,[0.]); tElapsed1 = toc;
@@ -69,7 +49,6 @@ catch
 end
 
 % 2. Solve the equation with the Fortran cycle reduction algorithm
-NumberOfTests = NumberOfTests+1;
 tElapsed2 = 0.;
 try
    tic; [X2,info] = cycle_reduction(C,B,A,cvg_tol,[0.]); tElapsed2 = toc;
@@ -89,7 +68,6 @@ catch
 end
 
 % 3. Compare solutions of the Fortran and Matlab routines
-NumberOfTests = NumberOfTests+1;
 if (norm(X1 - X2, 1) > cvg_tol)
    testFailed = testFailed+1;
    if debug
@@ -112,29 +90,6 @@ end
 
 t1 = clock;
 
-if ~debug
-    cd(getenv('TOP_TEST_DIR'));
-else
-    dprintf('FAILED tests: %i', testFailed)
-end
-
-if  isoctave
-    fid = fopen('cyclereduction.o.trs', 'w+');
-else
-    fid = fopen('cyclereduction.m.trs', 'w+');
-end
-if testFailed
-    fprintf(fid,':test-result: FAIL\n');
-    fprintf(fid,':list-of-failed-tests: cyclereduction.m\n');
-else
-    fprintf(fid,':test-result: PASS\n');
-end
-fprintf(fid,':number-tests: %i\n', NumberOfTests);
-fprintf(fid,':number-failed-tests: %i\n', testFailed);
-fprintf(fid,':elapsed-time: %f\n', etime(t1, t0));
-fclose(fid);
-
-if ~debug
-    exit;
-end
+fprintf('\n*** Elapsed time (in seconds): %.1f\n\n', etime(t1, t0));
 
+quit(testFailed > 0)
diff --git a/tests/histval_initval_file_unit_tests.m b/tests/histval_initval_file_unit_tests.m
index e2f6e514ea..9d5f622b3d 100644
--- a/tests/histval_initval_file_unit_tests.m
+++ b/tests/histval_initval_file_unit_tests.m
@@ -1,9 +1,9 @@
-top_test_dir = getenv('TOP_TEST_DIR');
-addpath([top_test_dir filesep '..' filesep 'matlab/']);
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'matlab']);
+
 dynare_config;
 
-cd('histval_initval_file');
-num_tests = 0;
+cd histval_initval_file
 failed_tests = {};
 
 ds = dseries(randn(10,4));
@@ -26,7 +26,6 @@ options.series = 'ds';
 ds1 = histvalf_initvalf(caller, M, options);
 
 failed_tests = my_assert(failed_tests, all(all(ds1 == ds)), 'basic test');
-num_tests = num_tests + 1;
 
 options = struct();
 options.series = 'ds1';
@@ -34,7 +33,6 @@ options.first_obs = 2;
 ds1 = histvalf_initvalf(caller, M, options);
 failed_tests = my_assert(failed_tests, ds1.init == dates('2Y'), ...
                          'init test 1');
-num_tests = num_tests + 1;
 
 options = struct();
 options.series = 'ds';
@@ -45,7 +43,6 @@ failed_tests = my_assert(failed_tests, ds1.init == dates('2Y'), ...
                          'first_obs last_obs test 1');
 failed_tests = my_assert(failed_tests, ds1.last == dates('9Y'), ...
                          'first_obs last_obs test 2');
-num_tests = num_tests + 2;
 
 options = struct();
 options.series = 'ds';
@@ -55,7 +52,6 @@ failed_tests = my_assert(failed_tests, ds1.init == dates('1Y'), ...
                          'last_obs test 1');
 failed_tests = my_assert(failed_tests, ds1.last == dates('9Y'), ...
                          'last_obs test 2');
-num_tests = num_tests + 2;
 
 options = struct();
 options.series = 'ds';
@@ -67,7 +63,6 @@ failed_tests = my_assert(failed_tests, ds1.init == dates('2Y'), ...
                          'first_obs, last_obs, nobs test 1');
 failed_tests = my_assert(failed_tests, ds1.last == dates('9Y'), ...
                          'first_obs, last_obs, nobs test 2');
-num_tests = num_tests + 2;
 
 options = struct();
 options.series = 'ds';
@@ -78,7 +73,6 @@ failed_tests = my_assert(failed_tests, ds1.init == dates('2Y'), ...
                          'last_obs, nobs test 1');
 failed_tests = my_assert(failed_tests, ds1.last == dates('9Y'), ...
                          'last_obs, nobs test 2');
-num_tests = num_tests + 2;
 
 options = struct();
 options.series = 'ds';
@@ -96,7 +90,6 @@ catch me
         failed_tests = cat(1, failed_tests, 'Wrong nobs error message' );
     end
 end
-num_tests = num_tests + 1;
 
 options = struct();
 options.series = 'ds';
@@ -111,7 +104,6 @@ catch me
                            'Wrong first period error message');
     end
 end
-num_tests = num_tests + 1;
 
 options = struct();
 options.series = 'ds';
@@ -127,7 +119,6 @@ catch me
                            'Wrong last period error message');
     end
 end
-num_tests = num_tests + 1;
 
 fh = fopen('data.m', 'w');
 init__ = 'INIT__ = ''1Y'';';
@@ -195,7 +186,6 @@ if ~isoctave && ((ispc && ~matlab_ver_less_than('8.2')) || (~ispc && ~matlab_ver
                              '*.xlsx file first_obs test');
     failed_tests = my_assert(failed_tests, series.nobs == 10, ...
                              '*.xlsx file nobs test');
-    num_tests = num_tests + 2;
 end
 
 % The table() function is not implemented in Octave
@@ -209,26 +199,10 @@ if ~isoctave && (ispc && ~matlab_ver_less_than('8.2'))
                              '*.xls file first_obs test');
     failed_tests = my_assert(failed_tests, series.nobs == 10, ...
                              '*.xls file nobs test');
-    num_tests = num_tests + 2;
 end
 
-cd(getenv('TOP_TEST_DIR'));
-if isoctave
-    ext = '.o.trs';
-else
-    ext = '.m.trs';
+if length(failed_tests) > 0
+    fprintf('\n*** Failed tests: %s\n', failed_tests{:})
 end
-fid = fopen([ 'histval_initval_file_unit_tests' ext ], 'w+');
-num_failed_tests = length(failed_tests);
-if num_failed_tests > 0
-  fprintf(fid,':test-result: FAIL\n');
-  fprintf(fid,':number-tests: %d\n', num_tests);
-  fprintf(fid,':number-failed-tests: %d\n', num_failed_tests);
-  fprintf(fid,':list-of-failed-tests: %s\n', failed_tests{:});
-else
-  fprintf(fid,':test-result: PASS\n');
-  fprintf(fid,':number-tests: %d\n', num_tests);
-  fprintf(fid,':number-failed-tests: 0\n');
-end
-fclose(fid);
-exit;
+
+quit(length(failed_tests) > 0)
diff --git a/tests/logarithmicreduction.m b/tests/logarithmicreduction.m
index eb8bf7de78..b8eb76a1e3 100644
--- a/tests/logarithmicreduction.m
+++ b/tests/logarithmicreduction.m
@@ -1,23 +1,10 @@
 debug = false;
 
-if debug
-    [top_test_dir, ~, ~] = fileparts(mfilename('fullpath'));
-else
-    top_test_dir = getenv('TOP_TEST_DIR');
-end
-
-addpath([top_test_dir filesep '..' filesep 'matlab']);
-
-if ~debug
-    % Test Dynare Version
-    if ~strcmp(dynare_version(), getenv('DYNARE_VERSION'))
-        error('Incorrect version of Dynare is being tested')
-    end
-end
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'matlab']);
 
 dynare_config;
 
-NumberOfTests = 0;
 testFailed = 0;
 
 if ~debug
@@ -25,17 +12,11 @@ if ~debug
     disp('***  TESTING: logarithmicreduction.m ***');
 end
 
-matlab_cr_path = [top_test_dir filesep '..' filesep 'matlab' filesep 'missing' filesep 'mex' filesep 'logarithmic_reduction'];
+matlab_cr_path = [source_dir filesep 'matlab' filesep 'missing' filesep 'mex' filesep 'logarithmic_reduction'];
 addpath(matlab_cr_path);
 logarithmic_reduction_matlab = @logarithmic_reduction;
 rmpath(matlab_cr_path);
 
-if isoctave
-   addpath([top_test_dir filesep '..' filesep 'mex' filesep 'octave']);
-else
-   addpath([top_test_dir filesep '..' filesep 'mex' filesep 'matlab']);
-end
-
 t0 = clock;
 
 % Set the dimension of the problem to be solved.
@@ -49,7 +30,6 @@ X1 = zeros(n,n);
 X2 = zeros(n,n);
 
 % 1. Solve the equation with the Matlab logarithmic reduction algorithm
-NumberOfTests = NumberOfTests+1;
 tElapsed1 = 0.;
 try
    tic; [X1,info] = logarithmic_reduction_matlab(A,B,C,cvg_tol,300,[0.]); tElapsed1 = toc;
@@ -69,7 +49,6 @@ catch
 end
 
 % 2. Solve the equation with the Fortran logarithmic reduction algorithm
-NumberOfTests = NumberOfTests+1;
 tElapsed2 = 0.;
 try
    tic; [X2,info] = logarithmic_reduction(A,B,C,cvg_tol,300,[0.]); tElapsed2 = toc;
@@ -89,7 +68,6 @@ catch
 end
 
 % 3. Compare solutions of the Fortran and Matlab routines
-NumberOfTests = NumberOfTests+1;
 if (norm(X1 - X2, 1) > cvg_tol)
    testFailed = testFailed+1;
    if debug
@@ -112,29 +90,6 @@ end
 
 t1 = clock;
 
-if ~debug
-    cd(getenv('TOP_TEST_DIR'));
-else
-    dprintf('FAILED tests: %i', testFailed)
-end
-
-if  isoctave
-    fid = fopen('logarithmicreduction.o.trs', 'w+');
-else
-    fid = fopen('logarithmicreduction.m.trs', 'w+');
-end
-if testFailed
-    fprintf(fid,':test-result: FAIL\n');
-    fprintf(fid,':list-of-failed-tests: logarithmicreduction.m\n');
-else
-    fprintf(fid,':test-result: PASS\n');
-end
-fprintf(fid,':number-tests: %i\n', NumberOfTests);
-fprintf(fid,':number-failed-tests: %i\n', testFailed);
-fprintf(fid,':elapsed-time: %f\n', etime(t1, t0));
-fclose(fid);
-
-if ~debug
-    exit;
-end
+fprintf('\n*** Elapsed time (in seconds): %.1f\n\n', etime(t1, t0));
 
+quit(testFailed > 0)
diff --git a/tests/nonlinearsolvers.m b/tests/nonlinearsolvers.m
index b8e07f3c4a..0195d02174 100644
--- a/tests/nonlinearsolvers.m
+++ b/tests/nonlinearsolvers.m
@@ -1,24 +1,12 @@
 debug = false;
 
-if debug
-    [top_test_dir, ~, ~] = fileparts(mfilename('fullpath'));
-else
-    top_test_dir = getenv('TOP_TEST_DIR');
-end
-
-addpath(sprintf('%s/matlab', top_test_dir(1:end-6)))
-addpath(sprintf('%s/tests/solver-test-functions', top_test_dir(1:end-6)))
-
-if ~debug
-    % Test Dynare Version
-    if ~strcmp(dynare_version(), getenv('DYNARE_VERSION'))
-        error('Incorrect version of Dynare is being tested')
-    end
-end
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'matlab']);
 
 dynare_config;
 
-NumberOfTests = 0;
+cd solver-test-functions
+
 testFailed = 0;
 
 if ~debug
@@ -56,7 +44,6 @@ objfun = { @rosenbrock,
 t0 = clock;
 
 for i=1:length(objfun)
-    NumberOfTests = NumberOfTests+1;
     switch func2str(objfun{i})
          case 'helicalvalley'
            % FIXME block_trust_region is diverging if x(1)<0.
@@ -110,7 +97,6 @@ t1 = clock; etime(t1, t0)
 %
 
 for i=1:length(objfun)
-    NumberOfTests = NumberOfTests+1;
     switch func2str(objfun{i})
       case 'chebyquad'
         % Fails with a system of 10 equations. 
@@ -151,31 +137,8 @@ for i=1:length(objfun)
     end
 end
 
-t2 = clock; etime(t2, t1)
+t2 = clock;
 
-if ~debug
-    cd(getenv('TOP_TEST_DIR'));
-else
-    dprintf('FAILED tests: %i', testFailed)
-end
-
-if  isoctave
-    fid = fopen('nonlinearsolvers.o.trs', 'w+');
-else
-    fid = fopen('nonlinearsolvers.m.trs', 'w+');
-end
-if testFailed
-    fprintf(fid,':test-result: FAIL\n');
-    fprintf(fid,':list-of-failed-tests: nonlinearsolvers.m\n');
-else
-    fprintf(fid,':test-result: PASS\n');
-end
-fprintf(fid,':number-tests: %i\n', NumberOfTests);
-fprintf(fid,':number-failed-tests: %i\n', testFailed);
-fprintf(fid,':elapsed-time: %f\n', etime(t2, t0));
-fclose(fid);
-
-if ~debug
-    exit;
-end
+fprintf('\n*** Elapsed time (in seconds): %.1f\n\n', etime(t2, t0));
 
+quit(testFailed > 0)
diff --git a/tests/riccatiupdate.m b/tests/riccatiupdate.m
index 82014424c1..0cc04561ad 100644
--- a/tests/riccatiupdate.m
+++ b/tests/riccatiupdate.m
@@ -1,23 +1,10 @@
 debug = true;
 
-if debug
-    [top_test_dir, ~, ~] = fileparts(mfilename('fullpath'));
-else
-    top_test_dir = getenv('TOP_TEST_DIR');
-end
-
-addpath([top_test_dir filesep '..' filesep 'matlab']);
-
-if ~debug
-    % Test Dynare Version
-    if ~strcmp(dynare_version(), getenv('DYNARE_VERSION'))
-        error('Incorrect version of Dynare is being tested')
-    end
-end
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'matlab']);
 
 dynare_config;
 
-NumberOfTests = 0;
 testFailed = 0;
 
 if ~debug
@@ -25,12 +12,6 @@ if ~debug
     disp('***  TESTING: riccatiupdate.m ***');
 end
 
-if isoctave
-   addpath([top_test_dir filesep '..' filesep 'mex' filesep 'octave']);
-else
-   addpath([top_test_dir filesep '..' filesep 'mex' filesep 'matlab']);
-end
-
 t0 = clock;
 
 % Set the number of experiments for time measurement
@@ -64,7 +45,6 @@ tElapsed1 = toc;
 disp(['Elapsed time for the Matlab Riccati update is: ' num2str(tElapsed1) ' (N=' int2str(N) ').'])
 
 % 2. Update the state varance-covariance matrix with the mex routine
-NumberOfTests = NumberOfTests+1;
 tElapsed2 = 0.;
 Ptmp_fortran = P;
 try
@@ -112,29 +92,6 @@ end
 
 t1 = clock;
 
-if ~debug
-    cd(getenv('TOP_TEST_DIR'));
-else
-    dprintf('FAILED tests: %i', testFailed)
-end
-
-if  isoctave
-    fid = fopen('riccatiupdate.o.trs', 'w+');
-else
-    fid = fopen('riccatiupdate.m.trs', 'w+');
-end
-if testFailed
-    fprintf(fid,':test-result: FAIL\n');
-    fprintf(fid,':list-of-failed-tests: riccatiupdate.m\n');
-else
-    fprintf(fid,':test-result: PASS\n');
-end
-fprintf(fid,':number-tests: %i\n', NumberOfTests);
-fprintf(fid,':number-failed-tests: %i\n', testFailed);
-fprintf(fid,':elapsed-time: %f\n', etime(t1, t0));
-fclose(fid);
-
-if ~debug
-    exit;
-end
+fprintf('\n*** Elapsed time (in seconds): %.1f\n\n', etime(t1, t0));
 
+quit(testFailed > 0)
diff --git a/tests/run_all_unit_tests.m b/tests/run_all_unit_tests.m
index 0a4c5ec005..d346a1ba00 100644
--- a/tests/run_all_unit_tests.m
+++ b/tests/run_all_unit_tests.m
@@ -15,28 +15,25 @@
 % You should have received a copy of the GNU General Public License
 % along with Dynare.  If not, see <https://www.gnu.org/licenses/>.
 
-top_test_dir = getenv('TOP_TEST_DIR');
-addpath([top_test_dir filesep 'utils']);
-addpath([top_test_dir filesep '..' filesep 'matlab']);
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'tests' filesep 'utils']);
+matlab_dir = [source_dir filesep 'matlab'];
+addpath(matlab_dir);
+
 dynare_config();
 
 if isoctave
     load_octave_packages
 end
 
-% Test Dynare Version
-if ~strcmp(dynare_version(), getenv('DYNARE_VERSION'))
-    error('Incorrect version of Dynare is being tested')
-end
-
-mlist = get_directory_description('../matlab');
+mlist = get_directory_description(matlab_dir);
 
 % Under Octave, do not run tests under matlab/missing/stats/
 % Also skip load_m_data_file_legacy.m: it fails in the first test, but
 % this is impossible to reproduce outside the runners.
 if isoctave
-    mlist = mlist(find(~strncmp('../matlab/missing/stats/', mlist, 24)));
-    mlist = mlist(find(~strcmp('../matlab/load_m_file_data_legacy.m', mlist)));
+    mlist = mlist(find(~strncmp([matlab_dir filesep 'missing/stats/'], mlist, 24)));
+    mlist = mlist(find(~strcmp([matlab_dir filesep 'load_m_file_data_legacy.m'], mlist)));
 end
 
 % Set random seed, for reproducibility
@@ -49,14 +46,11 @@ end
 
 failedtests = {};
 
-counter = 0;
-
 for i = 1:length(mlist)
-    f = [top_test_dir filesep mlist{i} ];
+    f = mlist{i};
     if is_unit_test_available(f)
         [check, info] = mtest(f);
         for j = 1:size(info, 1)
-            counter = counter + 1;
             if ~info{j,3}
                 failedtests{length(failedtests)+1} = [ mlist{i} '#' num2str(info{j,2}) ];
             end
@@ -64,24 +58,8 @@ for i = 1:length(mlist)
     end
 end
 
-cd(getenv('TOP_TEST_DIR'));
-if isoctave
-    fid = fopen('run_all_unit_tests.o.trs', 'w+');
-else
-    fid = fopen('run_all_unit_tests.m.trs', 'w+');
-end
 if length(failedtests) > 0
-  fprintf(fid,':test-result: FAIL\n');
-  fprintf(fid,':number-tests: %d\n', counter);
-  fprintf(fid,':number-failed-tests: %d\n', length(failedtests));
-  fprintf(fid,':list-of-failed-tests: %s\n', failedtests{:});
-else
-  fprintf(fid,':test-result: PASS\n');
-  fprintf(fid,':number-tests: %d\n', counter);
-  fprintf(fid,':number-failed-tests: 0\n');
-end
-fprintf(fid,':elapsed-time: %f\n',0.0);
-fclose(fid);
-if ~isoctave
-    exit
+    fprintf('\n*** Failed tests: %s\n', failedtests{:})
 end
+
+quit(length(failedtests) > 0)
diff --git a/tests/run_block_byte_tests_matlab.m b/tests/run_block_byte_tests_matlab.m
index c352784c12..d1b28e7091 100644
--- a/tests/run_block_byte_tests_matlab.m
+++ b/tests/run_block_byte_tests_matlab.m
@@ -1,4 +1,4 @@
-% Copyright © 2011-2022 Dynare Team
+% Copyright © 2011-2023 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -27,20 +27,14 @@
 % block, because otherwise the next 'load wsMat' within a 'catch' block will
 % overwrite the last exception.
 
-top_test_dir = getenv('TOP_TEST_DIR');
-addpath([top_test_dir filesep 'utils']);
-addpath([top_test_dir filesep '..' filesep 'matlab']);
-
-% Test Dynare Version
-if ~strcmp(dynare_version(), getenv('DYNARE_VERSION'))
-  error('Incorrect version of Dynare is being tested')
-end
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'tests' filesep 'utils']);
+addpath([source_dir filesep 'matlab']);
 
 % Test block_bytecode/ls2003.mod with various combinations of
 % block/bytecode/solve_algo/stack_solve_algo
 failedBlock = {};
-num_block_tests = 0;
-cd([top_test_dir filesep 'block_bytecode']);
+cd block_bytecode
 has_optimization_toolbox = user_has_matlab_license('optimization_toolbox');
 tic;
 for blockFlag = 0:1
@@ -62,7 +56,6 @@ for blockFlag = 0:1
         end
 
         for i = 1:length(solve_algos)
-            num_block_tests = num_block_tests + 1;
             if ~blockFlag && storageFlag == 0 && (i == 1)
                 % This is the reference simulation path against which all
                 % other simulations will be tested
@@ -108,7 +101,6 @@ for blockFlag = 0:1
             end
         end
         for i = 1:length(stack_solve_algos)
-            num_block_tests = num_block_tests + 1;
             try
                 old_path = path;
                 clear oo_ % Ensure that oo_.endo_simul won’t be overwritten when loading wsMat
@@ -135,20 +127,13 @@ for blockFlag = 0:1
         end
     end
 end
-ecput = toc;
+
 delete('wsMat.mat')
-cd(top_test_dir);
-fid = fopen('run_block_byte_tests_matlab.m.trs', 'w+');
+
 if size(failedBlock,2) > 0
-  fprintf(fid,':test-result: FAIL\n');
-  fprintf(fid,':number-tests: %d\n', num_block_tests);
-  fprintf(fid,':number-failed-tests: %d\n', size(failedBlock,2));
-  fprintf(fid,':list-of-failed-tests: %s\n', failedBlock{:});
-else
-  fprintf(fid,':test-result: PASS\n');
-  fprintf(fid,':number-tests: %d\n', num_block_tests);
-  fprintf(fid,':number-failed-tests: 0\n');
+    fprintf('\n*** Failed tests: %s\n', failedBlock{:})
 end
-fprintf(fid,':elapsed-time: %f\n', ecput);
-fclose(fid);
-exit;
+
+fprintf('\n*** Elapsed time (in seconds): %.1f\n\n', toc);
+
+quit(size(failedBlock,2) > 0)
diff --git a/tests/run_block_byte_tests_octave.m b/tests/run_block_byte_tests_octave.m
index 5299732d9b..55fd054345 100644
--- a/tests/run_block_byte_tests_octave.m
+++ b/tests/run_block_byte_tests_octave.m
@@ -1,4 +1,4 @@
-## Copyright © 2009-2022 Dynare Team
+## Copyright © 2009-2023 Dynare Team
 ##
 ## This file is part of Dynare.
 ##
@@ -24,20 +24,14 @@
 ## otherwise the newly created oo_ will be scratched upon loading,
 ## thus making the path comparison bogus.
 
-top_test_dir = getenv('TOP_TEST_DIR');
-addpath([top_test_dir filesep 'utils']);
-addpath([top_test_dir filesep '..' filesep 'matlab']);
-
-## Test Dynare Version
-if !strcmp(dynare_version(), getenv("DYNARE_VERSION"))
-    error("Incorrect version of Dynare is being tested")
-endif
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'tests' filesep 'utils']);
+addpath([source_dir filesep 'matlab']);
 
 ## Test block_bytecode/ls2003.mod with various combinations of
 ## block/bytecode/solve_algo/stack_solve_algo
 failedBlock = {};
-num_block_tests = 0;
-cd([top_test_dir filesep 'block_bytecode']);
+cd block_bytecode
 tic;
 for blockFlag = 0:1
     for storageFlag = 0:2 % 0=M-file, 1=use_dll, 2=bytecode
@@ -62,7 +56,6 @@ for blockFlag = 0:1
         endif
 
         for i = 1:length(solve_algos)
-            num_block_tests = num_block_tests + 1;
             if !blockFlag && storageFlag == 0 && (i == 1)
                 ## This is the reference simulation path against which all
                 ## other simulations will be tested
@@ -105,7 +98,6 @@ for blockFlag = 0:1
             endif
         endfor
         for i = 1:length(stack_solve_algos)
-            num_block_tests = num_block_tests + 1;
             try
                 old_path = path;
                 clear oo_ # Ensure that oo_.endo_simul won’t be overwritten when loading wsOct
@@ -130,22 +122,17 @@ for blockFlag = 0:1
         endfor
     endfor
 endfor
-ecput = toc;
+
 delete('wsOct');
-cd(top_test_dir);
-fid = fopen('run_block_byte_tests_octave.o.trs', 'w+');
+
 if size(failedBlock,2) > 0
-  fprintf(fid,':test-result: FAIL\n');
-  fprintf(fid,':number-tests: %d\n', num_block_tests);
-  fprintf(fid,':number-failed-tests: %d\n', size(failedBlock,2));
-  fprintf(fid,':list-of-failed-tests: %s\n', failedBlock{:});
-else
-  fprintf(fid,':test-result: PASS\n');
-  fprintf(fid,':number-tests: %d\n', num_block_tests);
-  fprintf(fid,':number-failed-tests: 0\n');
+    fprintf('\n*** Failed tests: %s\n', failedBlock{:})
 end
-fprintf(fid,':elapsed-time: %f\n', ecput);
-fclose(fid);
+
+fprintf('\n*** Elapsed time (in seconds): %.1f\n\n', toc);
+
+quit(size(failedBlock,2) > 0)
+
 ## Local variables:
 ## mode: Octave
 ## End:
diff --git a/tests/run_kronecker_tests.m b/tests/run_kronecker_tests.m
index 1aede5d8c7..bc34749dc0 100644
--- a/tests/run_kronecker_tests.m
+++ b/tests/run_kronecker_tests.m
@@ -1,4 +1,4 @@
-% Copyright © 2021 Dynare Team
+% Copyright © 2021-2023 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -15,8 +15,9 @@
 % You should have received a copy of the GNU General Public License
 % along with Dynare.  If not, see <https://www.gnu.org/licenses/>.
 
-top_test_dir = getenv('TOP_TEST_DIR');
-addpath([top_test_dir filesep '..' filesep 'matlab']);
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'matlab']);
+
 dynare_config
 
 cd kronecker
@@ -32,25 +33,12 @@ disp('')
 disp('**** Testing A_times_B_kronecker_C MEX...')
 info(3) = test_kron(3, num_procs);
 
-cd ..
-
-if isoctave
-    ext = '.o.trs';
-else
-    ext = '.m.trs';
-end
-fid = fopen([ 'run_kronecker_tests' ext ], 'w+');
 num_failed_tests = sum(~info);
 tests = { 'sparse1', 'sparse2', 'dense' };
 failed_tests = tests(find(~info));
+
 if num_failed_tests > 0
-  fprintf(fid,':test-result: FAIL\n');
-  fprintf(fid,':number-tests: 3\n');
-  fprintf(fid,':number-failed-tests: %d\n', num_failed_tests);
-  fprintf(fid,':list-of-failed-tests: %s\n', failed_tests{:});
-else
-  fprintf(fid,':test-result: PASS\n');
-  fprintf(fid,':number-tests: 3\n');
-  fprintf(fid,':number-failed-tests: 0\n');
+    fprintf('\n*** Failed tests: %s\n', failed_tests{:})
 end
-fclose(fid);
+
+quit(num_failed_tests > 0)
diff --git a/tests/run_reporting_test_matlab.m b/tests/run_reporting_test_matlab.m
index 349a0b27e5..6c8bf22c05 100644
--- a/tests/run_reporting_test_matlab.m
+++ b/tests/run_reporting_test_matlab.m
@@ -1,4 +1,4 @@
-% Copyright © 2013-2022 Dynare Team
+% Copyright © 2013-2023 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -15,21 +15,16 @@
 % You should have received a copy of the GNU General Public License
 % along with Dynare.  If not, see <https://www.gnu.org/licenses/>.
 
-top_test_dir = getenv('TOP_TEST_DIR');
-addpath([top_test_dir filesep '..' filesep 'matlab']);
-
-% Test Dynare Version
-if ~strcmp(dynare_version(), getenv('DYNARE_VERSION'))
-  error('Incorrect version of Dynare is being tested')
-end
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'matlab']);
 
 % To add default directories, empty dseries objects
-dynare_config
+dynare_config;
 
 disp('');
 disp(['***  TESTING: run_reporting_test_matlab.m ***']);
 try
-    cd([top_test_dir filesep 'reporting']);
+    cd reporting
     db_a = dseries('db_a.csv');
     db_q = dseries('db_q.csv');
     dc_a = dseries('dc_a.csv');
@@ -40,18 +35,4 @@ catch
     testFailed = true;
 end
 
-cd(getenv('TOP_TEST_DIR'));
-fid = fopen('run_reporting_test_matlab.m.trs', 'w+');
-if testFailed
-  fprintf(fid,':test-result: FAIL\n');
-  fprintf(fid,':number-tests: 1\n');
-  fprintf(fid,':number-failed-tests: 1\n');
-  fprintf(fid,':list-of-failed-tests: run_reporting_test_matlab.m\n');
-else
-  fprintf(fid,':test-result: PASS\n');
-  fprintf(fid,':number-tests: 1\n');
-  fprintf(fid,':number-failed-tests: 0\n');
-end
-fprintf(fid,':elapsed-time: %f\n',0.0);
-fclose(fid);
-exit;
+quit(testFailed)
diff --git a/tests/run_reporting_test_octave.m b/tests/run_reporting_test_octave.m
index d1fea8851d..18f3eef8a5 100644
--- a/tests/run_reporting_test_octave.m
+++ b/tests/run_reporting_test_octave.m
@@ -15,23 +15,18 @@
 ## You should have received a copy of the GNU General Public License
 ## along with Dynare.  If not, see <https://www.gnu.org/licenses/>.
 
-top_test_dir = getenv('TOP_TEST_DIR');
-addpath([top_test_dir filesep 'utils']);
-addpath([top_test_dir filesep '..' filesep 'matlab']);
+source_dir = getenv('source_root');
+addpath([source_dir filesep 'tests' filesep 'utils']);
+addpath([source_dir filesep 'matlab']);
 
 load_octave_packages
 
-## Test Dynare Version
-if !strcmp(dynare_version(), getenv("DYNARE_VERSION"))
-    error("Incorrect version of Dynare is being tested")
-endif
-
 ## To add default directories, empty dseries objects
 dynare_config();
 
 printf("\n***  TESTING:  run_reporting_test_octave.m ***\n");
 try
-    cd([top_test_dir filesep 'reporting']);
+    cd reporting
     db_a = dseries('db_a.csv');
     db_q = dseries('db_q.csv');
     dc_a = dseries('dc_a.csv');
@@ -42,21 +37,7 @@ catch
     testFailed = true;
 end
 
-cd(getenv('TOP_TEST_DIR'));
-fid = fopen('run_reporting_test_octave.o.trs', 'w+');
-if testFailed
-  fprintf(fid,':test-result: FAIL\n');
-  fprintf(fid,':number-tests: 1\n');
-  fprintf(fid,':number-failed-tests: 1\n');
-  fprintf(fid,':list-of-failed-tests: run_reporting_test_octave.m\n');
-else
-  fprintf(fid,':test-result: PASS\n');
-  fprintf(fid,':number-tests: 1\n');
-  fprintf(fid,':number-failed-tests: 0\n');
-  fprintf(fid,':list-of-passed-tests: run_reporting_test_octave.m\n');
-end
-fprintf(fid,':elapsed-time: %f\n',0.0);
-fclose(fid);
+quit(testFailed)
 
 ## Local variables:
 ## mode: Octave
-- 
GitLab