1use serde::{Deserialize, Serialize};
2use std::path::{Path, PathBuf};
3use walkdir::WalkDir;
4
5mod error;
6pub use error::Error;
7
8pub struct Harness {
10 test_paths: Vec<PathBuf>,
11 idx: usize,
12}
13
14impl Harness {
15 pub fn new<P: AsRef<Path>>(test_root: P) -> Result<Self, Error> {
18 let test_paths = WalkDir::new(test_root)
19 .min_depth(1)
20 .into_iter()
21 .filter_map(|e| {
22 let entry = match e {
23 Err(e) => return Some(Err(e)),
24 Ok(e) => e,
25 };
26 let path = entry.path();
27 if path.is_dir() {
28 None
29 } else {
30 let ext = path.extension()?;
31 if ext == "js" {
32 let file_name = path.file_name()?;
33 let file_name = file_name.to_str()?;
34 if file_name.ends_with("_FIXTURE.js") {
35 None
36 } else {
37 Some(Ok(path.to_path_buf()))
38 }
39 } else {
40 None
41 }
42 }
43 })
44 .collect::<Result<Vec<PathBuf>, walkdir::Error>>()?;
45 Ok(Self { test_paths, idx: 0 })
46 }
47
48 fn create_test_from(&self, p: &PathBuf) -> Result<Test, Error> {
49 let contents = std::fs::read_to_string(p)?;
50 let (yaml_start, yaml_end) = Self::find_yaml(&contents, p)?;
51 let yaml = contents[yaml_start..yaml_end].replace("\r", "\n");
52 let desc = serde_yaml::from_str(&yaml)?;
53 Ok(Test {
54 desc,
55 path: p.clone(),
56 source: contents,
57 })
58 }
59
60 fn find_yaml(content: &str, path: &PathBuf) -> Result<(usize, usize), Error> {
61 let start = content
62 .find("/*---")
63 .ok_or_else(|| Error::DescriptionInvalid(path.clone()))?;
64 let end = content
65 .find("---*/")
66 .ok_or_else(|| Error::DescriptionInvalid(path.clone()))?;
67 Ok((start + 5, end))
68 }
69}
70
71impl Iterator for Harness {
72 type Item = Result<Test, Error>;
73 fn next(&mut self) -> Option<Self::Item> {
74 if self.idx >= self.test_paths.len() {
75 None
76 } else {
77 let p = self.test_paths.get(self.idx)?;
78 self.idx += 1;
79 Some(self.create_test_from(p))
80 }
81 }
82}
83
84pub struct Test {
87 pub source: String,
90 pub path: PathBuf,
93 pub desc: Description,
96}
97
98#[derive(Debug, Deserialize, Clone, Serialize, PartialEq)]
101pub struct Description {
102 pub id: Option<String>,
104 pub esid: Option<String>,
106 pub es5id: Option<String>,
108 pub es6id: Option<String>,
110 pub info: Option<String>,
114 pub description: Option<String>,
118 pub negative: Option<Negative>,
121 #[serde(default)]
126 pub includes: Vec<String>,
127 #[serde(default)]
133 pub flags: Vec<Flag>,
134 #[serde(default)]
137 pub locale: Vec<String>,
138 #[serde(default)]
141 pub features: Vec<String>,
142}
143
144#[derive(Debug, Deserialize, Clone, Serialize, PartialEq)]
148pub struct Negative {
149 pub phase: Phase,
151 #[serde(alias = "type")]
154 pub kind: Option<String>,
155}
156
157#[derive(Debug, Deserialize, Clone, Copy, Serialize, PartialEq)]
159#[serde(rename_all = "camelCase")]
160pub enum Phase {
161 Parse,
163 Early,
166 Resolution,
168 Runtime,
170}
171
172#[derive(Debug, Deserialize, PartialEq, Clone, Copy, Serialize)]
173#[serde(rename_all = "camelCase")]
174pub enum Flag {
175 OnlyStrict,
177 NoStrict,
179 Module,
181 Raw,
183 Async,
186 Generated,
188 #[serde(alias = "CanBlockIsFalse")]
190 CanBlockIsFalse,
191 #[serde(alias = "CanBlockIsTrue")]
193 CanBlockIsTrue,
194 #[serde(alias = "non-deterministic")]
197 NonDeterministic,
198}