From ea57c40c438b9bb9981518b36ce70e7e445cf674 Mon Sep 17 00:00:00 2001
From: Ran Benita <ran@unusedvar.com>
Date: Fri, 9 Feb 2024 11:34:55 +0200
Subject: [PATCH] main: fix reversed collection order in Session

Since we're working with a stack (last in first out), we need to append
to it in reverse to preserve the order when popped.

Fix #11937.
---
 changelog/11937.bugfix.rst |  1 +
 src/_pytest/main.py        |  2 +-
 testing/acceptance_test.py |  2 +-
 testing/test_collection.py | 21 +++++++++++++++++++--
 4 files changed, 22 insertions(+), 4 deletions(-)
 create mode 100644 changelog/11937.bugfix.rst

diff --git a/changelog/11937.bugfix.rst b/changelog/11937.bugfix.rst
new file mode 100644
index 000000000..3b15fb528
--- /dev/null
+++ b/changelog/11937.bugfix.rst
@@ -0,0 +1 @@
+Fix a regression in pytest 8.0.0 whereby items would be collected in reverse order in some circumstances.
diff --git a/src/_pytest/main.py b/src/_pytest/main.py
index c9ef877d7..5c70ad74e 100644
--- a/src/_pytest/main.py
+++ b/src/_pytest/main.py
@@ -897,7 +897,7 @@ class Session(nodes.Collector):
 
                 # Prune this level.
                 any_matched_in_collector = False
-                for node in subnodes:
+                for node in reversed(subnodes):
                     # Path part e.g. `/a/b/` in `/a/b/test_file.py::TestIt::test_it`.
                     if isinstance(matchparts[0], Path):
                         is_match = node.path == matchparts[0]
diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py
index 0264aa288..e41d7a81f 100644
--- a/testing/acceptance_test.py
+++ b/testing/acceptance_test.py
@@ -241,7 +241,7 @@ class TestGeneralUsage:
         pytester.copy_example("issue88_initial_file_multinodes")
         p = pytester.makepyfile("def test_hello(): pass")
         result = pytester.runpytest(p, "--collect-only")
-        result.stdout.fnmatch_lines(["*Module*test_issue88*", "*MyFile*test_issue88*"])
+        result.stdout.fnmatch_lines(["*MyFile*test_issue88*", "*Module*test_issue88*"])
 
     def test_issue93_initialnode_importing_capturing(self, pytester: Pytester) -> None:
         pytester.makeconftest(
diff --git a/testing/test_collection.py b/testing/test_collection.py
index 8e41e0fae..ea8afc7c4 100644
--- a/testing/test_collection.py
+++ b/testing/test_collection.py
@@ -592,12 +592,12 @@ class TestSession:
         hookrec.assert_contains(
             [
                 ("pytest_collectstart", "collector.path == collector.session.path"),
-                ("pytest_collectstart", "collector.__class__.__name__ == 'Module'"),
-                ("pytest_pycollect_makeitem", "name == 'test_func'"),
                 (
                     "pytest_collectstart",
                     "collector.__class__.__name__ == 'SpecialFile'",
                 ),
+                ("pytest_collectstart", "collector.__class__.__name__ == 'Module'"),
+                ("pytest_pycollect_makeitem", "name == 'test_func'"),
                 ("pytest_collectreport", "report.nodeid.startswith(p.name)"),
             ]
         )
@@ -671,6 +671,23 @@ class TestSession:
         # ensure we are reporting the collection of the single test item (#2464)
         assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"]
 
+    def test_collect_parametrized_order(self, pytester: Pytester) -> None:
+        p = pytester.makepyfile(
+            """
+            import pytest
+
+            @pytest.mark.parametrize('i', [0, 1, 2])
+            def test_param(i): ...
+            """
+        )
+        items, hookrec = pytester.inline_genitems(f"{p}::test_param")
+        assert len(items) == 3
+        assert [item.nodeid for item in items] == [
+            "test_collect_parametrized_order.py::test_param[0]",
+            "test_collect_parametrized_order.py::test_param[1]",
+            "test_collect_parametrized_order.py::test_param[2]",
+        ]
+
 
 class Test_getinitialnodes:
     def test_global_file(self, pytester: Pytester) -> None: