Skip to content

Commit 40fe56d

Browse files
committed
Better temp directories
Adds a `.1`, `.2`, etc uniquifier to the end of the test run directory name so that if two test runs are executed within the same second there won't be a clash. The unique directory for the entire test run is now stored in the runner. Previously it was possible to end up with one test's temp files in `_test/cgay-20250926-101601` and the next test's temp files in `_test/cgay-20250926-101602`. Fixes #149 Fixes #199
1 parent ad26898 commit 40fe56d

5 files changed

Lines changed: 70 additions & 20 deletions

File tree

command-line.dylan

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,10 @@ define function run-or-list-tests
292292
report-function(result, *standard-output*);
293293
end;
294294
if (*output-captured?*)
295-
// TODO: be more specific about where the files are after fixing various problems
296-
// with test-temp-directory, i.e., store one _test subdir per runner.
297-
test-output("NOTE: Some test output was captured. See files named %s "
298-
"in the _test directory.\n",
299-
$captured-output-filename);
295+
test-output("NOTE: Some test output was captured. See %s\n",
296+
file-locator(runner-temp-directory(runner),
297+
"*",
298+
$captured-output-filename));
300299
end;
301300
if (result.result-status == $passed) 0 else 1 end
302301
end

documentation/source/reference.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ Test Execution
801801
temporary files created by the test or benchmark. The directory is created
802802
the first time this function is called for each test or benchmark and is not
803803
deleted after the test run is complete in case it's useful for post-mortem
804-
analysis. The directory is named ``_test/<user>-<timestamp>/<test-name>``
804+
analysis. The directory is named ``_test/<user>-<timestamp>.1/<test-name>``
805805
and is rooted at ``$DYLAN``, if defined, or in the current directory
806806
otherwise.
807807

documentation/source/usage.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,36 @@ creating a test and provide a method that returns :drm:`#f` on Windows:
289289
Tests that aren't run because of the `when:` option are marked as ``SKIPPED`` in the
290290
results.
291291

292+
Standard Output / Error
293+
~~~~~~~~~~~~~~~~~~~~~~~
294+
295+
Any output to :var:`*standard-output*` or :var:`*standard-error*` during a test is
296+
automatically captured and stored in a file named :file:`_captured-stdout.txt` in the
297+
:func:`test's temporary directory <test-temp-directory>` so that it doesn't clutter the
298+
console output but also can be examined upon test failure.
299+
300+
Temporary Files
301+
~~~~~~~~~~~~~~~
302+
303+
If you need to create temporary files for a test, you can of course use the system temp
304+
directory, but after multiple test runs it may not be easy to figure out which is the
305+
correct file to look at when debugging your test. Instead, use
306+
:func:`test-temp-directory` and its companion function :func:`write-test-file` to create
307+
the files in a directory created specifically for the test.
308+
309+
Example:
310+
311+
.. code:: dylan
312+
313+
define test test-something ()
314+
let file = file-locator(test-temp-directory(), "something.txt");
315+
write-test-file(file, contents: "hi there");
316+
...
317+
end test;
318+
319+
:func:`file-locator` is exported from the ``locators`` module of the ``system`` library.
320+
321+
292322
Benchmarks
293323
----------
294324

run.dylan

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ define open class <test-runner> (<object>)
8585
// to tests. For example, test data directory pathname.
8686
constant slot runner-options :: <string-table> = make(<string-table>),
8787
init-keyword: options:;
88+
89+
// A place to put temp files created by tests or the runner itself, one subdirectory
90+
// per test.
91+
constant slot runner-temp-directory :: <locator> = default-runner-temp-directory();
8892
end class <test-runner>;
8993

9094

utils.dylan

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -120,27 +120,44 @@ define function next-indent () => (indent :: <string>)
120120
concatenate(*indent*, $indent-step)
121121
end function;
122122

123-
// Return a temporary directory unique to the current test or benchmark. The
124-
// directory is created the first time this is called for a given test.
125-
// The directory is _test/<user>-<yyyymmdd-hhmmss>/<full-test-name>/, relative
126-
// to ${DYLAN}/, if defined, or relative to fs/working-directory() otherwise.
123+
define function default-runner-temp-directory () => (dir :: <directory-locator>)
124+
let dylan = os/environment-variable("DYLAN");
125+
let base = if (dylan)
126+
as(<directory-locator>, dylan)
127+
else
128+
fs/working-directory()
129+
end;
130+
let dated-dir
131+
= concatenate(os/login-name() | "unknown",
132+
"-",
133+
date/format("%Y%m%d-%H%M%S", date/now()));
134+
// We could just include milliseconds (%F) in the date format below but currently
135+
// <date> milliseconds are always zero, at least on Unix.
136+
// https://github.com/dylan-lang/testworks/issues/199
137+
iterate loop (i = 1)
138+
let uniquifier = format-to-string("%s.%d", dated-dir, i);
139+
let dir = subdirectory-locator(base, "_test", uniquifier);
140+
if (block ()
141+
fs/file-exists?(dir)
142+
exception (ex :: fs/<file-system-error>)
143+
#f
144+
end)
145+
loop(i + 1)
146+
else
147+
dir
148+
end
149+
end
150+
end function;
151+
152+
// Return a temporary directory unique to the current test or benchmark.
127153
define function test-temp-directory () => (d :: false-or(<directory-locator>))
128154
if (instance?(*component*, <runnable>))
129-
let dylan = os/environment-variable("DYLAN");
130-
let base = if (dylan)
131-
as(<directory-locator>, dylan)
132-
else
133-
fs/working-directory()
134-
end;
135-
let uniquifier
136-
= format-to-string("%s-%s", os/login-name() | "unknown",
137-
date/format("%Y%m%d-%H%M%S", date/now()));
138155
let safe-name = map(method (c)
139156
if (c == '\\' | c == '/') '_' else c end
140157
end,
141158
component-name(*component*));
142159
let test-directory
143-
= subdirectory-locator(base, "_test", uniquifier, safe-name);
160+
= subdirectory-locator(runner-temp-directory(*runner*), safe-name);
144161
fs/ensure-directories-exist(test-directory);
145162
test-directory
146163
end

0 commit comments

Comments
 (0)