Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
ErichDonGubler committed Jun 21, 2024
1 parent 240d475 commit 30a494a
Showing 1 changed file with 183 additions and 56 deletions.
239 changes: 183 additions & 56 deletions moz-webgpu-cts/src/wpt/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ use std::{
};

use camino::{Utf8Component, Utf8Path};

use clap::ValueEnum;
use format::lazy_format;
use itertools::Itertools;
use joinery::JoinableIterator;
use strum::{EnumIter, IntoEnumIterator};

/// A browser supported by [crate::main], used for [`TestEntryPath`]s.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ValueEnum)]
Expand Down Expand Up @@ -83,15 +84,39 @@ pub(crate) enum SpecType {
}

impl SpecType {
pub fn from_spec_path_base_name(base_name: &str) -> Option<(Self, &str)> {
// TODO: auto-generate `enum` variants?
[Self::Html].into_iter().find_map(|variant| {
base_name
.strip_suffix(variant.file_extension())
.map(|some| (variant, some))
// TODO: auto-generate `enum` variants?
pub fn iter() -> impl Iterator<Item = Self> {
[Self::Html, Self::Js(JsSpecType::DedicatedWorker)].into_iter()
}

pub fn from_base_name(base_name: &str) -> Option<(Self, &str)> {
Self::iter().find_map(|variant| {
strip_suffix_with_value(base_name, variant.file_extension(), variant)
})
}

pub fn validate_test_entry_base_name<'a>(
&self,
base_name: &'a str,
) -> Option<(TestEntryType, &'a str)> {
let permitted_test_entry_types = match self {
Self::Js(JsSpecType::DedicatedWorker) => &[TestEntryType::Js {
exec_scope: JsExecScope::DedicatedWorker,
}],
Self::Html => &[TestEntryType::Html],
};
permitted_test_entry_types
.iter()
.copied()
.find_map(|test_entry_type| {
strip_suffix_with_value(
base_name,
test_entry_type.file_extension(),
test_entry_type,
)
})
}

pub fn file_extension(&self) -> &'static str {
match self {
SpecType::Js(JsSpecType::DedicatedWorker) => ".worker.js",
Expand All @@ -115,17 +140,8 @@ pub(crate) enum JsSpecType {
/// [`metadata::File`]: crate::wpt::metadata::File
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) struct TestEntry<'a> {
/// Set if this is a JS test, which causes the test entry to have slightly different naming
/// from its specification file (see also [`SpecPath`]).
///
/// JS tests are converted to `*.html` tests at test execution time and reported as such.
/// The set of values observable here are determined by this entry's spec.'s
/// [`SpecPath::r#type`] and its
///
/// See also [WPT upstream's docs.' "Test Features" section][upstream]
///
/// [upstream]: https://web-platform-tests.org/writing-tests/file-names.html#test-features
pub js_exec_scope: Option<JsExecScope>,
/// The type of this entry. Based on it's spec. file's type (see [`SpecPath::type`]).
pub r#type: TestEntryType,
/// The variant of this particular test from this test's source code. If set, you should be
/// able to correlate this with
///
Expand All @@ -135,10 +151,60 @@ pub(crate) struct TestEntry<'a> {
pub variant: Option<Cow<'a, str>>,
}

/// An executed JS test entry's test type, viz., a [`TestEntry::js_exec_scope`].
///
/// [upstream]: https://web-platform-tests.org/writing-tests/testharness.html
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
/// The test entry analogue to [`SpecPath::type`].
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) enum TestEntryType {
/// An HTML-authored test with no divergence from the base name of its corresponding spec.
/// file. Corresponds to [`SpecType::Html`].
Html,
/// Corresponds to [`SpecType::Js`]. The test entry will have slightly different naming from
/// its spec. file.
///
/// JS tests are converted to `*.html` tests at test execution time and reported as such.
/// The set of values observable here are determined by this entry's spec.'s
/// [`SpecPath::type`] and its
///
/// See also [WPT upstream's docs.' "Test Features" section][upstream]
///
/// [upstream]: https://web-platform-tests.org/writing-tests/file-names.html#test-features
Js { exec_scope: JsExecScope },
}

impl TestEntryType {
pub fn iter() -> impl Iterator<Item = Self> {
// NOTE: `Html` could match at the same time as others, so make it last.
JsExecScope::iter()
.map(|exec_scope| Self::Js { exec_scope })
.chain([Self::Html])
}

pub fn from_base_name(base_name: &str) -> Option<(Self, &str)> {
Self::iter().find_map(|variant| {
strip_suffix_with_value(base_name, variant.file_extension(), variant)
})
}

pub fn file_extension(self) -> &'static str {
match self {
Self::Html => ".html",
Self::Js { exec_scope } => match exec_scope {
JsExecScope::DedicatedWorker => ".worker.html",
},
}
}

pub fn spec_type(self) -> SpecType {
match self {
Self::Html => SpecType::Html,
Self::Js { exec_scope } => match exec_scope {
JsExecScope::DedicatedWorker => SpecType::Js(JsSpecType::DedicatedWorker),
},
}
}
}

/// An executed JS test entry's test type, viz.,
#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) enum JsExecScope {
/// A `*.worker.js` test. See also [WPT upstream docs.' "Dedicated worker test (`.worker.js`)"
/// section][upstream].
Expand Down Expand Up @@ -186,20 +252,22 @@ impl<'a> TestEntryPath<'a> {
None => return Err(err()),
};

let (spec_type, path) = if let Some(path) = path.strip_suffix(".html") {
(SpecType::Html, path)
} else {
return Err(err());
};
let mut path = Cow::<'_, Utf8Path>::from(Utf8Path::new(path));

let (test_entry_type, base_name) =
TestEntryType::from_base_name(path.file_name().ok_or_else(err)?).ok_or_else(err)?;
let spec_type = test_entry_type.spec_type();

path = path.with_file_name(base_name).into();

Ok(Self {
spec_path: SpecPath {
root_dir,
path: Utf8Path::new(path).into(),
path,
r#type: spec_type,
},
test_entry: TestEntry {
js_exec_scope: None,
r#type: test_entry_type,
variant: variant.map(Into::into),
},
})
Expand All @@ -226,8 +294,7 @@ impl<'a> TestEntryPath<'a> {
.strip_suffix(".ini")
.ok_or_else(err)?;

let (spec_type, stripped) =
SpecType::from_spec_path_base_name(test_base_name).ok_or_else(err)?;
let (spec_type, stripped) = SpecType::from_base_name(test_base_name).ok_or_else(err)?;

(spec_type, Utf8Path::new(stripped))
};
Expand All @@ -242,10 +309,9 @@ impl<'a> TestEntryPath<'a> {

let (base_name, variant) = Self::split_test_base_name_from_variant(test_name);

let base_name = match spec_type {
SpecType::Js(_) => todo!(),
SpecType::Html => base_name.strip_suffix(".html").ok_or_else(err)?,
};
let (js_exec_scope, base_name) = spec_type
.validate_test_entry_base_name(base_name)
.ok_or_else(err)?;

if path.components().next_back() != Some(Utf8Component::Normal(base_name)) {
return Err(err());
Expand All @@ -258,6 +324,7 @@ impl<'a> TestEntryPath<'a> {
r#type: spec_type,
},
test_entry: TestEntry {
r#type: js_exec_scope,
variant: variant.map(Into::into),
},
})
Expand All @@ -281,7 +348,11 @@ impl<'a> TestEntryPath<'a> {
path,
r#type,
},
test_entry: TestEntry { variant },
test_entry:
TestEntry {
r#type: js_exec_scope,
variant,
},
} = self;

TestEntryPath {
Expand All @@ -291,6 +362,7 @@ impl<'a> TestEntryPath<'a> {
r#type,
},
test_entry: TestEntry {
r#type: js_exec_scope,
variant: variant.clone().map(|v| v.into_owned().into()),
},
}
Expand All @@ -302,12 +374,16 @@ impl<'a> TestEntryPath<'a> {
SpecPath {
root_dir: _,
path,
r#type,
r#type: _,
},
test_entry:
TestEntry {
r#type: js_exec_scope,
variant,
},
test_entry: TestEntry { variant },
} = self;
let base_name = path.file_name().unwrap();
let file_extension = r#type.file_extension();
let file_extension = js_exec_scope.file_extension();

lazy_format!(move |f| {
write!(f, "{base_name}{file_extension}")?;
Expand All @@ -324,23 +400,17 @@ impl<'a> TestEntryPath<'a> {
SpecPath {
root_dir,
path,
r#type,
r#type: _,
},
test_entry: TestEntry { variant },
test_entry: _,
} = self;
lazy_format!(move |f| {
write!(
f,
"{}{}{}",
root_dir.url_prefix(),
path.components().join_with('/'),
r#type.file_extension()
)?;
if let Some(variant) = variant.as_ref() {
write!(f, "{}", variant)?;
}
Ok(())
})
lazy_format!(move |f| write!(
f,
"{}{}/{}",
root_dir.url_prefix(),
path.components().dropping_back(1).join_with('/'),
self.test_name(),
))
}

pub(crate) fn rel_metadata_path(&self) -> impl Display + '_ {
Expand All @@ -351,7 +421,11 @@ impl<'a> TestEntryPath<'a> {
path,
r#type,
},
test_entry: TestEntry { variant: _ },
test_entry:
TestEntry {
r#type: _,
variant: _,
},
} = self;

let root_dir_dir = root_dir
Expand Down Expand Up @@ -475,6 +549,10 @@ impl From<ServoRootDir> for RootDir {
}
}

fn strip_suffix_with_value<'a, T>(s: &'a str, suffix: &str, t: T) -> Option<(T, &'a str)> {
s.strip_suffix(suffix).map(|some| (t, some))
}

#[test]
fn parse_test_entry_path() {
assert_eq!(
Expand All @@ -491,6 +569,7 @@ fn parse_test_entry_path() {
r#type: SpecType::Html,
},
test_entry: TestEntry {
r#type: TestEntryType::Html,
variant: Some("?stuff=things".into()),
}
}
Expand All @@ -509,7 +588,10 @@ fn parse_test_entry_path() {
path: Utf8Path::new("stuff/things/cts.https").into(),
r#type: SpecType::Html,
},
test_entry: TestEntry { variant: None }
test_entry: TestEntry {
r#type: TestEntryType::Html,
variant: None
}
}
);

Expand All @@ -527,10 +609,55 @@ fn parse_test_entry_path() {
r#type: SpecType::Html,
},
test_entry: TestEntry {
r#type: TestEntryType::Html,
variant: Some("?stuff=things".into()),
}
}
);

assert_eq!(
TestEntryPath::from_metadata_test(
Browser::Servo,
Path::new("tests/wpt/webgpu/meta/webgpu/do_the_thing.worker.js.ini"),
"do_the_thing.worker.html"
)
.unwrap(),
TestEntryPath {
spec_path: SpecPath {
root_dir: ServoRootDir::WebGpu.into(),
path: Utf8Path::new("webgpu/do_the_thing").into(),
r#type: SpecType::Js(JsSpecType::DedicatedWorker),
},
test_entry: TestEntry {
r#type: TestEntryType::Js {
exec_scope: JsExecScope::DedicatedWorker
},
variant: None,
}
}
);

assert_eq!(
TestEntryPath::from_metadata_test(
Browser::Servo,
Path::new("tests/wpt/webgpu/meta/webgpu/do_the_thing.worker.js.ini"),
"do_the_thing.worker.html?foo=bar"
)
.unwrap(),
TestEntryPath {
spec_path: SpecPath {
root_dir: ServoRootDir::WebGpu.into(),
path: Utf8Path::new("webgpu/do_the_thing").into(),
r#type: SpecType::Js(JsSpecType::DedicatedWorker),
},
test_entry: TestEntry {
r#type: TestEntryType::Js {
exec_scope: JsExecScope::DedicatedWorker
},
variant: Some("?foo=bar".into()),
}
}
);
}

#[test]
Expand Down

0 comments on commit 30a494a

Please sign in to comment.