diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c1c2836cb52a01660d408b2d92ee3285286e605d..4617cd49ecc815bf5e0e2d2b735469108685d5a8 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 60109e20d8e3defa2982ac1cf6b61371a06ff208..a2d49f9aeb1bc0cb02fd66afcd583510b29c4ed3 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 12531bae37968a7741d88c14605682d89a88e01d..c95ead0fed4d0a4689472fcbecb2e48a8e8d42dd 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 94efddf9605a9390e71e395b1e1dc90ae730b755..ce5ca305c31eff4fa59b586351b5324628ccb5d3 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 bb2c70482d07a15d624b4bc35ad30963ceff0d2a..6bf99c004afa72665de11f87623fb085f51d4f95 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 e2f6e514eadc5bc1876f386d2fca588b591ccc79..9d5f622b3d3276f0cc212460d3f1987606aa5e5e 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 eb8bf7de783e3c98e7f084ecf603250e8d436492..b8eb76a1e3ca563dfc6cc145d0d19e9ae954bcdc 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 b8e07f3c4a089466e317b1816262fdd131cef1f1..0195d0217409e9d2aee01b0ddc24a076b67ab459 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 82014424c195fb8f5eec1fc4a88b5172a982a39b..0cc04561ad911dc836f8a36fcf28dfa80d177046 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 0a4c5ec00545853b6de63b6a87d296a1fe206fd0..d346a1ba00d0ca928a071600490ef72df8f08978 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 c352784c12a7b0d7a1f0f42829adbc663f9007d5..d1b28e70913d11a87ecea50c14e59058354aba9e 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 5299732d9b50f17b44c8b96977faf869c727821c..55fd054345c5561e94b4276aca709abc0593e98e 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 1aede5d8c79b4d01f98a8c7618cd168d823befdc..bc34749dc052aa700cdfeece4ec1cddd2aefa773 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 349a0b27e539116d84a676d337ee9a9c47c261ee..6c8bf22c058745c0489eb13fbe7301ef06934ff3 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 d1fea8851da023c3f7dda287f2c2b240605b6e99..18f3eef8a5390edb03b6a96ecc74f9bc728d960b 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