]> git.lizzy.rs Git - rust.git/blob - src/compiletest/header.rs
Rollup merge of #27374 - dhuseby:fixing_configure_bsd, r=alexcrichton
[rust.git] / src / compiletest / header.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::env;
12 use std::fs::File;
13 use std::io::BufReader;
14 use std::io::prelude::*;
15 use std::path::{Path, PathBuf};
16
17 use common::Config;
18 use common;
19 use util;
20
21 pub struct TestProps {
22     // Lines that should be expected, in order, on standard out
23     pub error_patterns: Vec<String> ,
24     // Extra flags to pass to the compiler
25     pub compile_flags: Option<String>,
26     // Extra flags to pass when the compiled code is run (such as --bench)
27     pub run_flags: Option<String>,
28     // If present, the name of a file that this test should match when
29     // pretty-printed
30     pub pp_exact: Option<PathBuf>,
31     // Modules from aux directory that should be compiled
32     pub aux_builds: Vec<String> ,
33     // Environment settings to use during execution
34     pub exec_env: Vec<(String,String)> ,
35     // Lines to check if they appear in the expected debugger output
36     pub check_lines: Vec<String> ,
37     // Flag to force a crate to be built with the host architecture
38     pub force_host: bool,
39     // Check stdout for error-pattern output as well as stderr
40     pub check_stdout: bool,
41     // Don't force a --crate-type=dylib flag on the command line
42     pub no_prefer_dynamic: bool,
43     // Run --pretty expanded when running pretty printing tests
44     pub pretty_expanded: bool,
45     // Which pretty mode are we testing with, default to 'normal'
46     pub pretty_mode: String,
47     // Only compare pretty output and don't try compiling
48     pub pretty_compare_only: bool,
49     // Patterns which must not appear in the output of a cfail test.
50     pub forbid_output: Vec<String>,
51 }
52
53 // Load any test directives embedded in the file
54 pub fn load_props(testfile: &Path) -> TestProps {
55     let mut error_patterns = Vec::new();
56     let mut aux_builds = Vec::new();
57     let mut exec_env = Vec::new();
58     let mut compile_flags = None;
59     let mut run_flags = None;
60     let mut pp_exact = None;
61     let mut check_lines = Vec::new();
62     let mut force_host = false;
63     let mut check_stdout = false;
64     let mut no_prefer_dynamic = false;
65     let mut pretty_expanded = false;
66     let mut pretty_mode = None;
67     let mut pretty_compare_only = false;
68     let mut forbid_output = Vec::new();
69     iter_header(testfile, &mut |ln| {
70         match parse_error_pattern(ln) {
71           Some(ep) => error_patterns.push(ep),
72           None => ()
73         };
74
75         if compile_flags.is_none() {
76             compile_flags = parse_compile_flags(ln);
77         }
78
79         if run_flags.is_none() {
80             run_flags = parse_run_flags(ln);
81         }
82
83         if pp_exact.is_none() {
84             pp_exact = parse_pp_exact(ln, testfile);
85         }
86
87         if !force_host {
88             force_host = parse_force_host(ln);
89         }
90
91         if !check_stdout {
92             check_stdout = parse_check_stdout(ln);
93         }
94
95         if !no_prefer_dynamic {
96             no_prefer_dynamic = parse_no_prefer_dynamic(ln);
97         }
98
99         if !pretty_expanded {
100             pretty_expanded = parse_pretty_expanded(ln);
101         }
102
103         if pretty_mode.is_none() {
104             pretty_mode = parse_pretty_mode(ln);
105         }
106
107         if !pretty_compare_only {
108             pretty_compare_only = parse_pretty_compare_only(ln);
109         }
110
111         match parse_aux_build(ln) {
112             Some(ab) => { aux_builds.push(ab); }
113             None => {}
114         }
115
116         match parse_exec_env(ln) {
117             Some(ee) => { exec_env.push(ee); }
118             None => {}
119         }
120
121         match parse_check_line(ln) {
122             Some(cl) => check_lines.push(cl),
123             None => ()
124         };
125
126         match parse_forbid_output(ln) {
127             Some(of) => forbid_output.push(of),
128             None => (),
129         }
130
131         true
132     });
133
134     for key in vec!["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] {
135         match env::var(key) {
136             Ok(val) =>
137                 if exec_env.iter().find(|&&(ref x, _)| *x == key.to_string()).is_none() {
138                     exec_env.push((key.to_string(), val))
139                 },
140             Err(..) => {}
141         }
142     }
143
144     TestProps {
145         error_patterns: error_patterns,
146         compile_flags: compile_flags,
147         run_flags: run_flags,
148         pp_exact: pp_exact,
149         aux_builds: aux_builds,
150         exec_env: exec_env,
151         check_lines: check_lines,
152         force_host: force_host,
153         check_stdout: check_stdout,
154         no_prefer_dynamic: no_prefer_dynamic,
155         pretty_expanded: pretty_expanded,
156         pretty_mode: pretty_mode.unwrap_or("normal".to_string()),
157         pretty_compare_only: pretty_compare_only,
158         forbid_output: forbid_output,
159     }
160 }
161
162 pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool {
163     fn ignore_target(config: &Config) -> String {
164         format!("ignore-{}", util::get_os(&config.target))
165     }
166     fn ignore_architecture(config: &Config) -> String {
167         format!("ignore-{}", util::get_arch(&config.target))
168     }
169     fn ignore_stage(config: &Config) -> String {
170         format!("ignore-{}",
171                 config.stage_id.split('-').next().unwrap())
172     }
173     fn ignore_env(config: &Config) -> String {
174         format!("ignore-{}", util::get_env(&config.target).unwrap_or("<unknown>"))
175     }
176     fn ignore_gdb(config: &Config, line: &str) -> bool {
177         if config.mode != common::DebugInfoGdb {
178             return false;
179         }
180
181         if parse_name_directive(line, "ignore-gdb") {
182             return true;
183         }
184
185         match config.gdb_version {
186             Some(ref actual_version) => {
187                 if line.contains("min-gdb-version") {
188                     let min_version = line.trim()
189                                           .split(' ')
190                                           .last()
191                                           .expect("Malformed GDB version directive");
192                     // Ignore if actual version is smaller the minimum required
193                     // version
194                     gdb_version_to_int(actual_version) <
195                         gdb_version_to_int(min_version)
196                 } else {
197                     false
198                 }
199             }
200             None => false
201         }
202     }
203
204     fn ignore_lldb(config: &Config, line: &str) -> bool {
205         if config.mode != common::DebugInfoLldb {
206             return false;
207         }
208
209         if parse_name_directive(line, "ignore-lldb") {
210             return true;
211         }
212
213         match config.lldb_version {
214             Some(ref actual_version) => {
215                 if line.contains("min-lldb-version") {
216                     let min_version = line.trim()
217                                           .split(' ')
218                                           .last()
219                                           .expect("Malformed lldb version directive");
220                     // Ignore if actual version is smaller the minimum required
221                     // version
222                     lldb_version_to_int(actual_version) <
223                         lldb_version_to_int(min_version)
224                 } else {
225                     false
226                 }
227             }
228             None => false
229         }
230     }
231
232     let val = iter_header(testfile, &mut |ln| {
233         !parse_name_directive(ln, "ignore-test") &&
234         !parse_name_directive(ln, &ignore_target(config)) &&
235         !parse_name_directive(ln, &ignore_architecture(config)) &&
236         !parse_name_directive(ln, &ignore_stage(config)) &&
237         !parse_name_directive(ln, &ignore_env(config)) &&
238         !(config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty")) &&
239         !(config.target != config.host && parse_name_directive(ln, "ignore-cross-compile")) &&
240         !ignore_gdb(config, ln) &&
241         !ignore_lldb(config, ln)
242     });
243
244     !val
245 }
246
247 fn iter_header(testfile: &Path, it: &mut FnMut(&str) -> bool) -> bool {
248     let rdr = BufReader::new(File::open(testfile).unwrap());
249     for ln in rdr.lines() {
250         // Assume that any directives will be found before the first
251         // module or function. This doesn't seem to be an optimization
252         // with a warm page cache. Maybe with a cold one.
253         let ln = ln.unwrap();
254         if ln.starts_with("fn") ||
255                 ln.starts_with("mod") {
256             return true;
257         } else {
258             if !(it(ln.trim())) {
259                 return false;
260             }
261         }
262     }
263     return true;
264 }
265
266 fn parse_error_pattern(line: &str) -> Option<String> {
267     parse_name_value_directive(line, "error-pattern")
268 }
269
270 fn parse_forbid_output(line: &str) -> Option<String> {
271     parse_name_value_directive(line, "forbid-output")
272 }
273
274 fn parse_aux_build(line: &str) -> Option<String> {
275     parse_name_value_directive(line, "aux-build")
276 }
277
278 fn parse_compile_flags(line: &str) -> Option<String> {
279     parse_name_value_directive(line, "compile-flags")
280 }
281
282 fn parse_run_flags(line: &str) -> Option<String> {
283     parse_name_value_directive(line, "run-flags")
284 }
285
286 fn parse_check_line(line: &str) -> Option<String> {
287     parse_name_value_directive(line, "check")
288 }
289
290 fn parse_force_host(line: &str) -> bool {
291     parse_name_directive(line, "force-host")
292 }
293
294 fn parse_check_stdout(line: &str) -> bool {
295     parse_name_directive(line, "check-stdout")
296 }
297
298 fn parse_no_prefer_dynamic(line: &str) -> bool {
299     parse_name_directive(line, "no-prefer-dynamic")
300 }
301
302 fn parse_pretty_expanded(line: &str) -> bool {
303     parse_name_directive(line, "pretty-expanded")
304 }
305
306 fn parse_pretty_mode(line: &str) -> Option<String> {
307     parse_name_value_directive(line, "pretty-mode")
308 }
309
310 fn parse_pretty_compare_only(line: &str) -> bool {
311     parse_name_directive(line, "pretty-compare-only")
312 }
313
314 fn parse_exec_env(line: &str) -> Option<(String, String)> {
315     parse_name_value_directive(line, "exec-env").map(|nv| {
316         // nv is either FOO or FOO=BAR
317         let mut strs: Vec<String> = nv
318                                       .splitn(2, '=')
319                                       .map(|s| s.to_string())
320                                       .collect();
321
322         match strs.len() {
323           1 => (strs.pop().unwrap(), "".to_string()),
324           2 => {
325               let end = strs.pop().unwrap();
326               (strs.pop().unwrap(), end)
327           }
328           n => panic!("Expected 1 or 2 strings, not {}", n)
329         }
330     })
331 }
332
333 fn parse_pp_exact(line: &str, testfile: &Path) -> Option<PathBuf> {
334     match parse_name_value_directive(line, "pp-exact") {
335       Some(s) => Some(PathBuf::from(&s)),
336       None => {
337         if parse_name_directive(line, "pp-exact") {
338             testfile.file_name().map(|s| PathBuf::from(s))
339         } else {
340             None
341         }
342       }
343     }
344 }
345
346 fn parse_name_directive(line: &str, directive: &str) -> bool {
347     // This 'no-' rule is a quick hack to allow pretty-expanded and no-pretty-expanded to coexist
348     line.contains(directive) && !line.contains(&("no-".to_string() + directive))
349 }
350
351 pub fn parse_name_value_directive(line: &str, directive: &str)
352                                   -> Option<String> {
353     let keycolon = format!("{}:", directive);
354     match line.find(&keycolon) {
355         Some(colon) => {
356             let value = line[(colon + keycolon.len()) .. line.len()].to_string();
357             debug!("{}: {}", directive, value);
358             Some(value)
359         }
360         None => None
361     }
362 }
363
364 pub fn gdb_version_to_int(version_string: &str) -> isize {
365     let error_string = format!(
366         "Encountered GDB version string with unexpected format: {}",
367         version_string);
368     let error_string = error_string;
369
370     let components: Vec<&str> = version_string.trim().split('.').collect();
371
372     if components.len() != 2 {
373         panic!("{}", error_string);
374     }
375
376     let major: isize = components[0].parse().ok().expect(&error_string);
377     let minor: isize = components[1].parse().ok().expect(&error_string);
378
379     return major * 1000 + minor;
380 }
381
382 pub fn lldb_version_to_int(version_string: &str) -> isize {
383     let error_string = format!(
384         "Encountered LLDB version string with unexpected format: {}",
385         version_string);
386     let error_string = error_string;
387     let major: isize = version_string.parse().ok().expect(&error_string);
388     return major;
389 }