|
1 | | -macro_rules! spec_tests { |
2 | | - ($name:ident, $desc:expr) => { |
3 | | - mod $name { |
| 1 | +use crate::spec::testcase::SpecTestCase; |
| 2 | +use crate::spec::testcase::TestSuite; |
| 3 | +use crate::spec::testcase::PurlOrString; |
| 4 | +use std::path::Path; |
| 5 | +use std::fs; |
| 6 | +use std::borrow::Cow; |
| 7 | +use serde_json; |
| 8 | +use packageurl::PackageUrl; |
| 9 | +use std::str::FromStr; |
4 | 10 |
|
5 | | - use super::testcase::SpecTestCase; |
6 | | - use packageurl::PackageUrl; |
7 | | - use std::borrow::Cow; |
8 | | - use std::str::FromStr; |
9 | | - use std::sync::LazyLock; |
10 | 11 |
|
11 | | - static TEST_CASE: LazyLock<SpecTestCase<'static>> = |
12 | | - LazyLock::new(|| SpecTestCase::new($desc)); |
| 12 | +pub fn run_parse_test(case: &SpecTestCase) { |
| 13 | + if let PurlOrString::String(input) = &case.input { |
| 14 | + if let Ok(purl) = PackageUrl::from_str(input) { |
| 15 | + assert!(!case.expected_failure, "Expected failure: but parsing succeeded for PURL: {}", input); |
13 | 16 |
|
14 | | - #[test] |
15 | | - fn purl_to_components() { |
16 | | - if let Ok(purl) = PackageUrl::from_str(&TEST_CASE.purl) { |
17 | | - assert!(!TEST_CASE.is_invalid); |
18 | | - assert_eq!(TEST_CASE.ty.as_ref().unwrap().as_ref(), purl.ty()); |
19 | | - assert_eq!(TEST_CASE.name.as_ref().unwrap().as_ref(), purl.name()); |
20 | | - assert_eq!( |
21 | | - TEST_CASE.namespace.as_ref().map(Cow::as_ref), |
22 | | - purl.namespace() |
23 | | - ); |
24 | | - assert_eq!(TEST_CASE.version.as_ref().map(Cow::as_ref), purl.version()); |
25 | | - assert_eq!(TEST_CASE.subpath.as_ref().map(Cow::as_ref), purl.subpath()); |
26 | | - if let Some(ref quals) = TEST_CASE.qualifiers { |
27 | | - assert_eq!(quals, purl.qualifiers()); |
28 | | - } else { |
29 | | - assert!(purl.qualifiers().is_empty()); |
30 | | - } |
| 17 | + if let Some(PurlOrString::PurlComponent(expected)) = &case.expected_output { |
| 18 | + assert_eq!(Some(purl.ty()), expected.ty.as_ref().map(Cow::as_ref)); |
| 19 | + assert_eq!(Some(purl.name()), expected.name.as_ref().map(Cow::as_ref)); |
| 20 | + assert_eq!(purl.namespace(), expected.namespace.as_ref().map(Cow::as_ref)); |
| 21 | + assert_eq!(purl.version(), expected.version.as_ref().map(Cow::as_ref)); |
| 22 | + assert_eq!(purl.subpath(), expected.subpath.as_ref().map(Cow::as_ref)); |
| 23 | + |
| 24 | + if let Some(ref expected_quals) = expected.qualifiers { |
| 25 | + assert_eq!(purl.qualifiers(), expected_quals); |
31 | 26 | } else { |
32 | | - assert!(TEST_CASE.is_invalid); |
| 27 | + assert!(purl.qualifiers().is_empty()); |
33 | 28 | } |
| 29 | + } else { |
| 30 | + panic!("Expected PurlComponent as expected_output for: {}", case.description); |
34 | 31 | } |
| 32 | + } else { |
| 33 | + assert!(case.expected_failure, "Unexpected parse failure: {}", case.description); |
| 34 | + } |
| 35 | + } |
| 36 | +} |
35 | 37 |
|
36 | | - #[test] |
37 | | - fn components_to_canonical() { |
38 | | - if TEST_CASE.is_invalid { |
39 | | - return; |
40 | | - } |
41 | 38 |
|
42 | | - let mut purl = PackageUrl::new( |
43 | | - TEST_CASE.ty.as_ref().unwrap().clone(), |
44 | | - TEST_CASE.name.as_ref().unwrap().clone(), |
45 | | - ) |
46 | | - .unwrap(); |
| 39 | +pub fn run_build_test(case: &SpecTestCase) { |
| 40 | + let PurlOrString::PurlComponent(ref input) = case.input else { |
| 41 | + panic!("Expected PurlComponent as input for build test: {}", case.description); |
| 42 | + }; |
47 | 43 |
|
48 | | - if let Some(ref ns) = TEST_CASE.namespace { |
49 | | - purl.with_namespace(ns.as_ref()); |
50 | | - } |
| 44 | + if input.ty.is_none() || input.name.is_none() { |
| 45 | + assert!(case.expected_failure, "Missing type or name, but test not marked as failure: {}", case.description); |
| 46 | + return; |
| 47 | + } |
51 | 48 |
|
52 | | - if let Some(ref v) = TEST_CASE.version { |
53 | | - purl.with_version(v.as_ref()); |
54 | | - } |
| 49 | + let ty = input.ty.as_ref().unwrap().as_ref(); |
| 50 | + let name = input.name.as_ref().unwrap().as_ref(); |
55 | 51 |
|
56 | | - if let Some(ref sp) = TEST_CASE.subpath { |
57 | | - purl.with_subpath(sp.as_ref()).unwrap(); |
58 | | - } |
| 52 | + let purl_result = PackageUrl::new(ty, name); |
59 | 53 |
|
60 | | - if let Some(ref quals) = TEST_CASE.qualifiers { |
61 | | - for (k, v) in quals.iter() { |
62 | | - purl.add_qualifier(k.as_ref(), v.as_ref()).unwrap(); |
63 | | - } |
64 | | - } |
| 54 | + if purl_result.is_err() { |
| 55 | + assert!(case.expected_failure, "Purl build failed: {}", case.description); |
| 56 | + return; |
| 57 | + } |
| 58 | + |
| 59 | + let mut purl = purl_result.unwrap(); |
| 60 | + if let Some(ref ns) = input.namespace { |
| 61 | + purl.with_namespace(ns.as_ref()); |
| 62 | + } |
| 63 | + |
| 64 | + if let Some(ref v) = input.version { |
| 65 | + purl.with_version(v.as_ref()); |
| 66 | + } |
| 67 | + |
| 68 | + if let Some(ref sp) = input.subpath { |
| 69 | + purl.with_subpath(sp.as_ref()).unwrap(); |
| 70 | + } |
65 | 71 |
|
66 | | - assert_eq!( |
67 | | - TEST_CASE.canonical_purl.as_ref().unwrap(), |
68 | | - &purl.to_string() |
69 | | - ); |
| 72 | + if let Some(ref quals) = input.qualifiers { |
| 73 | + for (k, v) in quals.iter() { |
| 74 | + if let Err(_) = purl.add_qualifier(k.as_ref(), v.as_ref()) { |
| 75 | + assert!(case.expected_failure, "add_qualifier failed unexpectedly"); |
| 76 | + return; |
70 | 77 | } |
| 78 | + } |
| 79 | + } |
71 | 80 |
|
72 | | - #[test] |
73 | | - fn canonical_to_canonical() { |
74 | | - if TEST_CASE.is_invalid { |
75 | | - return; |
76 | | - } |
| 81 | + assert!(!case.expected_failure, "Test was expected to fail but succeeded: {}", case.description); |
| 82 | + if let Some(PurlOrString::String(expected)) = &case.expected_output { |
| 83 | + assert_eq!(&purl.to_string(), expected); |
| 84 | + } else { |
| 85 | + panic!("Expected String as expected_output for build test: {}", case.description); |
| 86 | + } |
| 87 | +} |
77 | 88 |
|
78 | | - let purl = |
79 | | - PackageUrl::from_str(&TEST_CASE.canonical_purl.as_ref().unwrap()).unwrap(); |
80 | | - assert_eq!( |
81 | | - TEST_CASE.canonical_purl.as_ref().unwrap(), |
82 | | - &purl.to_string() |
83 | | - ); |
| 89 | +pub fn run_roundtrip_test(case: &SpecTestCase) { |
| 90 | + let input = match &case.input { |
| 91 | + PurlOrString::String(s) => s, |
| 92 | + _ => panic!("Input must be a string: {}", case.description), |
| 93 | + }; |
| 94 | + |
| 95 | + match PackageUrl::from_str(input) { |
| 96 | + Ok(purl) => { |
| 97 | + assert!(!case.expected_failure, "Test was expected to fail but succeeded: {}", case.description); |
| 98 | + if let Some(PurlOrString::String(expected)) = &case.expected_output { |
| 99 | + assert_eq!(&purl.to_string(), expected); |
84 | 100 | } |
| 101 | + } |
| 102 | + Err(_) => { |
| 103 | + assert!(case.expected_failure, "Failed to create PURL for: {}", input); |
| 104 | + } |
| 105 | + } |
| 106 | +} |
85 | 107 |
|
86 | | - #[test] |
87 | | - fn purl_to_canonical() { |
88 | | - if TEST_CASE.is_invalid { |
89 | | - return; |
90 | | - } |
91 | | - let purl = PackageUrl::from_str(&TEST_CASE.purl).unwrap(); |
92 | | - assert_eq!( |
93 | | - TEST_CASE.canonical_purl.as_ref().unwrap(), |
94 | | - &purl.to_string() |
95 | | - ) |
| 108 | + |
| 109 | +pub fn run_tests_from_spec(path: &Path) { |
| 110 | + let data = fs::read(path).expect("Failed to read test file"); |
| 111 | + let suite: TestSuite = serde_json::from_slice(&data).expect("Invalid test file"); |
| 112 | + |
| 113 | + for case in suite.tests { |
| 114 | + |
| 115 | + match case.test_type.as_ref() { |
| 116 | + "parse" => { |
| 117 | + run_parse_test(&case); |
| 118 | + } |
| 119 | + "build" => { |
| 120 | + run_build_test(&case); |
| 121 | + } |
| 122 | + "roundtrip" => { |
| 123 | + run_roundtrip_test(&case); |
| 124 | + } |
| 125 | + other => { |
| 126 | + println!("Unknown test type '{}', skipping: {}", other, case.description); |
96 | 127 | } |
97 | 128 | } |
| 129 | + } |
| 130 | +} |
| 131 | + |
| 132 | +#[macro_export] |
| 133 | +macro_rules! generate_json_tests { |
| 134 | + ($($test_name:ident => $file_path:expr),* $(,)?) => { |
| 135 | + $( |
| 136 | + #[test] |
| 137 | + fn $test_name() { |
| 138 | + crate::spec::macros::run_tests_from_spec(std::path::Path::new($file_path)); |
| 139 | + } |
| 140 | + )* |
98 | 141 | }; |
99 | 142 | } |
0 commit comments