Repository: networkx/networkx

Issue:
descendants_at_distance appears to be untested
AFAICT `descendants_at_distance` is not explicitly tested anywhere. I was confused by this because the coverage report for the `breadth_first_search` module where the function is defined is at 98% (notably - the untested line is in `descendants_at_distance`), but I don't see any explicit calls to `descendants_at_distance` when grepping the test suite.

Adding some tests should help codify behavior and avoid issues like #4952 .


Discussion:
It looks like `descendants_at_distance` is [called by ](https://github.com/networkx/networkx/blob/e8914bb5681b6fad8a6764406d3c7a78ebc582ae/networkx/algorithms/dag.py#L624)`transitive_closure_dag` in the `dag.py` module.  
But that just means even more -- we should include tests for it. :}

Resolving patch:
diff --git a/networkx/algorithms/traversal/breadth_first_search.py b/networkx/algorithms/traversal/breadth_first_search.py
--- a/networkx/algorithms/traversal/breadth_first_search.py
+++ b/networkx/algorithms/traversal/breadth_first_search.py
@@ -383,28 +383,37 @@ def descendants_at_distance(G, source, distance):
     -------
     set()
         The descendants of `source` in `G` at the given `distance` from `source`
+
+    Examples
+    --------
+    >>> G = nx.path_graph(5)
+    >>> nx.descendants_at_distance(G, 2, 2)
+    {0, 4}
+    >>> H = nx.DiGraph()
+    >>> H.add_edges_from([(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)])
+    >>> nx.descendants_at_distance(H, 0, 2)
+    {3, 4, 5, 6}
+    >>> nx.descendants_at_distance(H, 5, 0)
+    {5}
+    >>> nx.descendants_at_distance(H, 5, 1)
+    set()
     """
     if not G.has_node(source):
         raise nx.NetworkXError(f"The node {source} is not in the graph.")
     current_distance = 0
-    queue = {source}
+    current_layer = {source}
     visited = {source}
 
-    # this is basically BFS, except that the queue only stores the nodes at
+    # this is basically BFS, except that the current layer only stores the nodes at
     # current_distance from source at each iteration
-    while queue:
-        if current_distance == distance:
-            return queue
-
-        current_distance += 1
-
-        next_vertices = set()
-        for vertex in queue:
-            for child in G[vertex]:
+    while current_distance < distance:
+        next_layer = set()
+        for node in current_layer:
+            for child in G[node]:
                 if child not in visited:
                     visited.add(child)
-                    next_vertices.add(child)
-
-        queue = next_vertices
+                    next_layer.add(child)
+        current_layer = next_layer
+        current_distance += 1
 
-    return set()
+    return current_layer


Test patch:
diff --git a/networkx/algorithms/traversal/tests/test_bfs.py b/networkx/algorithms/traversal/tests/test_bfs.py
--- a/networkx/algorithms/traversal/tests/test_bfs.py
+++ b/networkx/algorithms/traversal/tests/test_bfs.py
@@ -1,6 +1,8 @@
 from functools import partial
 import networkx as nx
 
+import pytest
+
 
 class TestBFS:
     @classmethod
@@ -48,6 +50,14 @@ def test_bfs_tree_isolates(self):
         assert sorted(T.nodes()) == [1]
         assert sorted(T.edges()) == []
 
+    def test_descendants_at_distance(self):
+        for distance, descendants in enumerate([{0}, {1}, {2, 3}, {4}]):
+            assert nx.descendants_at_distance(self.G, 0, distance) == descendants
+
+    def test_descendants_at_distance_missing_source(self):
+        with pytest.raises(nx.NetworkXError):
+            nx.descendants_at_distance(self.G, "abc", 0)
+
 
 class TestBreadthLimitedSearch:
     @classmethod
@@ -98,3 +108,11 @@ def test_limited_bfs_tree(self):
     def test_limited_bfs_edges(self):
         edges = nx.bfs_edges(self.G, source=9, depth_limit=4)
         assert list(edges) == [(9, 8), (9, 10), (8, 7), (7, 2), (2, 1), (2, 3)]
+
+    def test_limited_descendants_at_distance(self):
+        for distance, descendants in enumerate(
+            [{0}, {1}, {2}, {3, 7}, {4, 8}, {5, 9}, {6, 10}]
+        ):
+            assert nx.descendants_at_distance(self.G, 0, distance) == descendants
+        for distance, descendants in enumerate([{2}, {3, 7}, {8}, {9}, {10}]):
+            assert nx.descendants_at_distance(self.D, 2, distance) == descendants