Repository: conan-io/conan
Pull request #12864 (2023-01-07 23:01:23)

new test to show build_folder_vars works in test_package
try to provide some solution to https://github.com/conan-io/conan/issues/10201


Patch:
diff --git a/conan/tools/cmake/presets.py b/conan/tools/cmake/presets.py
index dacf021ef..306b811d3 100644
--- a/conan/tools/cmake/presets.py
+++ b/conan/tools/cmake/presets.py
@@ -10,16 +10,24 @@ from conans.util.files import save, load
 
 
 def _build_preset(conanfile, multiconfig):
+    return _common_build_and_test_preset_fields(conanfile, multiconfig)
+
+
+def _test_preset(conanfile, multiconfig):
+    return _common_build_and_test_preset_fields(conanfile, multiconfig)
+
+
+def _common_build_and_test_preset_fields(conanfile, multiconfig):
     build_type = conanfile.settings.get_safe("build_type")
     configure_preset_name = _configure_preset_name(conanfile, multiconfig)
-    ret = {"name": _build_preset_name(conanfile),
+    ret = {"name": _build_and_test_preset_name(conanfile),
            "configurePreset": configure_preset_name}
     if multiconfig:
         ret["configuration"] = build_type
     return ret
 
 
-def _build_preset_name(conanfile):
+def _build_and_test_preset_name(conanfile):
     build_type = conanfile.settings.get_safe("build_type")
     custom_conf, user_defined_build = get_build_folder_custom_vars(conanfile)
     if user_defined_build:
@@ -108,6 +116,14 @@ def _configure_preset(conanfile, generator, cache_variables, toolchain_file, mul
     return ret
 
 
+def _insert_preset(data, preset_name, preset):
+    position = _get_already_existing_preset_index(preset["name"], data[preset_name])
+    if position is not None:
+        data[preset_name][position] = preset
+    else:
+        data[preset_name].append(preset)
+
+
 def _forced_schema_2(conanfile):
     version = conanfile.conf.get("tools.cmake.cmaketoolchain.presets:max_schema_version",
                                  check_type=int)
@@ -143,6 +159,7 @@ def _contents(conanfile, toolchain_file, cache_variables, generator):
           }
     multiconfig = is_multi_configuration(generator)
     ret["buildPresets"].append(_build_preset(conanfile, multiconfig))
+    ret["testPresets"].append(_test_preset(conanfile, multiconfig))
     _conf = _configure_preset(conanfile, generator, cache_variables, toolchain_file, multiconfig)
     ret["configurePresets"].append(_conf)
     return ret
@@ -174,20 +191,14 @@ def write_cmake_presets(conanfile, toolchain_file, generator, cache_variables,
     if os.path.exists(preset_path):
         data = json.loads(load(preset_path))
         build_preset = _build_preset(conanfile, multiconfig)
-        position = _get_already_existing_preset_index(build_preset["name"], data["buildPresets"])
-        if position is not None:
-            data["buildPresets"][position] = build_preset
-        else:
-            data["buildPresets"].append(build_preset)
+        _insert_preset(data, "buildPresets", build_preset)
+
+        test_preset = _test_preset(conanfile, multiconfig)
+        _insert_preset(data, "testPresets", test_preset)
 
         configure_preset = _configure_preset(conanfile, generator, cache_variables, toolchain_file,
                                              multiconfig)
-        position = _get_already_existing_preset_index(configure_preset["name"],
-                                                      data["configurePresets"])
-        if position is not None:
-            data["configurePresets"][position] = configure_preset
-        else:
-            data["configurePresets"].append(configure_preset)
+        _insert_preset(data, "configurePresets", configure_preset)
     else:
         data = _contents(conanfile, toolchain_file, cache_variables, generator)
 


Test patch:
diff --git a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
index dbd70681e..e172d3927 100644
--- a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
@@ -594,9 +594,12 @@ def test_cmake_presets_multiple_settings_single_config():
     presets = json.loads(load(user_presets["include"][0]))
     assert len(presets["configurePresets"]) == 1
     assert len(presets["buildPresets"]) == 1
+    assert len(presets["testPresets"]) == 1
     assert presets["configurePresets"][0]["name"] == "apple-clang-12.0-gnu17-release"
     assert presets["buildPresets"][0]["name"] == "apple-clang-12.0-gnu17-release"
     assert presets["buildPresets"][0]["configurePreset"] == "apple-clang-12.0-gnu17-release"
+    assert presets["testPresets"][0]["name"] == "apple-clang-12.0-gnu17-release"
+    assert presets["testPresets"][0]["configurePreset"] == "apple-clang-12.0-gnu17-release"
 
     # If we create the "Debug" one, it has the same toolchain and preset file, that is
     # always multiconfig
@@ -608,12 +611,17 @@ def test_cmake_presets_multiple_settings_single_config():
     presets = json.loads(load(user_presets["include"][0]))
     assert len(presets["configurePresets"]) == 2
     assert len(presets["buildPresets"]) == 2
+    assert len(presets["testPresets"]) == 2
     assert presets["configurePresets"][0]["name"] == "apple-clang-12.0-gnu17-release"
     assert presets["configurePresets"][1]["name"] == "apple-clang-12.0-gnu17-debug"
     assert presets["buildPresets"][0]["name"] == "apple-clang-12.0-gnu17-release"
     assert presets["buildPresets"][1]["name"] == "apple-clang-12.0-gnu17-debug"
     assert presets["buildPresets"][0]["configurePreset"] == "apple-clang-12.0-gnu17-release"
     assert presets["buildPresets"][1]["configurePreset"] == "apple-clang-12.0-gnu17-debug"
+    assert presets["testPresets"][0]["name"] == "apple-clang-12.0-gnu17-release"
+    assert presets["testPresets"][1]["name"] == "apple-clang-12.0-gnu17-debug"
+    assert presets["testPresets"][0]["configurePreset"] == "apple-clang-12.0-gnu17-release"
+    assert presets["testPresets"][1]["configurePreset"] == "apple-clang-12.0-gnu17-debug"
 
     # But If we change, for example, the cppstd and the compiler version, the toolchain
     # and presets will be different, but it will be appended to the UserPresets.json
@@ -629,26 +637,32 @@ def test_cmake_presets_multiple_settings_single_config():
     presets = json.loads(load(user_presets["include"][1]))
     assert len(presets["configurePresets"]) == 1
     assert len(presets["buildPresets"]) == 1
+    assert len(presets["testPresets"]) == 1
     assert presets["configurePresets"][0]["name"] == "apple-clang-13-gnu20-release"
     assert presets["buildPresets"][0]["name"] == "apple-clang-13-gnu20-release"
     assert presets["buildPresets"][0]["configurePreset"] == "apple-clang-13-gnu20-release"
+    assert presets["testPresets"][0]["name"] == "apple-clang-13-gnu20-release"
+    assert presets["testPresets"][0]["configurePreset"] == "apple-clang-13-gnu20-release"
 
     # We can build with cmake manually
     if platform.system() == "Darwin":
         client.run_command("cmake . --preset apple-clang-12.0-gnu17-release")
         client.run_command("cmake --build --preset apple-clang-12.0-gnu17-release")
+        client.run_command("ctest --preset apple-clang-12.0-gnu17-release")
         client.run_command("./build/apple-clang-12.0-gnu17/Release/hello")
         assert "Hello World Release!" in client.out
         assert "__cplusplus2017" in client.out
 
         client.run_command("cmake . --preset apple-clang-12.0-gnu17-debug")
         client.run_command("cmake --build --preset apple-clang-12.0-gnu17-debug")
+        client.run_command("ctest --preset apple-clang-12.0-gnu17-debug")
         client.run_command("./build/apple-clang-12.0-gnu17/Debug/hello")
         assert "Hello World Debug!" in client.out
         assert "__cplusplus2017" in client.out
 
         client.run_command("cmake . --preset apple-clang-13-gnu20-release")
         client.run_command("cmake --build --preset apple-clang-13-gnu20-release")
+        client.run_command("ctest --preset apple-clang-13-gnu20-release")
         client.run_command("./build/apple-clang-13-gnu20/Release/hello")
         assert "Hello World Release!" in client.out
         assert "__cplusplus2020" in client.out
@@ -672,6 +686,7 @@ def test_cmake_presets_duplicated_install(multiconfig):
     assert os.path.exists(presets_path)
     contents = json.loads(load(presets_path))
     assert len(contents["buildPresets"]) == 1
+    assert len(contents["testPresets"]) == 1
 
 
 def test_remove_missing_presets():
@@ -736,6 +751,7 @@ def test_cmake_presets_options_single_config():
             shared_str = "shared_true" if shared else "shared_false"
             client.run_command("cmake . --preset apple-clang-{}-release".format(shared_str))
             client.run_command("cmake --build --preset apple-clang-{}-release".format(shared_str))
+            client.run_command("ctest --preset apple-clang-{}-release".format(shared_str))
             the_lib = "libhello.a" if not shared else "libhello.dylib"
             path = os.path.join(client.current_folder,
                                 "build", "apple-clang-{}".format(shared_str), "release", the_lib)
@@ -763,9 +779,12 @@ def test_cmake_presets_multiple_settings_multi_config():
     presets = json.loads(load(user_presets["include"][0]))
     assert len(presets["configurePresets"]) == 1
     assert len(presets["buildPresets"]) == 1
+    assert len(presets["testPresets"]) == 1
     assert presets["configurePresets"][0]["name"] == "dynamic-14"
     assert presets["buildPresets"][0]["name"] == "dynamic-14-release"
     assert presets["buildPresets"][0]["configurePreset"] == "dynamic-14"
+    assert presets["testPresets"][0]["name"] == "dynamic-14-release"
+    assert presets["testPresets"][0]["configurePreset"] == "dynamic-14"
 
     # If we create the "Debug" one, it has the same toolchain and preset file, that is
     # always multiconfig
@@ -777,11 +796,16 @@ def test_cmake_presets_multiple_settings_multi_config():
     presets = json.loads(load(user_presets["include"][0]))
     assert len(presets["configurePresets"]) == 1
     assert len(presets["buildPresets"]) == 2
+    assert len(presets["testPresets"]) == 2
     assert presets["configurePresets"][0]["name"] == "dynamic-14"
     assert presets["buildPresets"][0]["name"] == "dynamic-14-release"
     assert presets["buildPresets"][1]["name"] == "dynamic-14-debug"
     assert presets["buildPresets"][0]["configurePreset"] == "dynamic-14"
     assert presets["buildPresets"][1]["configurePreset"] == "dynamic-14"
+    assert presets["testPresets"][0]["name"] == "dynamic-14-release"
+    assert presets["testPresets"][1]["name"] == "dynamic-14-debug"
+    assert presets["testPresets"][0]["configurePreset"] == "dynamic-14"
+    assert presets["testPresets"][1]["configurePreset"] == "dynamic-14"
 
     # But If we change, for example, the cppstd and the compiler version, the toolchain
     # and presets will be different, but it will be appended to the UserPresets.json
@@ -796,19 +820,24 @@ def test_cmake_presets_multiple_settings_multi_config():
     presets = json.loads(load(user_presets["include"][1]))
     assert len(presets["configurePresets"]) == 1
     assert len(presets["buildPresets"]) == 1
+    assert len(presets["testPresets"]) == 1
     assert presets["configurePresets"][0]["name"] == "static-17"
     assert presets["buildPresets"][0]["name"] == "static-17-release"
     assert presets["buildPresets"][0]["configurePreset"] == "static-17"
+    assert presets["testPresets"][0]["name"] == "static-17-release"
+    assert presets["testPresets"][0]["configurePreset"] == "static-17"
 
     # We can build with cmake manually
     client.run_command("cmake . --preset dynamic-14")
 
     client.run_command("cmake --build --preset dynamic-14-release")
+    client.run_command("ctest --preset dynamic-14-release")
     client.run_command("build\\dynamic-14\\Release\\hello")
     assert "Hello World Release!" in client.out
     assert "MSVC_LANG2014" in client.out
 
     client.run_command("cmake --build --preset dynamic-14-debug")
+    client.run_command("ctest --preset dynamic-14-debug")
     client.run_command("build\\dynamic-14\\Debug\\hello")
     assert "Hello World Debug!" in client.out
     assert "MSVC_LANG2014" in client.out
@@ -816,6 +845,7 @@ def test_cmake_presets_multiple_settings_multi_config():
     client.run_command("cmake . --preset static-17")
 
     client.run_command("cmake --build --preset static-17-release")
+    client.run_command("ctest --preset static-17-release")
     client.run_command("build\\static-17\\Release\\hello")
     assert "Hello World Release!" in client.out
     assert "MSVC_LANG2017" in client.out
@@ -843,10 +873,12 @@ def test_user_presets_version2():
     if platform.system() == "Windows":
         client.run_command("cmake . --preset 14")
         client.run_command("cmake --build --preset 14-release")
+        client.run_command("ctest --preset 14-release")
         client.run_command(r"build\14\Release\hello.exe")
     else:
         client.run_command("cmake . --preset 14-release")
         client.run_command("cmake --build --preset 14-release")
+        client.run_command("ctest --preset 14-release")
         client.run_command("./build/14/Release/hello")
 
     assert "Hello World Release!" in client.out
@@ -859,10 +891,12 @@ def test_user_presets_version2():
     if platform.system() == "Windows":
         client.run_command("cmake . --preset 17")
         client.run_command("cmake --build --preset 17-release")
+        client.run_command("ctest --preset 17-release")
         client.run_command(r"build\17\Release\hello.exe")
     else:
         client.run_command("cmake . --preset 17-release")
         client.run_command("cmake --build --preset 17-release")
+        client.run_command("ctest --preset 17-release")
         client.run_command("./build/17/Release/hello")
 
     assert "Hello World Release!" in client.out
@@ -949,10 +983,12 @@ def test_cmake_presets_with_conanfile_txt():
     if platform.system() != "Windows":
         c.run_command("cmake --preset debug")
         c.run_command("cmake --build --preset debug")
+        c.run_command("ctest --preset debug")
         c.run_command("./build/Debug/foo")
     else:
         c.run_command("cmake --preset default")
         c.run_command("cmake --build --preset debug")
+        c.run_command("ctest --preset debug")
         c.run_command("build\\Debug\\foo")
 
     assert "Hello World Debug!" in c.out
@@ -960,9 +996,11 @@ def test_cmake_presets_with_conanfile_txt():
     if platform.system() != "Windows":
         c.run_command("cmake --preset release")
         c.run_command("cmake --build --preset release")
+        c.run_command("ctest --preset release")
         c.run_command("./build/Release/foo")
     else:
         c.run_command("cmake --build --preset release")
+        c.run_command("ctest --preset release")
         c.run_command("build\\Release\\foo")
 
     assert "Hello World Release!" in c.out
diff --git a/conans/test/integration/configuration/conf/test_conf_profile.py b/conans/test/integration/configuration/conf/test_conf_profile.py
index c2c208665..0f55ad7eb 100644
--- a/conans/test/integration/configuration/conf/test_conf_profile.py
+++ b/conans/test/integration/configuration/conf/test_conf_profile.py
@@ -164,3 +164,36 @@ def test_msbuild_compile_options():
     client.run("install . -pr=myprofile")
     msbuild_tool = client.load("conantoolchain_release_x64.props")
     assert "<ExceptionHandling>Async</ExceptionHandling>" in msbuild_tool
+
+
+def test_config_package_append(client):
+    profile1 = textwrap.dedent("""\
+        [conf]
+        user.myteam:myconf=["a", "b", "c"]
+        """)
+    profile2 = textwrap.dedent("""\
+        include(profile1)
+        [conf]
+        mypkg*:user.myteam:myconf+=["d"]
+        mydep*:user.myteam:myconf=+["e"]
+        """)
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        class Pkg(ConanFile):
+            def generate(self):
+                self.output.info(f"MYCONF: {self.conf.get('user.myteam:myconf')}")
+            def build(self):
+                self.output.info(f"MYCONFBUILD: {self.conf.get('user.myteam:myconf')}")
+            """)
+    client.save({"profile1": profile1,
+                 "profile2": profile2,
+                 "conanfile.py": conanfile})
+    client.run("install . mypkg/0.1@ -pr=profile2")
+    assert "conanfile.py (mypkg/0.1): MYCONF: ['a', 'b', 'c', 'd']" in client.out
+    client.run("install . mydep/0.1@ -pr=profile2")
+    assert "conanfile.py (mydep/0.1): MYCONF: ['e', 'a', 'b', 'c']" in client.out
+
+    client.run("create . mypkg/0.1@ -pr=profile2")
+    assert "mypkg/0.1: MYCONFBUILD: ['a', 'b', 'c', 'd']" in client.out
+    client.run("create . mydep/0.1@ -pr=profile2")
+    assert "mydep/0.1: MYCONFBUILD: ['e', 'a', 'b', 'c']" in client.out
diff --git a/conans/test/integration/py_requires/python_requires_test.py b/conans/test/integration/py_requires/python_requires_test.py
index c2d5bab40..174f92ad1 100644
--- a/conans/test/integration/py_requires/python_requires_test.py
+++ b/conans/test/integration/py_requires/python_requires_test.py
@@ -1020,3 +1020,103 @@ def test_multiple_reuse():
     c.run("install consumer")
     assert "VALUE COMMON: 42!!!" in c.out
     assert "VALUE TOOL: 42!!!" in c.out
+
+
+class TestTransitiveExtend:
+    # https://github.com/conan-io/conan/issues/10511
+    # https://github.com/conan-io/conan/issues/10565
+    def test_transitive_extend(self):
+        client = TestClient()
+        company = textwrap.dedent("""
+            from conan import ConanFile
+            class CompanyConanFile(ConanFile):
+                name = "company"
+                version = "1.0"
+
+                def msg1(self):
+                    return "company"
+                def msg2(self):
+                    return "company"
+            """)
+        project = textwrap.dedent("""
+            from conan import ConanFile
+            class ProjectBaseConanFile(ConanFile):
+                name = "project"
+                version = "1.0"
+
+                python_requires = "company/1.0"
+                python_requires_extend = "company.CompanyConanFile"
+
+                def msg1(self):
+                    return "project"
+            """)
+        consumer = textwrap.dedent("""
+            from conan import ConanFile
+            class Base(ConanFile):
+                name = "consumer"
+                version = "1.0"
+                python_requires = "project/1.0"
+                python_requires_extend = "project.ProjectBaseConanFile"
+                def generate(self):
+                    self.output.info("Msg1:{}!!!".format(self.msg1()))
+                    self.output.info("Msg2:{}!!!".format(self.msg2()))
+                """)
+        client.save({"company/conanfile.py": company,
+                     "project/conanfile.py": project,
+                     "consumer/conanfile.py": consumer})
+        client.run("export company")
+        client.run("export project")
+        client.run("install consumer")
+        assert "conanfile.py (consumer/1.0): Msg1:project!!!" in client.out
+        assert "conanfile.py (consumer/1.0): Msg2:company!!!" in client.out
+
+    def test_transitive_extend2(self):
+        client = TestClient()
+        company = textwrap.dedent("""
+            from conan import ConanFile
+            class CompanyConanFile(ConanFile):
+                name = "company"
+                version = "1.0"
+
+            class CompanyBase:
+                def msg1(self):
+                    return "company"
+                def msg2(self):
+                    return "company"
+            """)
+        project = textwrap.dedent("""
+            from conan import ConanFile
+            class ProjectBase:
+                def msg1(self):
+                    return "project"
+
+            class ProjectBaseConanFile(ConanFile):
+                name = "project"
+                version = "1.0"
+                python_requires = "company/1.0"
+
+                def init(self):
+                    pkg_name, base_class_name = "company", "CompanyBase"
+                    base_class = getattr(self.python_requires[pkg_name].module, base_class_name)
+                    global ProjectBase
+                    ProjectBase = type('ProjectBase', (ProjectBase, base_class, object), {})
+            """)
+        consumer = textwrap.dedent("""
+            from conan import ConanFile
+            class Base(ConanFile):
+                name = "consumer"
+                version = "1.0"
+                python_requires = "project/1.0"
+                python_requires_extend = "project.ProjectBase"
+                def generate(self):
+                    self.output.info("Msg1:{}!!!".format(self.msg1()))
+                    self.output.info("Msg2:{}!!!".format(self.msg2()))
+                """)
+        client.save({"company/conanfile.py": company,
+                     "project/conanfile.py": project,
+                     "consumer/conanfile.py": consumer})
+        client.run("export company")
+        client.run("export project")
+        client.run("install consumer")
+        assert "conanfile.py (consumer/1.0): Msg1:project!!!" in client.out
+        assert "conanfile.py (consumer/1.0): Msg2:company!!!" in client.out
diff --git a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
index 38a793f42..dcbc046aa 100644
--- a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
+++ b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py
@@ -401,12 +401,17 @@ def test_cmake_presets_multiconfig():
     presets = json.loads(client.load("CMakePresets.json"))
     assert len(presets["buildPresets"]) == 1
     assert presets["buildPresets"][0]["configuration"] == "Release"
+    assert len(presets["testPresets"]) == 1
+    assert presets["testPresets"][0]["configuration"] == "Release"
 
     client.run("install mylib/1.0@ -g CMakeToolchain -s build_type=Debug --profile:h=profile")
     presets = json.loads(client.load("CMakePresets.json"))
     assert len(presets["buildPresets"]) == 2
     assert presets["buildPresets"][0]["configuration"] == "Release"
     assert presets["buildPresets"][1]["configuration"] == "Debug"
+    assert len(presets["testPresets"]) == 2
+    assert presets["testPresets"][0]["configuration"] == "Release"
+    assert presets["testPresets"][1]["configuration"] == "Debug"
 
     client.run("install mylib/1.0@ -g CMakeToolchain -s build_type=RelWithDebInfo "
                "--profile:h=profile")
@@ -417,6 +422,11 @@ def test_cmake_presets_multiconfig():
     assert presets["buildPresets"][1]["configuration"] == "Debug"
     assert presets["buildPresets"][2]["configuration"] == "RelWithDebInfo"
     assert presets["buildPresets"][3]["configuration"] == "MinSizeRel"
+    assert len(presets["testPresets"]) == 4
+    assert presets["testPresets"][0]["configuration"] == "Release"
+    assert presets["testPresets"][1]["configuration"] == "Debug"
+    assert presets["testPresets"][2]["configuration"] == "RelWithDebInfo"
+    assert presets["testPresets"][3]["configuration"] == "MinSizeRel"
 
     # Repeat one
     client.run("install mylib/1.0@ -g CMakeToolchain -s build_type=Debug --profile:h=profile")
@@ -428,6 +438,12 @@ def test_cmake_presets_multiconfig():
     assert presets["buildPresets"][2]["configuration"] == "RelWithDebInfo"
     assert presets["buildPresets"][3]["configuration"] == "MinSizeRel"
 
+    assert len(presets["testPresets"]) == 4
+    assert presets["testPresets"][0]["configuration"] == "Release"
+    assert presets["testPresets"][1]["configuration"] == "Debug"
+    assert presets["testPresets"][2]["configuration"] == "RelWithDebInfo"
+    assert presets["testPresets"][3]["configuration"] == "MinSizeRel"
+
     assert len(presets["configurePresets"]) == 1
     assert presets["configurePresets"][0]["name"] == "default"
 
@@ -453,6 +469,9 @@ def test_cmake_presets_singleconfig():
     assert len(presets["buildPresets"]) == 1
     assert presets["buildPresets"][0]["configurePreset"] == "release"
 
+    assert len(presets["testPresets"]) == 1
+    assert presets["testPresets"][0]["configurePreset"] == "release"
+
     # Now two configurePreset, but named correctly
     client.run("install mylib/1.0@ -g CMakeToolchain -s build_type=Debug --profile:h=profile")
     presets = json.loads(client.load("CMakePresets.json"))
@@ -462,6 +481,9 @@ def test_cmake_presets_singleconfig():
     assert len(presets["buildPresets"]) == 2
     assert presets["buildPresets"][1]["configurePreset"] == "debug"
 
+    assert len(presets["testPresets"]) == 2
+    assert presets["testPresets"][1]["configurePreset"] == "debug"
+
     # Repeat configuration, it shouldn't add a new one
     client.run("install mylib/1.0@ -g CMakeToolchain -s build_type=Debug --profile:h=profile")
     presets = json.loads(client.load("CMakePresets.json"))
@@ -876,6 +898,38 @@ def test_cmake_layout_toolchain_folder():
     c.run("install . -s os=Linux -s compiler=gcc -s compiler.version=7 -s build_type=Debug "
           "-s compiler.libcxx=libstdc++11 -s arch=x86 "
           "-c tools.cmake.cmake_layout:build_folder_vars='[\"settings.arch\", \"settings.build_type\"]'")
-    print(c.out)
     assert os.path.exists(os.path.join(c.current_folder,
                                        "build/x86-debug/generators/conan_toolchain.cmake"))
+
+
+def test_test_package_layout():
+    """
+    test that the ``test_package`` folder also follows the cmake_layout and the
+    build_folder_vars
+    """
+    client = TestClient()
+    test_conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.cmake import cmake_layout
+
+        class Conan(ConanFile):
+            settings = "os", "arch", "compiler", "build_type"
+            generators = "CMakeToolchain"
+            test_type = "explicit"
+
+            def requirements(self):
+                self.requires(self.tested_reference_str)
+
+            def layout(self):
+                cmake_layout(self)
+
+            def test(self):
+                pass
+    """)
+    client.save({"conanfile.py": GenConanfile("pkg", "0.1"),
+                 "test_package/conanfile.py": test_conanfile})
+    config = "-c tools.cmake.cmake_layout:build_folder_vars='[\"settings.compiler.cppstd\"]'"
+    client.run(f"create . {config} -s compiler.cppstd=14")
+    client.run(f"create . {config} -s compiler.cppstd=17")
+    assert os.path.exists(os.path.join(client.current_folder, "test_package/build/14"))
+    assert os.path.exists(os.path.join(client.current_folder, "test_package/build/17"))