Skip to content

Commit ff3e950

Browse files
authored
Add feature flags to conformance tests based on test type (#393)
1 parent 8b6acd8 commit ff3e950

File tree

3 files changed

+139
-64
lines changed

3 files changed

+139
-64
lines changed

partiql-conformance-test-generator/src/generator.rs

Lines changed: 97 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ impl Generator {
168168
let mut module = Module::new(&mod_name);
169169
module.attr("allow(unused_imports)");
170170
module.attr("allow(clippy::module_inception)");
171+
if self.curr_path.iter().any(|s| s.contains("experimental")) {
172+
module.attr(r#"cfg(feature = "experimental")"#);
173+
}
171174
module.import("super", "*");
172175

173176
self.push_scope(Some(mod_name.clone()));
@@ -194,6 +197,9 @@ impl Generator {
194197
let mut module = Module::new(&mod_name.escape_module_name());
195198
module.attr("allow(unused_imports)");
196199
module.attr("allow(clippy::module_inception)");
200+
if self.curr_path.iter().any(|s| s.contains("experimental")) {
201+
module.attr(r#"cfg(feature = "experimental")"#);
202+
}
197203
module.import("super", "*");
198204

199205
self.push_scope(Some(mod_name.clone()));
@@ -216,6 +222,10 @@ impl Generator {
216222
let module = scope.new_module(&mod_name.escape_module_name());
217223
module.attr("allow(unused_imports)");
218224
module.attr("allow(clippy::module_inception)");
225+
226+
if self.curr_path.iter().any(|s| s.contains("experimental")) {
227+
module.attr(r#"cfg(feature = "experimental")"#);
228+
}
219229
module.import("super", "*");
220230

221231
self.push_scope(Some(mod_name));
@@ -232,6 +242,10 @@ impl Generator {
232242
let module = scope.new_module(&mod_name.escape_module_name());
233243
module.attr("allow(unused_imports)");
234244
module.attr("allow(clippy::module_inception)");
245+
246+
if self.curr_path.iter().any(|s| s.contains("experimental")) {
247+
module.attr(r#"cfg(feature = "experimental")"#);
248+
}
235249
module.import("super", "*");
236250

237251
self.push_scope(Some(mod_name));
@@ -328,6 +342,9 @@ impl Generator {
328342
let module = scope.new_module(&mod_name.escape_module_name());
329343
module.attr("allow(unused_imports)");
330344
module.import("super", "*");
345+
if self.curr_path.iter().any(|s| s.contains("experimental")) {
346+
module.attr(r#"cfg(feature = "experimental")"#);
347+
}
331348

332349
self.push_scope(Some(mod_name.clone()));
333350
self.gen_variants(module.scope(), &namespace.contents);
@@ -362,13 +379,16 @@ impl Generator {
362379
}
363380
}
364381

365-
fn create_test<'a>(
382+
fn create_test<F>(
366383
&mut self,
367-
scope: &'a mut Scope,
384+
scope: &mut Scope,
368385
test_case: &TestCase,
369386
name_prefix: Option<&str>,
370387
needs_env: bool,
371-
) -> &'a mut Function {
388+
mut body: F,
389+
) where
390+
F: FnMut(&mut Function),
391+
{
372392
let bare_name = &test_case.name;
373393
let prefixed_name = if let Some(prefix) = name_prefix {
374394
format!("{prefix}_{bare_name}")
@@ -398,10 +418,9 @@ impl Generator {
398418
} else {
399419
quote! {}
400420
};
401-
402421
test_fn.line(escape_fn_code(env));
403422

404-
test_fn
423+
body(test_fn)
405424
}
406425

407426
fn write_aside_expected(&mut self, test_case: &TestCase, expected: String) -> (String, bool) {
@@ -439,90 +458,105 @@ impl Generator {
439458
let test_case_expr =
440459
|gen: &dyn Fn(&str) -> _| stmts.iter().map(|s| gen(s)).collect::<Vec<_>>();
441460

442-
fn mode_data(eval_mode: &EvaluationMode) -> (&'static str, TokenStream) {
461+
fn mode_data(eval_mode: &EvaluationMode) -> (&'static str, &'static str, TokenStream) {
443462
match eval_mode {
444-
EvaluationMode::EvalModeError => ("strict", quote! { EvaluationMode::Error }),
445-
EvaluationMode::EvalModeCoerce => ("permissive", quote! { EvaluationMode::Coerce }),
463+
EvaluationMode::EvalModeError => {
464+
("strict", "strict", quote! { EvaluationMode::Error })
465+
}
466+
EvaluationMode::EvalModeCoerce => (
467+
"permissive",
468+
"permissive",
469+
quote! { EvaluationMode::Coerce },
470+
),
446471
}
447472
}
448473

449474
for assertion in &test_case.assert {
450475
match assertion {
451476
Assertion::SyntaxSuccess(_) => {
452-
let test_fn = self.create_test(scope, test_case, None, false);
453-
let stmts = test_case_expr(&|stmt: &str| quote! {pass_syntax(#stmt);});
454-
test_fn.line(escape_fn_code(quote! {
455-
#(#stmts)*
456-
}));
477+
self.create_test(scope, test_case, None, false, |test_fn| {
478+
test_fn.attr(r#"cfg(feature = "syntax")"#);
479+
let stmts = test_case_expr(&|stmt: &str| quote! {pass_syntax(#stmt);});
480+
test_fn.line(escape_fn_code(quote! {
481+
#(#stmts)*
482+
}));
483+
})
457484
}
458485
Assertion::SyntaxFail(_) => {
459-
let test_fn = self.create_test(scope, test_case, None, false);
460-
let stmts = test_case_expr(&|stmt: &str| quote! {fail_syntax(#stmt);});
461-
test_fn.line(escape_fn_code(quote! {
462-
#(#stmts)*
463-
}));
486+
self.create_test(scope, test_case, None, false, |test_fn| {
487+
test_fn.attr(r#"cfg(feature = "syntax")"#);
488+
let stmts = test_case_expr(&|stmt: &str| quote! {fail_syntax(#stmt);});
489+
test_fn.line(escape_fn_code(quote! {
490+
#(#stmts)*
491+
}));
492+
})
464493
}
465494
Assertion::StaticAnalysisFail(_) => {
466-
let test_fn = self.create_test(scope, test_case, None, false);
467-
468-
let stmts = test_case_expr(&|stmt: &str| quote! {fail_semantics(#stmt);});
469-
test_fn.line(escape_fn_code(quote! {
470-
#(#stmts)*
471-
}));
495+
self.create_test(scope, test_case, None, false, |test_fn| {
496+
test_fn.attr(r#"cfg(feature = "semantics")"#);
497+
let stmts = test_case_expr(&|stmt: &str| quote! {fail_semantics(#stmt);});
498+
test_fn.line(escape_fn_code(quote! {
499+
#(#stmts)*
500+
}));
501+
})
472502
}
473503
Assertion::EvaluationSuccess(EvaluationSuccessAssertion {
474504
output,
475505
eval_mode,
476506
..
477507
}) => {
478508
for mode in eval_mode {
479-
let (prefix, mode) = mode_data(mode);
480-
481-
let test_fn = self.create_test(scope, test_case, Some(prefix), true);
482-
test_fn.line("\n//**** evaluation success test case(s) ****//");
483-
509+
let (prefix, feature, mode) = mode_data(mode);
484510
let (expected, is_file) =
485511
self.write_aside_expected(test_case, elt_to_string(output));
486-
let expected = if is_file {
487-
quote! {include_str!(#expected)}
488-
} else {
489-
quote! {#expected}
490-
};
491-
492-
// emit asserts for all statements
493-
let stmts = test_case_expr(&|stmt: &str| {
494-
// emit PartiQL statement and evaluation mode assert
495-
quote! {
496-
let stmt = #stmt;
497-
pass_eval(stmt, #mode, &env, &expected);
498-
}
499-
});
500512

501-
test_fn.line(escape_fn_code(quote! {
502-
let expected = #expected.into();
503-
#(#stmts)*
504-
}));
513+
self.create_test(scope, test_case, Some(prefix), true, |test_fn| {
514+
test_fn.attr(&format!(r#"cfg(feature = "{feature}")"#));
515+
test_fn.line("\n//**** evaluation success test case(s) ****//");
516+
517+
let expected = if is_file {
518+
quote! {include_str!(#expected)}
519+
} else {
520+
quote! {#expected}
521+
};
522+
523+
// emit asserts for all statements
524+
let stmts = test_case_expr(&|stmt: &str| {
525+
// emit PartiQL statement and evaluation mode assert
526+
quote! {
527+
let stmt = #stmt;
528+
pass_eval(stmt, #mode, &env, &expected);
529+
}
530+
});
531+
532+
test_fn.line(escape_fn_code(quote! {
533+
let expected = #expected.into();
534+
#(#stmts)*
535+
}));
536+
});
505537
}
506538
}
507539
Assertion::EvaluationFail(EvaluationFailAssertion { eval_mode, .. }) => {
508540
for mode in eval_mode {
509-
let (prefix, mode) = mode_data(mode);
510-
511-
let test_fn = self.create_test(scope, test_case, Some(prefix), true);
512-
test_fn.line("\n//**** evaluation failure test case(s) ****//");
513-
514-
// emit asserts for all statements
515-
let stmts = test_case_expr(&|stmt: &str| {
516-
// emit PartiQL statement and evaluation mode assert
517-
quote! {
518-
let stmt = #stmt;
519-
fail_eval(stmt, #mode, &env);
520-
}
541+
let (prefix, feature, mode) = mode_data(mode);
542+
543+
self.create_test(scope, test_case, Some(prefix), true, |test_fn| {
544+
test_fn.attr(&format!(r#"cfg(feature = "{feature}")"#));
545+
test_fn.line("\n//**** evaluation failure test case(s) ****//");
546+
547+
// emit asserts for all statements
548+
let stmts = test_case_expr(&|stmt: &str| {
549+
// emit PartiQL statement and evaluation mode assert
550+
quote! {
551+
let stmt = #stmt;
552+
fail_eval(stmt, #mode, &env);
553+
}
554+
});
555+
556+
test_fn.line(escape_fn_code(quote! {
557+
#(#stmts)*
558+
}));
521559
});
522-
523-
test_fn.line(escape_fn_code(quote! {
524-
#(#stmts)*
525-
}));
526560
}
527561
}
528562
}

partiql-conformance-tests/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@ serde = { version = "1.*", features = ["derive"], optional = true }
5353
serde_json = { version = "1.*", optional = true }
5454

5555
[features]
56-
default = []
56+
default = ["base"]
57+
base = ["syntax", "semantics", "strict", "permissive"]
58+
syntax=[]
59+
semantics=[]
60+
strict=[]
61+
permissive=[]
62+
experimental=[]
5763
conformance_test=[]
5864
report_tool = ["serde"]
5965
serde = ["dep:serde", "dep:serde_json"]

partiql-conformance-tests/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,41 @@ cargo test --package partiql-conformance-tests --features "conformance_test"
1616

1717
---
1818

19+
Conformance tests are generated from the [PartiQL Test Data](partiql-tests/README.md).
20+
21+
### Default Tests
22+
The default tests can be run with:
23+
```shell
24+
cargo test --package partiql-conformance-tests --features "conformance_test"
25+
```
26+
27+
Which is equivalent to
28+
```shell
29+
cargo test --package partiql-conformance-tests --no-default-features --features "base,conformance_test"
30+
```
31+
32+
### Test Categories
33+
It is also possible to run subsets of the tests. See the set of test categories in [Cargo.toml](Cargo.toml)
34+
35+
To run only `semantic` analysis tests:
36+
```shell
37+
cargo test --package partiql-conformance-tests --no-default-features --features "semantic,conformance_test"
38+
```
39+
40+
41+
To run only `strict` tests:
42+
```shell
43+
cargo test --package partiql-conformance-tests --no-default-features --features "strict,conformance_test"
44+
```
45+
46+
To run `experimental` tests in addition to all default tests:
47+
```shell
48+
cargo test --package partiql-conformance-tests --features "experimental, conformance_test"
49+
```
50+
51+
52+
### Individual Tests
53+
1954
Running an individual test (or subset of tests) can vary by the IDE being used. Using CLion, you may need to first edit
2055
the test run configuration and enable the "Use all features in test" checkbox or explicitly add the
2156
`--features "conformance_test"` test option.

0 commit comments

Comments
 (0)