diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index e3ecb00dd67a4..5c8dab5378fe0 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -320,20 +320,29 @@ mod tests { multi::MultiCompiler, solc::{Solc, SolcCompiler}, }; + use std::sync::OnceLock; + fn testdata() -> &'static Path { + static CACHE: OnceLock = OnceLock::new(); + CACHE.get_or_init(|| { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../testdata").canonicalize().unwrap() + }) + } + + #[must_use] struct LinkerTest { project: Project, output: ProjectCompileOutput, - dependency_assertions: HashMap>, + dependency_assertions: HashMap<&'static str, Vec<(&'static str, Address)>>, } impl LinkerTest { - fn new(path: impl Into, strip_prefixes: bool) -> Self { - let path = path.into(); + fn new(path: &Path, strip_prefixes: bool) -> Self { + assert!(path.exists(), "Path {path:?} does not exist"); let paths = ProjectPathsConfig::builder() - .root("../../testdata") - .lib("../../testdata/lib") - .sources(path.clone()) + .root(testdata()) + .lib(testdata().join("lib")) + .sources(path) .tests(path) .build() .unwrap(); @@ -357,10 +366,10 @@ mod tests { fn assert_dependencies( mut self, - artifact_id: String, - deps: Vec<(String, Address)>, + artifact_id: &'static str, + deps: &[(&'static str, Address)], ) -> Self { - self.dependency_assertions.insert(artifact_id, deps); + self.dependency_assertions.insert(artifact_id, deps.to_vec()); self } @@ -387,7 +396,8 @@ mod tests { fn iter_linking_targets<'a>( &'a self, linker: &'a Linker<'_>, - ) -> impl IntoIterator + 'a { + ) -> impl Iterator + 'a { + self.sanity_check(linker); linker.contracts.keys().filter_map(move |id| { // If we didn't strip paths, artifacts will have absolute paths. // That's expected and we want to ensure that only `libraries` object has relative @@ -409,12 +419,17 @@ mod tests { }) } + fn sanity_check(&self, linker: &Linker<'_>) { + assert!(!self.dependency_assertions.is_empty(), "Dependency assertions are empty"); + assert!(!linker.contracts.is_empty(), "Linker contracts are empty"); + } + fn validate_assertions(&self, identifier: String, output: LinkOutput) { let LinkOutput { libs_to_deploy, libraries } = output; let assertions = self .dependency_assertions - .get(&identifier) + .get(identifier.as_str()) .unwrap_or_else(|| panic!("Unexpected artifact: {identifier}")); assert_eq!( @@ -426,14 +441,14 @@ mod tests { libs_to_deploy ); - for (dep_identifier, address) in assertions { + for &(dep_identifier, address) in assertions { let (file, name) = dep_identifier.split_once(':').unwrap(); if let Some(lib_address) = libraries.libs.get(Path::new(file)).and_then(|libs| libs.get(name)) { assert_eq!( - *lib_address, - address.to_string(), + lib_address.parse::
().unwrap(), + address, "incorrect library address for dependency {dep_identifier} of {identifier}" ); } else { @@ -443,28 +458,48 @@ mod tests { } } - fn link_test(path: impl Into, test_fn: impl Fn(LinkerTest)) { - let path = path.into(); - test_fn(LinkerTest::new(path.clone(), true)); - test_fn(LinkerTest::new(path, false)); + fn link_test(path: impl AsRef, mut test_fn: impl FnMut(LinkerTest)) { + fn link_test(path: &Path, test_fn: &mut dyn FnMut(LinkerTest)) { + test_fn(LinkerTest::new(path, true)); + test_fn(LinkerTest::new(path, false)); + } + link_test(path.as_ref(), &mut test_fn); + } + + #[test] + #[should_panic = "assertions are empty"] + fn no_assertions() { + link_test(testdata().join("default/linking/simple"), |linker| { + linker.test_with_sender_and_nonce(Address::default(), 1); + }); + } + + #[test] + #[should_panic = "does not exist"] + fn unknown_path() { + link_test("doesnotexist", |linker| { + linker + .assert_dependencies("a:b", &[]) + .test_with_sender_and_nonce(Address::default(), 1); + }); } #[test] fn link_simple() { - link_test("../../testdata/default/linking/simple", |linker| { + link_test(testdata().join("default/linking/simple"), |linker| { linker - .assert_dependencies("default/linking/simple/Simple.t.sol:Lib".to_string(), vec![]) + .assert_dependencies("default/linking/simple/Simple.t.sol:Lib", &[]) .assert_dependencies( - "default/linking/simple/Simple.t.sol:LibraryConsumer".to_string(), - vec![( - "default/linking/simple/Simple.t.sol:Lib".to_string(), + "default/linking/simple/Simple.t.sol:LibraryConsumer", + &[( + "default/linking/simple/Simple.t.sol:Lib", address!("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), )], ) .assert_dependencies( - "default/linking/simple/Simple.t.sol:SimpleLibraryLinkingTest".to_string(), - vec![( - "default/linking/simple/Simple.t.sol:Lib".to_string(), + "default/linking/simple/Simple.t.sol:SimpleLibraryLinkingTest", + &[( + "default/linking/simple/Simple.t.sol:Lib", address!("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), )], ) @@ -474,43 +509,43 @@ mod tests { #[test] fn link_nested() { - link_test("../../testdata/default/linking/nested", |linker| { + link_test(testdata().join("default/linking/nested"), |linker| { linker - .assert_dependencies("default/linking/nested/Nested.t.sol:Lib".to_string(), vec![]) + .assert_dependencies("default/linking/nested/Nested.t.sol:Lib", &[]) .assert_dependencies( - "default/linking/nested/Nested.t.sol:NestedLib".to_string(), - vec![( - "default/linking/nested/Nested.t.sol:Lib".to_string(), + "default/linking/nested/Nested.t.sol:NestedLib", + &[( + "default/linking/nested/Nested.t.sol:Lib", address!("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), )], ) .assert_dependencies( - "default/linking/nested/Nested.t.sol:LibraryConsumer".to_string(), - vec![ + "default/linking/nested/Nested.t.sol:LibraryConsumer", + &[ // Lib shows up here twice, because the linker sees it twice, but it should // have the same address and nonce. ( - "default/linking/nested/Nested.t.sol:Lib".to_string(), + "default/linking/nested/Nested.t.sol:Lib", Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3") .unwrap(), ), ( - "default/linking/nested/Nested.t.sol:NestedLib".to_string(), + "default/linking/nested/Nested.t.sol:NestedLib", Address::from_str("0x47e9Fbef8C83A1714F1951F142132E6e90F5fa5D") .unwrap(), ), ], ) .assert_dependencies( - "default/linking/nested/Nested.t.sol:NestedLibraryLinkingTest".to_string(), - vec![ + "default/linking/nested/Nested.t.sol:NestedLibraryLinkingTest", + &[ ( - "default/linking/nested/Nested.t.sol:Lib".to_string(), + "default/linking/nested/Nested.t.sol:Lib", Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3") .unwrap(), ), ( - "default/linking/nested/Nested.t.sol:NestedLib".to_string(), + "default/linking/nested/Nested.t.sol:NestedLib", Address::from_str("0x47e9fbef8c83a1714f1951f142132e6e90f5fa5d") .unwrap(), ), @@ -522,101 +557,94 @@ mod tests { #[test] fn link_duplicate() { - link_test("../../testdata/default/linking/duplicate", |linker| { + link_test(testdata().join("default/linking/duplicate"), |linker| { linker + .assert_dependencies("default/linking/duplicate/Duplicate.t.sol:A", &[]) + .assert_dependencies("default/linking/duplicate/Duplicate.t.sol:B", &[]) .assert_dependencies( - "default/linking/duplicate/Duplicate.t.sol:A".to_string(), - vec![], - ) - .assert_dependencies( - "default/linking/duplicate/Duplicate.t.sol:B".to_string(), - vec![], - ) - .assert_dependencies( - "default/linking/duplicate/Duplicate.t.sol:C".to_string(), - vec![( - "default/linking/duplicate/Duplicate.t.sol:A".to_string(), + "default/linking/duplicate/Duplicate.t.sol:C", + &[( + "default/linking/duplicate/Duplicate.t.sol:A", address!("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), )], ) .assert_dependencies( - "default/linking/duplicate/Duplicate.t.sol:D".to_string(), - vec![( - "default/linking/duplicate/Duplicate.t.sol:B".to_string(), + "default/linking/duplicate/Duplicate.t.sol:D", + &[( + "default/linking/duplicate/Duplicate.t.sol:B", address!("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), )], ) .assert_dependencies( - "default/linking/duplicate/Duplicate.t.sol:E".to_string(), - vec![ + "default/linking/duplicate/Duplicate.t.sol:E", + &[ ( - "default/linking/duplicate/Duplicate.t.sol:A".to_string(), + "default/linking/duplicate/Duplicate.t.sol:A", Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3") .unwrap(), ), ( - "default/linking/duplicate/Duplicate.t.sol:C".to_string(), + "default/linking/duplicate/Duplicate.t.sol:C", Address::from_str("0x47e9fbef8c83a1714f1951f142132e6e90f5fa5d") .unwrap(), ), ], ) .assert_dependencies( - "default/linking/duplicate/Duplicate.t.sol:LibraryConsumer".to_string(), - vec![ + "default/linking/duplicate/Duplicate.t.sol:LibraryConsumer", + &[ ( - "default/linking/duplicate/Duplicate.t.sol:A".to_string(), + "default/linking/duplicate/Duplicate.t.sol:A", Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3") .unwrap(), ), ( - "default/linking/duplicate/Duplicate.t.sol:B".to_string(), + "default/linking/duplicate/Duplicate.t.sol:B", Address::from_str("0x47e9fbef8c83a1714f1951f142132e6e90f5fa5d") .unwrap(), ), ( - "default/linking/duplicate/Duplicate.t.sol:C".to_string(), + "default/linking/duplicate/Duplicate.t.sol:C", Address::from_str("0x8be503bcded90ed42eff31f56199399b2b0154ca") .unwrap(), ), ( - "default/linking/duplicate/Duplicate.t.sol:D".to_string(), + "default/linking/duplicate/Duplicate.t.sol:D", Address::from_str("0x47c5e40890bce4a473a49d7501808b9633f29782") .unwrap(), ), ( - "default/linking/duplicate/Duplicate.t.sol:E".to_string(), + "default/linking/duplicate/Duplicate.t.sol:E", Address::from_str("0x29b2440db4a256b0c1e6d3b4cdcaa68e2440a08f") .unwrap(), ), ], ) .assert_dependencies( - "default/linking/duplicate/Duplicate.t.sol:DuplicateLibraryLinkingTest" - .to_string(), - vec![ + "default/linking/duplicate/Duplicate.t.sol:DuplicateLibraryLinkingTest", + &[ ( - "default/linking/duplicate/Duplicate.t.sol:A".to_string(), + "default/linking/duplicate/Duplicate.t.sol:A", Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3") .unwrap(), ), ( - "default/linking/duplicate/Duplicate.t.sol:B".to_string(), + "default/linking/duplicate/Duplicate.t.sol:B", Address::from_str("0x47e9fbef8c83a1714f1951f142132e6e90f5fa5d") .unwrap(), ), ( - "default/linking/duplicate/Duplicate.t.sol:C".to_string(), + "default/linking/duplicate/Duplicate.t.sol:C", Address::from_str("0x8be503bcded90ed42eff31f56199399b2b0154ca") .unwrap(), ), ( - "default/linking/duplicate/Duplicate.t.sol:D".to_string(), + "default/linking/duplicate/Duplicate.t.sol:D", Address::from_str("0x47c5e40890bce4a473a49d7501808b9633f29782") .unwrap(), ), ( - "default/linking/duplicate/Duplicate.t.sol:E".to_string(), + "default/linking/duplicate/Duplicate.t.sol:E", Address::from_str("0x29b2440db4a256b0c1e6d3b4cdcaa68e2440a08f") .unwrap(), ), @@ -628,33 +656,33 @@ mod tests { #[test] fn link_cycle() { - link_test("../../testdata/default/linking/cycle", |linker| { + link_test(testdata().join("default/linking/cycle"), |linker| { linker .assert_dependencies( - "default/linking/cycle/Cycle.t.sol:Foo".to_string(), - vec![ + "default/linking/cycle/Cycle.t.sol:Foo", + &[ ( - "default/linking/cycle/Cycle.t.sol:Foo".to_string(), + "default/linking/cycle/Cycle.t.sol:Foo", Address::from_str("0x47e9Fbef8C83A1714F1951F142132E6e90F5fa5D") .unwrap(), ), ( - "default/linking/cycle/Cycle.t.sol:Bar".to_string(), + "default/linking/cycle/Cycle.t.sol:Bar", Address::from_str("0x5a443704dd4B594B382c22a083e2BD3090A6feF3") .unwrap(), ), ], ) .assert_dependencies( - "default/linking/cycle/Cycle.t.sol:Bar".to_string(), - vec![ + "default/linking/cycle/Cycle.t.sol:Bar", + &[ ( - "default/linking/cycle/Cycle.t.sol:Foo".to_string(), + "default/linking/cycle/Cycle.t.sol:Foo", Address::from_str("0x47e9Fbef8C83A1714F1951F142132E6e90F5fa5D") .unwrap(), ), ( - "default/linking/cycle/Cycle.t.sol:Bar".to_string(), + "default/linking/cycle/Cycle.t.sol:Bar", Address::from_str("0x5a443704dd4B594B382c22a083e2BD3090A6feF3") .unwrap(), ), @@ -666,43 +694,43 @@ mod tests { #[test] fn link_create2_nested() { - link_test("../../testdata/default/linking/nested", |linker| { + link_test(testdata().join("default/linking/nested"), |linker| { linker - .assert_dependencies("default/linking/nested/Nested.t.sol:Lib".to_string(), vec![]) + .assert_dependencies("default/linking/nested/Nested.t.sol:Lib", &[]) .assert_dependencies( - "default/linking/nested/Nested.t.sol:NestedLib".to_string(), - vec![( - "default/linking/nested/Nested.t.sol:Lib".to_string(), + "default/linking/nested/Nested.t.sol:NestedLib", + &[( + "default/linking/nested/Nested.t.sol:Lib", address!("0xddb1Cd2497000DAeA687CEa3dc34Af44084BEa74"), )], ) .assert_dependencies( - "default/linking/nested/Nested.t.sol:LibraryConsumer".to_string(), - vec![ + "default/linking/nested/Nested.t.sol:LibraryConsumer", + &[ // Lib shows up here twice, because the linker sees it twice, but it should // have the same address and nonce. ( - "default/linking/nested/Nested.t.sol:Lib".to_string(), + "default/linking/nested/Nested.t.sol:Lib", Address::from_str("0xddb1Cd2497000DAeA687CEa3dc34Af44084BEa74") .unwrap(), ), ( - "default/linking/nested/Nested.t.sol:NestedLib".to_string(), + "default/linking/nested/Nested.t.sol:NestedLib", Address::from_str("0xfebE2F30641170642f317Ff6F644Cee60E7Ac369") .unwrap(), ), ], ) .assert_dependencies( - "default/linking/nested/Nested.t.sol:NestedLibraryLinkingTest".to_string(), - vec![ + "default/linking/nested/Nested.t.sol:NestedLibraryLinkingTest", + &[ ( - "default/linking/nested/Nested.t.sol:Lib".to_string(), + "default/linking/nested/Nested.t.sol:Lib", Address::from_str("0xddb1Cd2497000DAeA687CEa3dc34Af44084BEa74") .unwrap(), ), ( - "default/linking/nested/Nested.t.sol:NestedLib".to_string(), + "default/linking/nested/Nested.t.sol:NestedLib", Address::from_str("0xfebE2F30641170642f317Ff6F644Cee60E7Ac369") .unwrap(), ), @@ -719,7 +747,7 @@ mod tests { #[test] fn linking_failure() { - let linker = LinkerTest::new("../../testdata/default/linking/simple", true); + let linker = LinkerTest::new(&testdata().join("default/linking/simple"), true); let linker_instance = Linker::new(linker.project.root(), linker.output.artifact_ids().collect());