Repository: conan-io/conan
Pull request #17496 (2024-12-18 06:38:04)

fix missing is_test fix of 2.9.3
Changelog: Bugfix: Integrate Conan 2.9.3 missing fix https://github.com/conan-io/conan/pull/17338
Docs: Omit

@bmwrightson raised the issue in https://github.com/conan-io/conan/pull/17338#issuecomment-2549828139

This PR replaces https://github.com/conan-io/conan/pull/17495 because this needs to have permissions to write a package in Github so it can't be a fork.



Patch:
diff --git a/conan/__init__.py b/conan/__init__.py
index 7e18c782a..2e09becb1 100644
--- a/conan/__init__.py
+++ b/conan/__init__.py
@@ -1,5 +1,5 @@
 from conans.model.conan_file import ConanFile
 from conan.tools.scm import Version as _Version
 
-__version__ = '2.10.2'
+__version__ = '2.10.3'
 conan_version = _Version(__version__)
diff --git a/conans/model/requires.py b/conans/model/requires.py
index b64e24bc8..5be2f3413 100644
--- a/conans/model/requires.py
+++ b/conans/model/requires.py
@@ -255,6 +255,7 @@ class Requirement:
         self.transitive_libs = self.transitive_libs or other.transitive_libs
         if not other.test:
             self.test = False  # it it was previously a test, but also required by non-test
+        # necessary even if no propagation, order of requires matter
         self.is_test = self.is_test or other.is_test
         # package_id_mode is not being propagated downstream. So it is enough to check if the
         # current require already defined it or not
@@ -347,7 +348,6 @@ class Requirement:
 
         if self.test:
             downstream_require.test = True
-        downstream_require.is_test = require.is_test
 
         # If the current one is resolving conflicts, the downstream one will be too
         downstream_require.force = require.force


Test patch:
diff --git a/test/integration/graph/test_test_requires.py b/test/integration/graph/test_test_requires.py
index 5342bc3ad..d74bec93a 100644
--- a/test/integration/graph/test_test_requires.py
+++ b/test/integration/graph/test_test_requires.py
@@ -203,6 +203,8 @@ def test_requires_transitive_diamond_components_order():
     like the above, but in different order
      libc  --(test-requires)---> liba
        |-----> libb -----------/
+    libc->liba => test=False, direct=True, is_test=True
+    direct_dependencies (components check) => False
     https://github.com/conan-io/conan/issues/17164
     """
     c = TestClient(light=True)
@@ -230,6 +232,48 @@ def test_requires_transitive_diamond_components_order():
     assert "libc/0.1: Created package" in c.out
 
 
+def test_wrong_requirement_test_requires():
+    """ https://github.com/conan-io/conan/issues/17312
+
+    app --------> etas --------------> enum
+      \\-->hwinfo-/---(test_requires)---/
+       \\-----------------------------/
+
+    app->enum => test=False, direct=True, is_test=True
+    direct_dependencies (components check) => True
+    """
+    c = TestClient(light=True)
+    app = textwrap.dedent("""
+        from conan import ConanFile
+        class Pkg(ConanFile):
+            name = "app"
+            version = "0.1"
+            requires = "etas/0.1", "enum/0.1", "hwinfo/0.1"
+
+            def generate(self):
+                for r, d in self.dependencies.items():
+                    assert not (r.direct and r.is_test)
+
+            def package_info(self):
+                self.cpp_info.requires = ["etas::etas", "enum::enum", "hwinfo::hwinfo"]
+            """)
+    files = {
+        "enum/conanfile.py": GenConanfile("enum", "0.1"),
+        "etas/conanfile.py": GenConanfile("etas", "0.1").with_requirement("enum/0.1"),
+        "hwinfo/conanfile.py": GenConanfile("hwinfo", "0.1").with_requirement("etas/0.1")
+                                                            .with_test_requires("enum/0.1"),
+        "app/conanfile.py": app,
+    }
+    c.save(files)
+    c.run("create enum")
+    c.run("create etas")
+    c.run("create hwinfo")
+    # The assert in generate() doesnt fail
+    c.run("install app")
+    # the package_info() doesn't fail
+    c.run("create app")
+
+
 def test_test_requires_options():
     """ the default_options = {} values also propagate to ``test_requires()`` """
     c = TestClient(light=True)