Merge "Fix flaky test" into main
diff --git a/libs/libfdt/src/iterators.rs b/libs/libfdt/src/iterators.rs
index a524655..7406164 100644
--- a/libs/libfdt/src/iterators.rs
+++ b/libs/libfdt/src/iterators.rs
@@ -330,7 +330,7 @@
 }
 
 impl<'a> DescendantsIterator<'a> {
-    pub(crate) fn new(node: &'a FdtNode) -> Self {
+    pub(crate) fn new(node: &FdtNode<'a>) -> Self {
         Self { node: Some((*node, 0)) }
     }
 }
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 7eb08b2..d90f5f0 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -487,7 +487,7 @@
     }
 
     /// Returns an iterator of descendants
-    pub fn descendants(&'a self) -> DescendantsIterator<'a> {
+    pub fn descendants(&self) -> DescendantsIterator<'a> {
         DescendantsIterator::new(self)
     }
 
@@ -811,6 +811,41 @@
         Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
     }
 
+    fn next_node_offset(&self, depth: usize) -> Result<Option<(c_int, usize)>> {
+        let mut next_depth: c_int = depth.try_into().or(Err(FdtError::BadValue))?;
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let ret = unsafe {
+            libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
+        };
+        let Ok(next_depth) = usize::try_from(next_depth) else {
+            return Ok(None);
+        };
+        Ok(fdt_err_or_option(ret)?.map(|offset| (offset, next_depth)))
+    }
+
+    /// Returns the next node
+    pub fn next_node(self, depth: usize) -> Result<Option<(Self, usize)>> {
+        Ok(self
+            .next_node_offset(depth)?
+            .map(|(offset, next_depth)| (FdtNodeMut { fdt: self.fdt, offset }, next_depth)))
+    }
+
+    /// Deletes this and returns the next node
+    pub fn delete_and_next_node(mut self, depth: usize) -> Result<Option<(Self, usize)>> {
+        // Skip all would-be-removed descendants.
+        let mut iter = self.next_node_offset(depth)?;
+        while let Some((descendant_offset, descendant_depth)) = iter {
+            if descendant_depth <= depth {
+                break;
+            }
+            let descendant = FdtNodeMut { fdt: self.fdt, offset: descendant_offset };
+            iter = descendant.next_node_offset(descendant_depth)?;
+        }
+        // SAFETY: This consumes self, so invalid node wouldn't be used any further
+        unsafe { self.nop_self()? };
+        Ok(iter.map(|(offset, next_depth)| (FdtNodeMut { fdt: self.fdt, offset }, next_depth)))
+    }
+
     fn parent(&'a self) -> Result<FdtNode<'a>> {
         // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
         let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index e68557f..08fb8a5 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -399,3 +399,94 @@
 
     assert_eq!(expected_names, subnode_names);
 }
+
+#[test]
+fn node_mut_delete_and_next_node() {
+    let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+    let fdt = Fdt::from_mut_slice(&mut data).unwrap();
+
+    let expected_nodes = vec![
+        (Ok(cstr!("node_b")), 1),
+        (Ok(cstr!("node_c")), 1),
+        (Ok(cstr!("node_z")), 1),
+        (Ok(cstr!("node_za")), 2),
+        (Ok(cstr!("node_zb")), 2),
+        (Ok(cstr!("__symbols__")), 1),
+    ];
+
+    let mut expected_nodes_iter = expected_nodes.iter();
+    let mut iter = fdt.root_mut().unwrap().next_node(0).unwrap();
+    while let Some((node, depth)) = iter {
+        let node_name = node.as_node().name();
+        if node_name == Ok(cstr!("node_a")) || node_name == Ok(cstr!("node_zz")) {
+            iter = node.delete_and_next_node(depth).unwrap();
+        } else {
+            // Note: Checking name here is easier than collecting names and assert_eq!(),
+            //       because we can't keep name references while iterating with FdtNodeMut.
+            let expected_node = expected_nodes_iter.next();
+            assert_eq!(expected_node, Some(&(node_name, depth)));
+            iter = node.next_node(depth).unwrap();
+        }
+    }
+    assert_eq!(None, expected_nodes_iter.next());
+
+    let root = fdt.root().unwrap();
+    let all_descendants: Vec<_> =
+        root.descendants().map(|(node, depth)| (node.name(), depth)).collect();
+    assert_eq!(expected_nodes, all_descendants);
+}
+
+#[test]
+#[ignore] // Borrow checker test. Compilation success is sufficient.
+fn node_name_lifetime() {
+    let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+    let fdt = Fdt::from_slice(&data).unwrap();
+
+    let name = {
+        let root = fdt.root().unwrap();
+        root.name()
+        // Make root to be dropped
+    };
+    assert_eq!(Ok(cstr!("")), name);
+}
+
+#[test]
+#[ignore] // Borrow checker test. Compilation success is sufficient.
+fn node_subnode_lifetime() {
+    let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+    let fdt = Fdt::from_slice(&data).unwrap();
+
+    let name = {
+        let node_a = {
+            let root = fdt.root().unwrap();
+            root.subnode(cstr!("node_a")).unwrap()
+            // Make root to be dropped
+        };
+        assert_ne!(None, node_a);
+        node_a.unwrap().name()
+        // Make node_a to be dropped
+    };
+    assert_eq!(Ok(cstr!("node_a")), name);
+}
+
+#[test]
+#[ignore] // Borrow checker test. Compilation success is sufficient.
+fn node_descendants_lifetime() {
+    let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+    let fdt = Fdt::from_slice(&data).unwrap();
+
+    let first_descendant_name = {
+        let (first_descendant, _) = {
+            let mut descendants_iter = {
+                let root = fdt.root().unwrap();
+                root.descendants()
+                // Make root to be dropped
+            };
+            descendants_iter.next().unwrap()
+            // Make descendants_iter to be dropped
+        };
+        first_descendant.name()
+        // Make first_descendant to be dropped
+    };
+    assert_eq!(Ok(cstr!("node_a")), first_descendant_name);
+}