@@ -5,10 +5,11 @@ use std::{
55} ;
66
77use camino:: { Utf8Component , Utf8Path } ;
8-
98use clap:: ValueEnum ;
109use format:: lazy_format;
10+ use itertools:: Itertools ;
1111use joinery:: JoinableIterator ;
12+ use strum:: { EnumIter , IntoEnumIterator } ;
1213
1314/// A browser supported by [crate::main], used for [`TestEntryPath`]s.
1415#[ derive( Clone , Copy , Debug , Eq , Hash , Ord , PartialEq , PartialOrd , ValueEnum ) ]
@@ -63,8 +64,19 @@ pub(crate) struct SpecPath<'a> {
6364 pub r#type : SpecType ,
6465}
6566
67+ /// The type of tests that can be specified in a [`SpecPath`].
6668#[ derive( Clone , Debug , Eq , Hash , Ord , PartialEq , PartialOrd ) ]
6769pub ( crate ) enum SpecType {
70+ /// A JavaScript test.
71+ ///
72+ /// See also:
73+ ///
74+ /// * [WPT upstream docs.' "JavaScript Tests (`testharness.js`)" section][upstream] for
75+ /// background.
76+ /// * [`JsExecScope`], which will be set in test entries specified in a file with this type.
77+ ///
78+ /// [upstream]: https://web-platform-tests.org/writing-tests/testharness.html
79+ Js ( JsSpecType ) ,
6880 /// A catch-all for all `*.html` test spec. files. This is likely incorrect, but it works well
6981 /// enough for now!
7082 Html ,
@@ -73,7 +85,7 @@ pub(crate) enum SpecType {
7385
7486impl SpecType {
7587 fn iter ( ) -> impl Iterator < Item = Self > {
76- [ Self :: Html ] . into_iter ( )
88+ [ Self :: Html , Self :: Js ( JsSpecType :: DedicatedWorker ) ] . into_iter ( )
7789 }
7890
7991 pub fn from_base_name ( base_name : & str ) -> Option < ( Self , & str ) > {
@@ -82,12 +94,43 @@ impl SpecType {
8294 } )
8395 }
8496
97+ pub fn validate_test_entry_base_name < ' a > (
98+ & self ,
99+ base_name : & ' a str ,
100+ ) -> Option < ( TestEntryType , & ' a str ) > {
101+ let permitted_test_entry_types = match self {
102+ Self :: Js ( JsSpecType :: DedicatedWorker ) => & [ TestEntryType :: Js {
103+ exec_scope : JsExecScope :: DedicatedWorker ,
104+ } ] ,
105+ Self :: Html => & [ TestEntryType :: Html ] ,
106+ } ;
107+ permitted_test_entry_types
108+ . iter ( )
109+ . copied ( )
110+ . find_map ( |test_entry_type| {
111+ strip_suffix_with_value (
112+ base_name,
113+ test_entry_type. file_extension ( ) ,
114+ test_entry_type,
115+ )
116+ } )
117+ }
118+
85119 pub fn file_extension ( & self ) -> & ' static str {
86120 match self {
121+ SpecType :: Js ( JsSpecType :: DedicatedWorker ) => ".worker.js" ,
87122 SpecType :: Html => ".html" ,
88123 }
89124 }
90125}
126+
127+ /// A subtype of [`SpecType::Js`].
128+ #[ derive( Clone , Debug , Eq , Hash , Ord , PartialEq , PartialOrd ) ]
129+ pub ( crate ) enum JsSpecType {
130+ /// A `*.worker.js` test.
131+ DedicatedWorker ,
132+ }
133+
91134/// A symbolic path to an executed WPT test entry and its metadata, contained in a test
92135/// specification (see also [`SpecPath`]). In combination with [`SpecPath`], this is useful for
93136/// correlating entries from [`ExecutionReport`]s and [`metadata::File`]s.
@@ -96,6 +139,8 @@ impl SpecType {
96139/// [`metadata::File`]: crate::wpt::metadata::File
97140#[ derive( Clone , Debug , Eq , Hash , Ord , PartialEq , PartialOrd ) ]
98141pub ( crate ) struct TestEntry < ' a > {
142+ /// The type of this entry. Based on it's spec. file's type (see [`SpecPath::type`]).
143+ pub r#type : TestEntryType ,
99144 /// The variant of this particular test from this test's source code. If set, you should be
100145 /// able to correlate this with
101146 ///
@@ -105,6 +150,69 @@ pub(crate) struct TestEntry<'a> {
105150 pub variant : Option < Cow < ' a , str > > ,
106151}
107152
153+ /// The test entry analogue to [`SpecPath::type`].
154+ #[ derive( Clone , Copy , Debug , Eq , Hash , Ord , PartialEq , PartialOrd ) ]
155+ pub ( crate ) enum TestEntryType {
156+ /// An HTML-authored test with no divergence from the base name of its corresponding spec.
157+ /// file. Corresponds to [`SpecType::Html`].
158+ Html ,
159+ /// Corresponds to [`SpecType::Js`]. The test entry will have slightly different naming from
160+ /// its spec. file.
161+ ///
162+ /// JS tests are converted to `*.html` tests at test execution time and reported as such.
163+ /// The set of values observable here are determined by this entry's spec.'s
164+ /// [`SpecPath::type`] and its
165+ ///
166+ /// See also [WPT upstream's docs.' "Test Features" section][upstream]
167+ ///
168+ /// [upstream]: https://web-platform-tests.org/writing-tests/file-names.html#test-features
169+ Js { exec_scope : JsExecScope } ,
170+ }
171+
172+ impl TestEntryType {
173+ fn iter ( ) -> impl Iterator < Item = Self > {
174+ // NOTE: `Html`'s file extension is less specific than other file extensions, so try
175+ // matching it last.
176+ JsExecScope :: iter ( )
177+ . map ( |exec_scope| Self :: Js { exec_scope } )
178+ . chain ( [ Self :: Html ] )
179+ }
180+
181+ pub fn from_base_name ( base_name : & str ) -> Option < ( Self , & str ) > {
182+ Self :: iter ( ) . find_map ( |variant| {
183+ strip_suffix_with_value ( base_name, variant. file_extension ( ) , variant)
184+ } )
185+ }
186+
187+ pub fn file_extension ( self ) -> & ' static str {
188+ match self {
189+ Self :: Html => ".html" ,
190+ Self :: Js { exec_scope } => match exec_scope {
191+ JsExecScope :: DedicatedWorker => ".worker.html" ,
192+ } ,
193+ }
194+ }
195+
196+ pub fn spec_type ( self ) -> SpecType {
197+ match self {
198+ Self :: Html => SpecType :: Html ,
199+ Self :: Js { exec_scope } => match exec_scope {
200+ JsExecScope :: DedicatedWorker => SpecType :: Js ( JsSpecType :: DedicatedWorker ) ,
201+ } ,
202+ }
203+ }
204+ }
205+
206+ /// An executed JS test entry's test type, viz.,
207+ #[ derive( Clone , Copy , Debug , EnumIter , Eq , Hash , Ord , PartialEq , PartialOrd ) ]
208+ pub ( crate ) enum JsExecScope {
209+ /// A `*.worker.js` test. See also [WPT upstream docs.' "Dedicated worker test (`.worker.js`)"
210+ /// section][upstream].
211+ ///
212+ /// [upstream]: https://web-platform-tests.org/writing-tests/testharness.html#dedicated-worker-tests-worker-js
213+ DedicatedWorker ,
214+ }
215+
108216const ROOT_DIR_FX_MOZILLA_STR : & str = "testing/web-platform/mozilla" ;
109217const ROOT_DIR_FX_MOZILLA_COMPONENTS : & [ & str ] = & [ "testing" , "web-platform" , "mozilla" ] ;
110218const ROOT_DIR_FX_UPSTREAM_STR : & str = "testing/web-platform" ;
@@ -144,19 +252,22 @@ impl<'a> TestEntryPath<'a> {
144252 None => return Err ( err ( ) ) ,
145253 } ;
146254
147- let ( spec_type, path) = if let Some ( path) = path. strip_suffix ( ".html" ) {
148- ( SpecType :: Html , path)
149- } else {
150- return Err ( err ( ) ) ;
151- } ;
255+ let mut path = Cow :: < ' _ , Utf8Path > :: from ( Utf8Path :: new ( path) ) ;
256+
257+ let ( test_entry_type, base_name) =
258+ TestEntryType :: from_base_name ( path. file_name ( ) . ok_or_else ( err) ?) . ok_or_else ( err) ?;
259+ let spec_type = test_entry_type. spec_type ( ) ;
260+
261+ path = path. with_file_name ( base_name) . into ( ) ;
152262
153263 Ok ( Self {
154264 spec_path : SpecPath {
155265 root_dir,
156- path : Utf8Path :: new ( path ) . into ( ) ,
266+ path,
157267 r#type : spec_type,
158268 } ,
159269 test_entry : TestEntry {
270+ r#type : test_entry_type,
160271 variant : variant. map ( Into :: into) ,
161272 } ,
162273 } )
@@ -198,9 +309,9 @@ impl<'a> TestEntryPath<'a> {
198309
199310 let ( base_name, variant) = Self :: split_test_base_name_from_variant ( test_name) ;
200311
201- let base_name = match spec_type {
202- SpecType :: Html => base_name . strip_suffix ( ".html" ) . ok_or_else ( err ) ? ,
203- } ;
312+ let ( js_exec_scope , base_name) = spec_type
313+ . validate_test_entry_base_name ( base_name )
314+ . ok_or_else ( err ) ? ;
204315
205316 if path. components ( ) . next_back ( ) != Some ( Utf8Component :: Normal ( base_name) ) {
206317 return Err ( err ( ) ) ;
@@ -213,6 +324,7 @@ impl<'a> TestEntryPath<'a> {
213324 r#type : spec_type,
214325 } ,
215326 test_entry : TestEntry {
327+ r#type : js_exec_scope,
216328 variant : variant. map ( Into :: into) ,
217329 } ,
218330 } )
@@ -236,7 +348,11 @@ impl<'a> TestEntryPath<'a> {
236348 path,
237349 r#type,
238350 } ,
239- test_entry : TestEntry { variant } ,
351+ test_entry :
352+ TestEntry {
353+ r#type : js_exec_scope,
354+ variant,
355+ } ,
240356 } = self ;
241357
242358 TestEntryPath {
@@ -246,6 +362,7 @@ impl<'a> TestEntryPath<'a> {
246362 r#type,
247363 } ,
248364 test_entry : TestEntry {
365+ r#type : js_exec_scope,
249366 variant : variant. clone ( ) . map ( |v| v. into_owned ( ) . into ( ) ) ,
250367 } ,
251368 }
@@ -257,12 +374,16 @@ impl<'a> TestEntryPath<'a> {
257374 SpecPath {
258375 root_dir : _,
259376 path,
260- r#type,
377+ r#type : _,
378+ } ,
379+ test_entry :
380+ TestEntry {
381+ r#type : js_exec_scope,
382+ variant,
261383 } ,
262- test_entry : TestEntry { variant } ,
263384 } = self ;
264385 let base_name = path. file_name ( ) . unwrap ( ) ;
265- let file_extension = r#type . file_extension ( ) ;
386+ let file_extension = js_exec_scope . file_extension ( ) ;
266387
267388 lazy_format ! ( move |f| {
268389 write!( f, "{base_name}{file_extension}" ) ?;
@@ -279,23 +400,17 @@ impl<'a> TestEntryPath<'a> {
279400 SpecPath {
280401 root_dir,
281402 path,
282- r#type,
403+ r#type : _ ,
283404 } ,
284- test_entry : TestEntry { variant } ,
405+ test_entry : _ ,
285406 } = self ;
286- lazy_format ! ( move |f| {
287- write!(
288- f,
289- "{}{}{}" ,
290- root_dir. url_prefix( ) ,
291- path. components( ) . join_with( '/' ) ,
292- r#type. file_extension( )
293- ) ?;
294- if let Some ( variant) = variant. as_ref( ) {
295- write!( f, "{}" , variant) ?;
296- }
297- Ok ( ( ) )
298- } )
407+ lazy_format ! ( move |f| write!(
408+ f,
409+ "{}{}/{}" ,
410+ root_dir. url_prefix( ) ,
411+ path. components( ) . dropping_back( 1 ) . join_with( '/' ) ,
412+ self . test_name( ) ,
413+ ) )
299414 }
300415
301416 pub ( crate ) fn rel_metadata_path ( & self ) -> impl Display + ' _ {
@@ -306,7 +421,11 @@ impl<'a> TestEntryPath<'a> {
306421 path,
307422 r#type,
308423 } ,
309- test_entry : TestEntry { variant : _ } ,
424+ test_entry :
425+ TestEntry {
426+ r#type : _,
427+ variant : _,
428+ } ,
310429 } = self ;
311430
312431 let root_dir_dir = root_dir
@@ -450,6 +569,7 @@ fn parse_test_entry_path() {
450569 r#type: SpecType :: Html ,
451570 } ,
452571 test_entry: TestEntry {
572+ r#type: TestEntryType :: Html ,
453573 variant: Some ( "?stuff=things" . into( ) ) ,
454574 }
455575 }
@@ -468,7 +588,10 @@ fn parse_test_entry_path() {
468588 path: Utf8Path :: new( "stuff/things/cts.https" ) . into( ) ,
469589 r#type: SpecType :: Html ,
470590 } ,
471- test_entry: TestEntry { variant: None }
591+ test_entry: TestEntry {
592+ r#type: TestEntryType :: Html ,
593+ variant: None
594+ }
472595 }
473596 ) ;
474597
@@ -486,10 +609,55 @@ fn parse_test_entry_path() {
486609 r#type: SpecType :: Html ,
487610 } ,
488611 test_entry: TestEntry {
612+ r#type: TestEntryType :: Html ,
489613 variant: Some ( "?stuff=things" . into( ) ) ,
490614 }
491615 }
492616 ) ;
617+
618+ assert_eq ! (
619+ TestEntryPath :: from_metadata_test(
620+ Browser :: Servo ,
621+ Path :: new( "tests/wpt/webgpu/meta/webgpu/do_the_thing.worker.js.ini" ) ,
622+ "do_the_thing.worker.html"
623+ )
624+ . unwrap( ) ,
625+ TestEntryPath {
626+ spec_path: SpecPath {
627+ root_dir: ServoRootDir :: WebGpu . into( ) ,
628+ path: Utf8Path :: new( "webgpu/do_the_thing" ) . into( ) ,
629+ r#type: SpecType :: Js ( JsSpecType :: DedicatedWorker ) ,
630+ } ,
631+ test_entry: TestEntry {
632+ r#type: TestEntryType :: Js {
633+ exec_scope: JsExecScope :: DedicatedWorker
634+ } ,
635+ variant: None ,
636+ }
637+ }
638+ ) ;
639+
640+ assert_eq ! (
641+ TestEntryPath :: from_metadata_test(
642+ Browser :: Servo ,
643+ Path :: new( "tests/wpt/webgpu/meta/webgpu/do_the_thing.worker.js.ini" ) ,
644+ "do_the_thing.worker.html?foo=bar"
645+ )
646+ . unwrap( ) ,
647+ TestEntryPath {
648+ spec_path: SpecPath {
649+ root_dir: ServoRootDir :: WebGpu . into( ) ,
650+ path: Utf8Path :: new( "webgpu/do_the_thing" ) . into( ) ,
651+ r#type: SpecType :: Js ( JsSpecType :: DedicatedWorker ) ,
652+ } ,
653+ test_entry: TestEntry {
654+ r#type: TestEntryType :: Js {
655+ exec_scope: JsExecScope :: DedicatedWorker
656+ } ,
657+ variant: Some ( "?foo=bar" . into( ) ) ,
658+ }
659+ }
660+ ) ;
493661}
494662
495663#[ test]
0 commit comments