@@ -87,12 +87,7 @@ fn parse_config(&str[] args) -> config {
87
87
src_base = getopts:: opt_str( match , "src-base" ) ,
88
88
build_base = getopts:: opt_str( match , "build-base" ) ,
89
89
stage_id = getopts:: opt_str( match , "stage-id" ) ,
90
- mode = alt getopts:: opt_str( match , "mode" ) {
91
- "compile-fail" { mode_compile_fail }
92
- "run-fail" { mode_run_fail }
93
- "run-pass" { mode_run_pass }
94
- _ { fail "invalid mode" }
95
- } ,
90
+ mode = str_mode( getopts:: opt_str( match , "mode" ) ) ,
96
91
run_ignored = getopts:: opt_present( match , "ignored" ) ,
97
92
filter = if vec:: len ( match . free) > 0 u {
98
93
option:: some( match. free . ( 0 ) )
@@ -115,22 +110,37 @@ fn log_config(&config config) {
115
110
logv( c, #fmt( "stage_id: %s" , config. stage_id) ) ;
116
111
logv( c, #fmt( "mode: %s" , mode_str( config. mode) ) ) ;
117
112
logv( c, #fmt( "run_ignored: %b" , config. run_ignored) ) ;
118
- logv( c, #fmt( "filter: %s" , alt ( config. filter) {
119
- option:: some( ?f) { f }
120
- option:: none { "(none)" }
121
- } ) ) ;
122
- logv( c, #fmt( "runtool: %s" , alt ( config. runtool) {
123
- option:: some( ?s) { s }
124
- option:: none { "(none)" }
125
- } ) ) ;
126
- logv( c, #fmt( "rustcflags: %s" , alt ( config. rustcflags) {
127
- option:: some( ?s) { s }
128
- option:: none { "(none)" }
129
- } ) ) ;
113
+ logv( c, #fmt( "filter: %s" , opt_str( config. filter) ) ) ;
114
+ logv( c, #fmt( "runtool: %s" , opt_str( config. runtool) ) ) ;
115
+ logv( c, #fmt( "rustcflags: %s" , opt_str( config. rustcflags) ) ) ;
130
116
logv( c, #fmt( "verbose: %b" , config. verbose) ) ;
131
117
logv( c, #fmt( "\n " ) ) ;
132
118
}
133
119
120
+ fn opt_str( option:: t[ str] maybestr) -> str {
121
+ alt maybestr {
122
+ option : : some( ?s) { s }
123
+ option:: none { "(none)" }
124
+ }
125
+ }
126
+
127
+ fn str_opt( str maybestr) -> option:: t[ str] {
128
+ if maybestr != "(none)" {
129
+ option:: some( maybestr)
130
+ } else {
131
+ option:: none
132
+ }
133
+ }
134
+
135
+ fn str_mode( str s) -> mode {
136
+ alt s {
137
+ "compile-fail" { mode_compile_fail }
138
+ "run-fail" { mode_run_fail }
139
+ "run-pass" { mode_run_pass }
140
+ _ { fail "invalid mode" }
141
+ }
142
+ }
143
+
134
144
fn mode_str( mode mode) -> str {
135
145
alt ( mode) {
136
146
mode_compile_fail { "compile-fail" }
@@ -147,7 +157,7 @@ fn run_tests(&config config) {
147
157
auto cx = rec ( config = config,
148
158
procsrv = procsrv:: mk ( ) ) ;
149
159
auto tests = make_tests ( cx) ;
150
- test:: run_tests_console ( opts, tests) ;
160
+ test:: run_tests_console_ ( opts, tests. tests , tests . to_task ) ;
151
161
procsrv:: close ( cx. procsrv ) ;
152
162
}
153
163
@@ -156,16 +166,21 @@ fn test_opts(&config config) -> test::test_opts {
156
166
run_ignored = config. run_ignored )
157
167
}
158
168
159
- fn make_tests( & cx cx) -> test:: test_desc[ ] {
169
+ type tests_and_conv_fn = rec( test:: test_desc[ ] tests,
170
+ fn( & fn( ) ) -> task to_task) ;
171
+
172
+ fn make_tests ( & cx cx) -> tests_and_conv_fn {
160
173
log #fmt( "making tests from %s" , cx. config. src_base) ;
174
+ auto configport = port[ str] ( ) ;
161
175
auto tests = ~[ ] ;
162
176
for ( str file in fs:: list_dir( cx. config. src_base) ) {
163
177
log #fmt( "inspecting file %s" , file) ;
164
178
if ( is_test ( file) ) {
165
- tests += ~[ make_test( cx, file) ] ;
179
+ tests += ~[ make_test ( cx, file, configport ) ] ;
166
180
}
167
181
}
168
- ret tests;
182
+ ret rec( tests = tests,
183
+ to_task = bind closure_to_task ( cx, configport, _) ) ;
169
184
}
170
185
171
186
fn is_test ( & str testfile) -> bool {
@@ -176,9 +191,10 @@ fn is_test(&str testfile) -> bool {
176
191
|| str:: starts_with ( name, "~" ) )
177
192
}
178
193
179
- fn make_test( & cx cx, & str testfile) -> test:: test_desc {
194
+ fn make_test ( & cx cx, & str testfile,
195
+ & port[ str] configport) -> test:: test_desc {
180
196
rec( name = testfile,
181
- fn = make_test_fn ( cx , testfile ) ,
197
+ fn = make_test_closure ( testfile , chan ( configport ) ) ,
182
198
ignore = is_test_ignored ( cx. config , testfile) )
183
199
}
184
200
@@ -206,44 +222,97 @@ iter iter_header(&str testfile) -> str {
206
222
}
207
223
}
208
224
209
- fn make_test_fn( & cx cx, & str testfile) -> test:: test_fn {
210
- // We're doing some ferociously unsafe nonsense here by creating a closure
211
- // and letting the test runner spawn it into a task. To avoid having
212
- // different tasks fighting over their refcounts and then the wrong task
213
- // freeing a box we need to clone everything, and make sure our closure
214
- // outlives all the tasks.
215
- fn clonestr( & str s) -> str {
216
- str :: unsafe_from_bytes( str :: bytes( s) )
217
- }
225
+ /*
226
+ So this is kind of crappy:
227
+
228
+ A test is just defined as a function, as you might expect, but tests have to
229
+ run their own tasks. Unfortunately, if your test needs dynamic data then it
230
+ needs to be a closure, and transferring closures across tasks without
231
+ committing a host of memory management transgressions is just impossible.
232
+
233
+ To get around this, the standard test runner allows you the opportunity do
234
+ your own conversion from a test function to a task. It gives you your function
235
+ and you give it back a task.
236
+
237
+ So that's what we're going to do. Here's where it gets stupid. To get the
238
+ the data out of the test function we are going to run the test function,
239
+ which will do nothing but send the data for that test to a port we've set
240
+ up. Then we'll spawn that data into another task and return the task.
241
+ Really convoluted. Need to think up of a better definition for tests.
242
+ */
243
+
244
+ fn make_test_closure ( & str testfile ,
245
+ chan[ str] configchan ) -> test:: test_fn {
246
+ bind send_config ( testfile, configchan)
247
+ }
248
+
249
+ fn send_config ( str testfile , chan[ str] configchan ) {
250
+ task:: send ( configchan, testfile) ;
251
+ }
252
+
253
+ /*
254
+ FIXME: Good god forgive me.
255
+
256
+ So actually shuttling structural data across tasks isn't possible at this
257
+ time, but we can send strings! Sadly, I need the whole config record, in the
258
+ test task so, instead of fixing the mechanism in the compiler I'm going to
259
+ break up the config record and pass everything individually to the spawned
260
+ function. */
261
+
262
+ fn closure_to_task ( cx cx, port[ str] configport , & fn ( ) testfn) -> task {
263
+ testfn ( ) ;
264
+ auto testfile = task:: recv ( configport) ;
265
+ ret spawn run_test_task ( cx. config . compile_lib_path ,
266
+ cx. config . run_lib_path ,
267
+ cx. config . rustc_path ,
268
+ cx. config . src_base ,
269
+ cx. config . build_base ,
270
+ cx. config . stage_id ,
271
+ mode_str ( cx. config . mode ) ,
272
+ cx. config . run_ignored ,
273
+ opt_str ( cx. config . filter ) ,
274
+ opt_str ( cx. config . runtool ) ,
275
+ opt_str ( cx. config . rustcflags ) ,
276
+ cx. config . verbose ,
277
+ procsrv:: clone ( cx. procsrv ) . chan ,
278
+ testfile) ;
279
+ }
280
+
281
+ fn run_test_task ( str compile_lib_path ,
282
+ str run_lib_path ,
283
+ str rustc_path ,
284
+ str src_base ,
285
+ str build_base ,
286
+ str stage_id ,
287
+ str mode ,
288
+ bool run_ignored ,
289
+ str opt_filter ,
290
+ str opt_runtool ,
291
+ str opt_rustcflags ,
292
+ bool verbose ,
293
+ procsrv:: reqchan procsrv_chan ,
294
+ str testfile ) {
295
+
296
+ auto config = rec ( compile_lib_path = compile_lib_path,
297
+ run_lib_path = run_lib_path,
298
+ rustc_path = rustc_path,
299
+ src_base = src_base,
300
+ build_base = build_base,
301
+ stage_id = stage_id,
302
+ mode = str_mode ( mode) ,
303
+ run_ignored = run_ignored,
304
+ filter = str_opt ( opt_filter) ,
305
+ runtool = str_opt ( opt_runtool) ,
306
+ rustcflags = str_opt ( opt_rustcflags) ,
307
+ verbose = verbose) ;
308
+
309
+ auto procsrv = procsrv:: from_chan ( procsrv_chan) ;
218
310
219
- fn cloneoptstr( & option:: t[ str ] s) -> option:: t[ str ] {
220
- alt s {
221
- option:: some( ?s) { option:: some( clonestr( s) ) }
222
- option:: none { option:: none }
223
- }
224
- }
311
+ auto cx = rec ( config = config,
312
+ procsrv = procsrv) ;
225
313
226
- auto configclone = rec(
227
- compile_lib_path = clonestr( cx. config. compile_lib_path) ,
228
- run_lib_path = clonestr( cx. config. run_lib_path) ,
229
- rustc_path = clonestr( cx. config. rustc_path) ,
230
- src_base = clonestr( cx. config. src_base) ,
231
- build_base = clonestr( cx. config. build_base) ,
232
- stage_id = clonestr( cx. config. stage_id) ,
233
- mode = cx. config. mode,
234
- run_ignored = cx. config. run_ignored,
235
- filter = cloneoptstr( cx. config. filter) ,
236
- runtool = cloneoptstr( cx. config. runtool) ,
237
- rustcflags = cloneoptstr( cx. config. rustcflags) ,
238
- verbose = cx. config. verbose) ;
239
- auto cxclone = rec( config = configclone,
240
- procsrv = procsrv:: clone( cx. procsrv) ) ;
241
- auto testfileclone = clonestr( testfile) ;
242
- ret bind run_test( cxclone, testfileclone) ;
243
- }
244
-
245
- fn run_test( cx cx, str testfile) {
246
314
log #fmt( "running %s" , testfile) ;
315
+ task:: unsupervise ( ) ;
247
316
auto props = load_props ( testfile) ;
248
317
alt ( cx. config . mode ) {
249
318
mode_compile_fail {
@@ -561,12 +630,16 @@ mod procsrv {
561
630
562
631
export handle;
563
632
export mk;
633
+ export from_chan;
564
634
export clone;
565
635
export run;
566
636
export close;
637
+ export reqchan;
638
+
639
+ type reqchan = chan[ request ] ;
567
640
568
641
type handle = rec ( option:: t[ task] task ,
569
- chan [ request ] chan) ;
642
+ reqchan chan) ;
570
643
571
644
tag request {
572
645
exec( str , str , vec[ str ] , chan[ response] ) ;
@@ -581,6 +654,11 @@ mod procsrv {
581
654
chan = res. chan ) ;
582
655
}
583
656
657
+ fn from_chan ( & reqchan ch) -> handle {
658
+ rec ( task = option:: none,
659
+ chan = ch)
660
+ }
661
+
584
662
fn clone ( & handle handle) -> handle {
585
663
// Sharing tasks across tasks appears to be (yet another) recipe for
586
664
// disaster, so our handle clones will not get the task pointer.
0 commit comments