1 ! Copyright (C) 2003, 2010 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors arrays assocs combinators command-line
4 compiler.units continuations debugger effects generalizations io
5 io.files.temp io.files.unique kernel lexer math math.functions
6 math.vectors namespaces parser prettyprint quotations sequences
7 sequences.generalizations source-files source-files.errors
8 source-files.errors.debugger splitting stack-checker summary
9 system tools.errors tools.time unicode vocabs vocabs.files
10 vocabs.hierarchy vocabs.hierarchy.private vocabs.loader
11 vocabs.metadata vocabs.parser vocabs.refresh words ;
14 TUPLE: test-failure < source-file-error continuation ;
16 SYMBOL: +test-failure+
18 M: test-failure error-type drop +test-failure+ ;
22 test-failures [ V{ } clone ] initialize
25 { type +test-failure+ }
26 { word ":test-failures" }
27 { plural "unit test failures" }
28 { icon "vocab:ui/tools/error-list/icons/unit-test-error.png" }
29 { quot [ test-failures get ] }
33 f silent-tests? set-global
35 SYMBOL: verbose-tests?
36 t verbose-tests? set-global
38 SYMBOL: restartable-tests?
39 t restartable-tests? set-global
41 : <test-failure> ( error experiment path line# -- test-failure )
47 error-continuation get >>continuation ;
49 SYMBOL: long-unit-tests-threshold
50 long-unit-tests-threshold [ 10,000,000,000 ] initialize
52 SYMBOL: long-unit-tests-enabled?
53 long-unit-tests-enabled? [ t ] initialize
57 : notify-test-failed ( error experiment path line# -- )
58 "--> test failed!" print
59 <test-failure> test-failures get push
60 notify-error-observers ;
62 SYMBOL: current-test-file
64 : notify-test-file-failed ( error -- )
65 [ f current-test-file get ] keep error-line notify-test-failed ;
67 :: (unit-test) ( output input -- error/f failed? tested? )
68 [ { } input with-datastack output assert-sequence= f f ] [ t ] recover t ;
70 : (long-unit-test) ( output input -- error/f failed? tested? )
71 long-unit-tests-enabled? get [ (unit-test) ] [ 2drop f f f ] if ;
73 : (unit-test-comparator) ( output input comparator -- error/f failed? tested? )
75 { } _ with-datastack _ >quotation
76 [ 3dup @ [ 3drop t ] [ drop assert ] if ] compose
77 with-datastack first dup not
78 ] [ t ] recover t ; inline
80 : (unit-test~) ( output input -- error/f failed? tested? )
81 [ ~ ] (unit-test-comparator) ;
83 : (unit-test-v~) ( output input -- error/f failed? tested? )
84 [ v~ ] (unit-test-comparator) ;
86 : short-effect ( effect -- pair )
87 [ in>> length ] [ out>> length ] bi 2array ;
89 :: (must-infer-as) ( effect quot -- error/f failed? tested? )
90 [ quot infer short-effect effect assert= f f ] [ t ] recover t ;
92 :: (must-infer) ( quot -- error/f failed? tested? )
93 [ quot infer drop f f ] [ t ] recover t ;
95 SINGLETON: did-not-fail
97 M: did-not-fail summary drop "Did not fail" ;
99 :: (must-fail-with) ( quot pred -- error/f failed? tested? )
100 [ { } quot with-datastack drop did-not-fail t ]
101 [ dup pred call( error -- ? ) [ drop f f ] [ t ] if ] recover t ;
103 :: (must-fail) ( quot -- error/f failed? tested? )
104 [ { } quot with-datastack drop did-not-fail t ] [ drop f f ] recover t ;
106 : experiment-title ( word -- string )
107 "(" ?head drop ")" ?tail drop
108 H{ { CHAR: - CHAR: \s } } substitute >title ;
110 MACRO: <experiment> ( word -- quot )
111 [ stack-effect in>> length dup ]
112 [ name>> experiment-title ] bi
113 '[ _ ndup _ narray _ prefix ] ;
115 : experiment. ( seq -- )
116 [ first write ": " write ]
117 [ rest verbose-tests? get [ . ] [ short. ] if flush ] bi ;
119 :: experiment ( word: ( -- error/f failed? tested? ) line# -- )
120 word <experiment> :> e
121 silent-tests? get [ e experiment. ] unless
124 current-test-file get [
125 e current-test-file get line# notify-test-failed
128 ] [ 2drop "Warning: test skipped!" print ] if ; inline
130 : parse-test ( accum word -- accum )
132 lexer get line>> suffix!
133 \ experiment suffix! ; inline
140 [ "(" ")" surround search '[ _ parse-test ] ] bi
145 : fake-unit-test ( quot -- test-failures )
147 "fake" current-test-file set
148 V{ } clone test-failures set
151 ] with-scope notify-error-observers ; inline
155 : run-test-file ( path -- )
156 dup current-test-file [
157 test-failures get current-test-file get +test-failure+ delete-file-errors
159 restartable-tests? get
160 [ dup compute-restarts empty? not ] [ f ] if
161 [ rethrow ] [ notify-test-file-failed ] if
165 SYMBOL: forget-tests?
169 : forget-tests ( files -- )
171 [ [ [ forget-source ] each ] with-compilation-unit ] [ drop ] if ;
173 : possible-long-unit-tests ( vocab nanos -- )
174 long-unit-tests-threshold get [
175 dupd > long-unit-tests-enabled? get not and [
177 "Warning: possible long unit test for " write
178 vocab-name write " - " write
179 1,000,000,000 /f pprint " seconds" print
183 : test-vocab ( vocab -- )
185 dup source-loaded?>> [
187 [ [ run-test-file ] each ]
190 ] benchmark possible-long-unit-tests
194 : test-vocabs ( vocabs -- )
195 [ don't-test? ] reject [ test-vocab ] each ;
199 : with-test-file ( ..a quot: ( ..a path -- ..b ) -- ..b )
200 '[ "" "" _ cleanup-unique-file ] with-temp-directory ; inline
202 : with-test-directory ( ..a quot: ( ..a -- ..b ) -- ..b )
203 [ cleanup-unique-directory ] with-temp-directory ; inline
208 TEST: unit-test-comparator
215 M: test-failure error. ( error -- )
217 [ error-location print nl ]
218 [ asset>> [ experiment. nl ] when* ]
220 [ continuation>> call>> callstack. ]
223 : :test-failures ( -- ) test-failures get errors. ;
225 : test ( prefix -- ) loaded-child-vocab-names test-vocabs ;
227 : test-all ( -- ) "" test ;
229 : refresh-and-test ( prefix -- ) to-refresh [ do-refresh ] keepdd test-vocabs ;
231 : refresh-and-test-all ( -- ) "" refresh-and-test ;
235 "--fast" swap [ member? ] [ remove ] 2bi swap
236 [ f long-unit-tests-enabled? set-global ] when
238 dup vocab-roots get member? [
239 "" vocabs-to-load [ require-all ] keep
241 [ load ] [ loaded-child-vocab-names ] bi
244 test-failures get empty?
245 [ [ "==== FAILING TESTS" print flush :test-failures ] unless ]