Skip to content

Commit 562cb37

Browse files
authored
Merge pull request #205 from cgay/dev
Fix a few bugs
2 parents 1d1808c + bd3d8e4 commit 562cb37

7 files changed

Lines changed: 105 additions & 21 deletions

File tree

assertions.dylan

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,8 +521,10 @@ define function do-check-condition
521521
phase := format-to-string("checking if expression %s signals a condition of class %s",
522522
expr, condition-class);
523523
block ()
524-
thunk();
525-
let reason = "no condition signaled";
524+
let (#rest v) = thunk();
525+
let reason
526+
= concatenate("no condition signaled; return values: ",
527+
join(v, ", ", key: curry(format-to-string, "%s")));
526528
record-check(description, $failed, reason);
527529
terminate? & assertion-failure(concatenate(caller, ": ", reason));
528530
exception (ex :: condition-class)

command-line.dylan

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,12 @@ define function run-or-list-tests
291291
else
292292
report-function(result, *standard-output*);
293293
end;
294+
if (*output-captured?*)
295+
test-output("NOTE: Some test output was captured. See %s\n",
296+
file-locator(runner-temp-directory(runner),
297+
"*",
298+
$captured-output-filename));
299+
end;
294300
if (result.result-status == $passed) 0 else 1 end
295301
end
296302
end function;

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: 14 additions & 1 deletion
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

@@ -250,14 +254,23 @@ define method execute-component
250254
add!(subresults, result);
251255
result
252256
end;
257+
let ostream = make(<string-stream>, direction: #"output");
253258
let (status, reason)
254259
= dynamic-bind (*check-recording-function* = record-result,
255260
*benchmark-recording-function* = record-result,
256-
*indent* = next-indent())
261+
*indent* = next-indent(),
262+
// This (intentionally) doesn't affect `test-output` because it
263+
// uses `runner-output-stream`.
264+
*standard-output* = ostream,
265+
*standard-error* = ostream)
257266
let cond
258267
= profiling (cpu-time-seconds, cpu-time-microseconds, allocation)
259268
block ()
260269
test.test-function();
270+
cleanup
271+
let output = stream-contents(ostream);
272+
~empty?(output)
273+
& write-captured-output(output);
261274
exception (err :: <assertion-failure>,
262275
test: curry(handle-condition?, runner))
263276
// An assertion failure causes the remainder of a test to be

tests/testworks-test-suite.dylan

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,9 @@ define test test-write-test-file ()
745745
assert-equal("abc", fs/with-open-file (stream = y)
746746
read-to-end(stream)
747747
end);
748+
749+
let z = write-test-file("1/2/3", contents: "123");
750+
assert-equal(file-locator(test-temp-directory(), "1", "2", "3"), z);
748751
end test;
749752

750753
define test test-register-component--duplicate-test-name-causes-error ()

utils.dylan

Lines changed: 47 additions & 17 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
@@ -165,9 +182,8 @@ define function write-test-file
165182
locator
166183
end function;
167184

168-
// For tests to do debugging output.
169-
// TODO(cgay): Collect this and stdio into a log file per test run
170-
// or per test. The Surefire report has a place for stdout, too.
185+
// For output to the main output stream for the test run. That is, this output is never
186+
// redirected to the _captured-stdout.txt file in a test's temp directory.
171187
define method test-output
172188
(format-string :: <string>, #rest format-args) => ()
173189
let stream = if (*runner*)
@@ -180,3 +196,17 @@ define method test-output
180196
force-output(stream);
181197
end;
182198
end method;
199+
200+
define constant $captured-output-filename :: <string> = "_captured-stdout.txt";
201+
202+
define variable *output-captured?* :: <boolean> = #f;
203+
204+
define function write-captured-output (output :: <string>)
205+
block ()
206+
let file = file-locator(test-temp-directory(), $captured-output-filename);
207+
write-test-file(file, contents: output);
208+
*output-captured?* := #t;
209+
exception (err :: <error>)
210+
test-output("ERROR writing to test's output file: %s\n%s\n", err, output);
211+
end;
212+
end function;

0 commit comments

Comments
 (0)