[opentitanlib] Add testdata! macro

Some OT Tool tests use files located in a "testdata" directory. Add a
macro that can resolve this path in in the bazel build environment.

Signed-off-by: Jon Flatley <jflat@google.com>
diff --git a/sw/host/opentitanlib/src/otp/lc_state.rs b/sw/host/opentitanlib/src/otp/lc_state.rs
index 5592738..0f45c90 100644
--- a/sw/host/opentitanlib/src/otp/lc_state.rs
+++ b/sw/host/opentitanlib/src/otp/lc_state.rs
@@ -73,14 +73,14 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::otp::tests::testdata;
+    use crate::testdata;
     use anyhow::Result;
     use deser_hjson::from_str;
     use std::fs::read_to_string;
 
     #[test]
     fn test_lc_state_deserialize() -> Result<()> {
-        let _: LcState = from_str(&read_to_string(&testdata("lc_ctrl_state.hjson"))?)?;
+        let _: LcState = from_str(&read_to_string(&testdata!("lc_ctrl_state.hjson"))?)?;
         Ok(())
     }
 
diff --git a/sw/host/opentitanlib/src/otp/mod.rs b/sw/host/opentitanlib/src/otp/mod.rs
index 635dcdf..08570d1 100644
--- a/sw/host/opentitanlib/src/otp/mod.rs
+++ b/sw/host/opentitanlib/src/otp/mod.rs
@@ -13,24 +13,18 @@
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::testdata;
     use anyhow::Result;
-    use std::path::Path;
-
-    pub(crate) fn testdata(s: &str) -> String {
-        let mut result = "sw/host/opentitanlib/src/otp/testdata/".to_string();
-        result.push_str(s);
-        result
-    }
 
     #[test]
     fn test_vmem_serialize() -> Result<()> {
-        let mut otp_mmap = otp_mmap::OtpMap::new(Path::new(&testdata("otp_ctrl_mmap.hjson")))?;
-        let mut otp_img = otp_img::OtpImg::new(Path::new(&testdata("otp_ctrl_img_dev.hjson")))?;
-        let lc_state = lc_state::LcSecded::new(Path::new(&testdata("lc_ctrl_state.hjson")))?;
+        let mut otp_mmap = otp_mmap::OtpMap::new(&testdata!("otp_ctrl_mmap.hjson"))?;
+        let mut otp_img = otp_img::OtpImg::new(&testdata!("otp_ctrl_img_dev.hjson"))?;
+        let lc_state = lc_state::LcSecded::new(&testdata!("lc_ctrl_state.hjson"))?;
         let vmem = otp_mmap.make_vmem(&mut otp_img)?;
         let keys = otp_mmap.generate_keys(&otp_img);
         let result = vmem.generate(keys, &lc_state)?;
-        let expected = std::fs::read_to_string(Path::new(&testdata("output.vmem")))?;
+        let expected = std::fs::read_to_string(testdata!("output.vmem"))?;
         let expected = expected
             .split("\n")
             .filter(|s| !s.is_empty())
diff --git a/sw/host/opentitanlib/src/otp/otp_mmap.rs b/sw/host/opentitanlib/src/otp/otp_mmap.rs
index 0a6be49..1b9e68b 100644
--- a/sw/host/opentitanlib/src/otp/otp_mmap.rs
+++ b/sw/host/opentitanlib/src/otp/otp_mmap.rs
@@ -190,20 +190,20 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::otp::tests::testdata;
+    use crate::testdata;
     use std::fs::read_to_string;
 
     #[test]
     fn test_mmap_deserialize() {
         let _: OtpMap =
-            deser_hjson::from_str(&read_to_string(testdata("otp_ctrl_mmap.hjson")).unwrap())
+            deser_hjson::from_str(&read_to_string(testdata!("otp_ctrl_mmap.hjson")).unwrap())
                 .unwrap();
     }
 
     #[test]
     fn test_img_deserialize() {
         let _: OtpImg =
-            deser_hjson::from_str(&read_to_string(testdata("otp_ctrl_img_dev.hjson")).unwrap())
+            deser_hjson::from_str(&read_to_string(testdata!("otp_ctrl_img_dev.hjson")).unwrap())
                 .unwrap();
     }
 }
diff --git a/sw/host/opentitanlib/src/util/image.rs b/sw/host/opentitanlib/src/util/image.rs
index 71aba52..f20879d 100644
--- a/sw/host/opentitanlib/src/util/image.rs
+++ b/sw/host/opentitanlib/src/util/image.rs
@@ -101,18 +101,16 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-
-    fn testdata(s: &str) -> String {
-        let mut result = "sw/host/opentitanlib/src/util/testdata/".to_string();
-        result.push_str(s);
-        result
-    }
+    use crate::testdata;
 
     #[test]
     fn test_assemble_concat() -> Result<()> {
         // Test image assembly by concatenation.
         let mut image = ImageAssembler::with_params(16, false);
-        image.parse(&[testdata("hello.txt"), testdata("world.txt")])?;
+        image.parse(&[
+            testdata!("hello.txt").to_str().unwrap(),
+            testdata!("world.txt").to_str().unwrap(),
+        ])?;
         let data = image.assemble()?;
         assert_eq!(data, b"HelloWorld\xff\xff\xff\xff\xff\xff");
         Ok(())
@@ -122,7 +120,10 @@
     fn test_assemble_offset() -> Result<()> {
         // Test image assembly by explicit offsets.
         let mut image = ImageAssembler::with_params(16, false);
-        image.parse(&[testdata("hello.txt@0"), testdata("world.txt@0x8")])?;
+        image.parse(&[
+            testdata!("hello.txt@0").to_str().unwrap(),
+            testdata!("world.txt@0x8").to_str().unwrap(),
+        ])?;
         let data = image.assemble()?;
         assert_eq!(data, b"Hello\xff\xff\xffWorld\xff\xff\xff");
         Ok(())
@@ -132,7 +133,10 @@
     fn test_assemble_mirrored() -> Result<()> {
         // Test image assembly with mirroring.
         let mut image = ImageAssembler::with_params(20, true);
-        image.parse(&[testdata("hello.txt"), testdata("world.txt")])?;
+        image.parse(&[
+            testdata!("hello.txt").to_str().unwrap(),
+            testdata!("world.txt").to_str().unwrap(),
+        ])?;
         let data = image.assemble()?;
         assert_eq!(data, b"HelloWorldHelloWorld");
         Ok(())
@@ -142,7 +146,10 @@
     fn test_assemble_mirrored_offset_error() -> Result<()> {
         // Test image assembly where one of the source files isn't read completely.
         let mut image = ImageAssembler::with_params(16, true);
-        image.parse(&[testdata("hello.txt@0"), testdata("world.txt@0x5")])?;
+        image.parse(&[
+            testdata!("hello.txt@0").to_str().unwrap(),
+            testdata!("world.txt@0x5").to_str().unwrap(),
+        ])?;
         let err = image.assemble().unwrap_err();
         assert_eq!(
             err.to_string(),
diff --git a/sw/host/opentitanlib/src/util/mod.rs b/sw/host/opentitanlib/src/util/mod.rs
index 1c3c7bf..e60485b 100644
--- a/sw/host/opentitanlib/src/util/mod.rs
+++ b/sw/host/opentitanlib/src/util/mod.rs
@@ -26,3 +26,37 @@
         Iterator::collect(IntoIterator::into_iter([$($v),*]))
     }};
 }
+
+/// The `testdata` macro can be used in tests to reference testdata directories.
+#[macro_export]
+#[cfg(test)]
+macro_rules! testdata {
+    () => {{
+        use std::path::PathBuf;
+        let mut path = PathBuf::new();
+        path.push(file!());
+        path.pop();
+        path.push("testdata");
+        path
+    }};
+    ($f:expr) => {{
+        let mut path = testdata!();
+        path.push($f);
+        path
+    }};
+}
+
+#[cfg(test)]
+mod test {
+    #[test]
+    fn test_testdata() {
+        assert_eq!(
+            testdata!().to_str().unwrap(),
+            "sw/host/opentitanlib/src/util/testdata"
+        );
+        assert_eq!(
+            testdata!("my.file").to_str().unwrap(),
+            "sw/host/opentitanlib/src/util/testdata/my.file"
+        );
+    }
+}