Repository: conan-io/conan
Pull request: `test_type` mechanism deprecation
Changelog: Feature: In the conanfile.py of the test_package, the reference being tested is always available at `self.tested_reference_str`.
Changelog: Feature: Introduced a new `test_type` value `explicit` so a user can declare explicitly the `requires` or `build_requires` manually (using `self.tested_reference_str`), it won't be automatically injected as a require. In Conan 2.0 the `test_type` attribute will be ignored, the behavior will be always explicit, so declaring `test_type="explicit"` will make the test recipe compatible with Conan 2.0.
Docs: https://github.com/conan-io/docs/pull/2341

Close https://github.com/conan-io/conan/issues/10073
Close https://github.com/conan-io/conan/issues/9606


Problem statement:
[question] How to find out the name of the package to be tested in test_package?
In a `test_package` I can access all dependencies via `self.deps_cpp_info`, but is there any way I can determine which one is the subject under test?

[feature] test package conanfile, `test_type` mechanism deprecation
Conan has the `test_type` mechanism so we can specify in the conanfile of a `test package` the type of requirement that we want to test.
The `test_type` allows `test_type = "build_requires"` or `test_type="requires"` but that doesn't cover the current variety of requirements we could have in 2.0 with the requirements traits. 

So we want to introduce a mechanism in `1.X` to be prepared for the removal of `test_type` in Conan 2.0 to receive (injected) in the conanfile a variable with the reference to be tested (so in case the version is bumped we don't need to update the test_package for example) and then write the requirement explicitly, using that variable. Something like:


```
def MyTestPackage(ConanFile):

      def requirements(self):
          self.build_requires(self.testing_package_reference)

```

Hints from issue discussion:
Oh, I have found a solution!

Looks like I can get the name from `self.display_name`. Something like `self.display_name.split("/")[0]`.

Is this a good _(and stable)_ way to go?
It seems that ``display_name`` is not documented, so it wouldn't be stable.

Why would you have more than 1 require in a ``test_package``? Maybe a ``build_require`` would make sense, but more than 1 ``require``, it would be good to know what is the use case. And why a ``test_package`` doesn't know the name of the package that is testing? As it needs to write code specific for the thing that is testing, it seems that it will always know which package is testing.
In many cases the `test_package/conanfile.py` looks like this _(this one is from conan-center-index)_:

```
import os
from conans import ConanFile, CMake, tools

class TestPackageConan(ConanFile):
    settings = "os", "compiler", "build_type", "arch"
    generators = "cmake_find_package", "cmake"

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

    def test(self):
        if not tools.cross_building(self.settings):
            bin_path = os.path.join("bin","test_package")
            self.run(bin_path, run_environment=True)
```

This code itself does not know the name of the package under test. However, in our case, our `test_package` does a bit more than that. So to avoid copying and duplicating this code everywhere we use a common base class _(via python_requires)_.

But now I wanted to know which package I am testing right now _(inside the common base class)_. `self.deps_cpp_info` doesn't help, since it contains also the indirect dependencies _(if B depends on A and I'm testing B it contains A and B)_.

This will be solved by https://github.com/conan-io/conan/issues/10073, instead of injecting a ``require``, the reference of the tested package will be injected. Then it will be explicit to define the ``require`` and it will be obvious to get the name of the package being tested.
This feature will solve https://github.com/conan-io/conan/issues/9606 too.

Commit message: Rename to testing_reference

Patch to review:
diff --git a/conans/client/graph/graph_manager.py b/conans/client/graph/graph_manager.py
index 8ef2708ffbe..e30aa14a36e 100644
--- a/conans/client/graph/graph_manager.py
+++ b/conans/client/graph/graph_manager.py
@@ -100,6 +100,7 @@ def load_consumer_conanfile(self, conanfile_path, info_folder,
             if test:
                 conanfile.display_name = "%s (test package)" % str(test)
                 conanfile.output.scope = conanfile.display_name
+                conanfile.testing_reference = str(test)
             run_configure_method(conanfile, down_options=None, down_ref=None, ref=None)
         else:
             conanfile = self._loader.load_conanfile_txt(conanfile_path, profile_host=profile_host)
@@ -157,8 +158,9 @@ def _load_root_node(self, reference, create_reference, profile_host, graph_lock,
 
         path = reference  # The reference must be pointing to a user space conanfile
         if create_reference:  # Test_package -> tested reference
-            return self._load_root_test_package(path, create_reference, graph_lock, profile_host,
+            ret = self._load_root_test_package(path, create_reference, graph_lock, profile_host,
                                                 require_overrides)
+            return ret
 
         # It is a path to conanfile.py or conanfile.txt
         root_node = self._load_root_consumer(path, graph_lock, profile_host, root_ref,
@@ -246,26 +248,32 @@ def _load_root_test_package(self, path, create_reference, graph_lock, profile,
                                                )
         conanfile.display_name = "%s (test package)" % str(test)
         conanfile.output.scope = conanfile.display_name
+        conanfile.testing_reference = str(create_reference)
 
         # Injection of the tested reference
         test_type = getattr(conanfile, "test_type", ("requires", ))
         if not isinstance(test_type, (list, tuple)):
             test_type = (test_type, )
-        if "build_requires" in test_type:
-            if getattr(conanfile, "build_requires", None):
-                # Injecting the tested reference
-                existing = conanfile.build_requires
-                if not isinstance(existing, (list, tuple)):
-                    existing = [existing]
-                conanfile.build_requires = list(existing) + [create_reference]
-            else:
-                conanfile.build_requires = str(create_reference)
-        if "requires" in test_type:
-            require = conanfile.requires.get(create_reference.name)
-            if require:
-                require.ref = require.range_ref = create_reference
-            else:
-                conanfile.requires.add_ref(create_reference)
+
+        if "explicit" in test_type:  # 2.0 mode, not automatically add the require, always explicit
+            pass
+        else:
+            if "build_requires" in test_type:
+                if getattr(conanfile, "build_requires", None):
+                    # Injecting the tested reference
+                    existing = conanfile.build_requires
+                    if not isinstance(existing, (list, tuple)):
+                        existing = [existing]
+                    conanfile.build_requires = list(existing) + [create_reference]
+                else:
+                    conanfile.build_requires = str(create_reference)
+            if "requires" in test_type:
+                require = conanfile.requires.get(create_reference.name)
+                if require:
+                    require.ref = require.range_ref = create_reference
+                else:
+                    conanfile.requires.add_ref(create_reference)
+
         ref = ConanFileReference(conanfile.name, conanfile.version,
                                  create_reference.user, create_reference.channel, validate=False)
         root_node = Node(ref, conanfile, recipe=RECIPE_CONSUMER, context=CONTEXT_HOST, path=path)
diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py
index 4fd18da4482..ec92439efd6 100644
--- a/conans/model/conan_file.py
+++ b/conans/model/conan_file.py
@@ -142,6 +142,9 @@ class ConanFile(object):
     # Run in windows bash
     win_bash = None
 
+    # For test_package, reference being tested
+    testing_reference = None
+
     def __init__(self, output, runner, display_name="", user=None, channel=None):
         # an output stream (writeln, info, warn error)
         self.output = ScopedOutput(display_name, output)
diff --git a/conans/test/integration/command/test_package_test.py b/conans/test/integration/command/test_package_test.py
index f98d38060dd..c60e9b47825 100644
--- a/conans/test/integration/command/test_package_test.py
+++ b/conans/test/integration/command/test_package_test.py
@@ -277,3 +277,47 @@ def test(self):
         client.run("create . lasote/stable")  # create rebuild the package
         self.assertEqual("Bye FindCmake",
                          load(os.path.join(client.cache.package_layout(pref.ref).package(pref), "FindXXX.cmake")))
+
+
+def test_testing_reference():
+    """
+    At the test_package/conanfile the variable `self.testing_reference` is injected with the
+    str of the reference being tested. It is available in all the methods.
+
+    Compatibility with Conan 2.0:
+    If the 'test_type' is set to "explicit" the require won't be automatically injected and has to
+    be the user the one injecting the require or the build require using the
+    `self.testing_reference`. This 'test_type' can be removed in 2.0 if we consider it has
+    to be always explicit. The recipes will still work in Conan 2.0 because the 'test_type' will be
+    ignored.
+    """
+    client = TestClient()
+    test_conanfile = textwrap.dedent("""
+    from conans import ConanFile, CMake
+    import os
+
+    class HelloReuseConan(ConanFile):
+
+        test_type = "explicit"
+
+        def generate(self):
+            self.output.warn("At generate: {}".format(self.testing_reference))
+            assert len(self.dependencies.values()) == 1
+            assert len(self.dependencies.build.values()) == 1
+
+        def build(self):
+            self.output.warn("At build: {}".format(self.testing_reference))
+
+        def build_requirements(self):
+            self.output.warn("At build_requirements: {}".format(self.testing_reference))
+            self.build_requires(self.testing_reference)
+
+        def test(self):
+            self.output.warn("At test: {}".format(self.testing_reference))
+    """)
+
+    client.save({"conanfile.py": GenConanfile(), "test_package/conanfile.py": test_conanfile})
+    client.run("create . foo/1.0@")
+    for method in ("generate", "build", "build_requirements", "test"):
+        assert "At {}: foo/1.0".format(method) in client.out
+


Code review comments:
diff_hunk: @@ -100,6 +100,7 @@ def load_consumer_conanfile(self, conanfile_path, info_folder,
             if test:
                 conanfile.display_name = "%s (test package)" % str(test)
                 conanfile.output.scope = conanfile.display_name
+                conanfile.testing_reference = str(test)
path: conans/client/graph/graph_manager.py
text: @user1:
Why ``str()`` and not the RecipeReference object? Why not ``repr()`` so we don't lose potential information like the recipe revision?

@author:
Yes, I was expecting this comment to talk about it:

 If you do a `self.requires(self.testing_reference)` it fails because it expects an `str`. So we would need to enable a `ConanFileReference` as a valid argument. 

Also, I introduced the `str` here because I'm not sure we have documented the `ConanFileReference` (maybe in the `dependencies`?) and it is probably that the users are going to compare the reference so we have to know that `ConanFileReference.loads("foo/1.0") != "foo/1.0"`. So I wanted to talk about this before taking the decission.