Repository: conan-io/conan
Pull request #18082 (2025-04-04 18:42:43)

improve coverage
Changelog: Omit
Docs: Omit


Add a few tests to improve coverage, and also fix minor hidden bug (low impact)


Patch:
diff --git a/conan/api/subapi/cache.py b/conan/api/subapi/cache.py
index b42715070..c188f5fad 100644
--- a/conan/api/subapi/cache.py
+++ b/conan/api/subapi/cache.py
@@ -24,7 +24,7 @@ class CacheAPI:
 
     def export_path(self, ref: RecipeReference):
         cache = PkgCache(self.conan_api.cache_folder, self.conan_api.config.global_conf)
-        ref.revision = None if ref.revision == "latest" else ref.revision
+        ref = _resolve_latest_ref(cache, ref)
         ref_layout = cache.recipe_layout(ref)
         return _check_folder_existence(ref, "export", ref_layout.export())
 
@@ -36,13 +36,13 @@ class CacheAPI:
 
     def export_source_path(self, ref: RecipeReference):
         cache = PkgCache(self.conan_api.cache_folder, self.conan_api.config.global_conf)
-        ref.revision = None if ref.revision == "latest" else ref.revision
+        ref = _resolve_latest_ref(cache, ref)
         ref_layout = cache.recipe_layout(ref)
         return _check_folder_existence(ref, "export_sources", ref_layout.export_sources())
 
     def source_path(self, ref: RecipeReference):
         cache = PkgCache(self.conan_api.cache_folder, self.conan_api.config.global_conf)
-        ref.revision = None if ref.revision == "latest" else ref.revision
+        ref = _resolve_latest_ref(cache, ref)
         ref_layout = cache.recipe_layout(ref)
         return _check_folder_existence(ref, "source", ref_layout.source())
 
diff --git a/conan/cli/cli.py b/conan/cli/cli.py
index 6c94be4c4..4a9ed3ece 100644
--- a/conan/cli/cli.py
+++ b/conan/cli/cli.py
@@ -45,6 +45,7 @@ class Cli:
             Cli._builtin_commands = self._commands.copy()
         else:
             self._commands = Cli._builtin_commands.copy()
+            self._groups = defaultdict(list)
             for k, v in self._commands.items():  # Fill groups data too
                 self._groups[v.group].append(k)
 
diff --git a/conan/internal/api/uploader.py b/conan/internal/api/uploader.py
index ce057893e..8a014732e 100644
--- a/conan/internal/api/uploader.py
+++ b/conan/internal/api/uploader.py
@@ -82,9 +82,9 @@ class PackagePreparator:
         self._app = app
         self._global_conf = global_conf
 
-    def prepare(self, upload_bundle, enabled_remotes):
+    def prepare(self, pkg_list, enabled_remotes):
         local_url = self._global_conf.get("core.scm:local_url", choices=["allow", "block"])
-        for ref, bundle in upload_bundle.refs().items():
+        for ref, bundle in pkg_list.refs().items():
             layout = self._app.cache.recipe_layout(ref)
             conanfile_path = layout.conanfile()
             conanfile = self._app.loader.load_basic(conanfile_path)
@@ -97,7 +97,7 @@ class PackagePreparator:
 
             if bundle.get("upload"):
                 self._prepare_recipe(ref, bundle, conanfile, enabled_remotes)
-            for pref, prev_bundle in upload_bundle.prefs(ref, bundle).items():
+            for pref, prev_bundle in pkg_list.prefs(ref, bundle).items():
                 if prev_bundle.get("upload"):
                     self._prepare_package(pref, prev_bundle)
 
diff --git a/conan/internal/cache/cache.py b/conan/internal/cache/cache.py
index 8363cc4f5..bf7d64d2b 100644
--- a/conan/internal/cache/cache.py
+++ b/conan/internal/cache/cache.py
@@ -111,10 +111,8 @@ class PkgCache:
     def recipe_layout(self, ref: RecipeReference):
         """ the revision must exists, the folder must exist
         """
-        if ref.revision is None:  # Latest one
-            ref_data = self._db.get_latest_recipe(ref)
-        else:
-            ref_data = self._db.get_recipe(ref)
+        assert ref.revision is not None
+        ref_data = self._db.get_recipe(ref)
         ref_path = ref_data.get("path")
         ref = ref_data.get("ref")  # new revision with timestamp
         return RecipeLayout(ref, os.path.join(self._base_folder, ref_path))
diff --git a/conan/internal/cache/integrity_check.py b/conan/internal/cache/integrity_check.py
index 58dec88ea..b1148e612 100644
--- a/conan/internal/cache/integrity_check.py
+++ b/conan/internal/cache/integrity_check.py
@@ -20,11 +20,11 @@ class IntegrityChecker:
     def __init__(self, cache):
         self._cache = cache
 
-    def check(self, upload_data):
+    def check(self, pkg_list):
         corrupted = False
-        for ref, recipe_bundle in upload_data.refs().items():
+        for ref, recipe_bundle in pkg_list.refs().items():
             corrupted = self._recipe_corrupted(ref) or corrupted
-            for pref, prev_bundle in upload_data.prefs(ref, recipe_bundle).items():
+            for pref, prev_bundle in pkg_list.prefs(ref, recipe_bundle).items():
                 corrupted = self._package_corrupted(pref) or corrupted
         if corrupted:
             raise ConanException("There are corrupted artifacts, check the error logs")
diff --git a/conan/internal/default_settings.py b/conan/internal/default_settings.py
index 51b2b5d55..0be97dce2 100644
--- a/conan/internal/default_settings.py
+++ b/conan/internal/default_settings.py
@@ -146,7 +146,8 @@ compiler:
         cstd: [null, 99, gnu99, 11, gnu11, 17, gnu17, 23, gnu23]
     intel-cc:
         version: ["2021.1", "2021.2", "2021.3", "2021.4", "2022.1", "2022.2",
-                  "2022.3", "2023.0", "2023.1", "2023.2", "2024.0",]
+                  "2022.3", "2023.0", "2023.1", "2023.2", "2024.0", "2024.1",
+                  "2025.0", "2025.1"]
         update: [null, ANY]
         mode: ["icx", "classic", "dpcpp"]
         libcxx: [null, libstdc++, libstdc++11, libc++]
diff --git a/conan/internal/model/conf.py b/conan/internal/model/conf.py
index 3e3e95959..4cb9d7c29 100644
--- a/conan/internal/model/conf.py
+++ b/conan/internal/model/conf.py
@@ -7,7 +7,9 @@ import fnmatch
 from collections import OrderedDict
 
 from conan.errors import ConanException
+from conan.internal.model.options import _PackageOption
 from conan.internal.model.recipe_ref import ref_matches
+from conan.internal.model.settings import SettingsItem
 
 BUILT_IN_CONFS = {
     "core:required_conan_version": "Raise if current version does not match the defined range.",
@@ -155,7 +157,7 @@ class _ConfVarPlaceHolder:
     pass
 
 
-class _ConfValue(object):
+class _ConfValue:
 
     def __init__(self, name, value, path=False, update=None):
         if name != name.lower():
@@ -163,6 +165,8 @@ class _ConfValue(object):
         self._name = name
         self._value = value
         self._value_type = type(value)
+        if isinstance(value, (_PackageOption, SettingsItem)):
+            raise ConanException(f"Invalid 'conf' type, please use Python types (int, str, ...)")
         self._path = path
         self._update = update
 
@@ -220,6 +224,8 @@ class _ConfValue(object):
         if isinstance(value, list):
             self._value.extend(value)
         else:
+            if isinstance(value, (_PackageOption, SettingsItem)):
+                raise ConanException(f"Invalid 'conf' type, please use Python types (int, str, ...)")
             self._value.append(value)
 
     def prepend(self, value):
@@ -229,6 +235,8 @@ class _ConfValue(object):
         if isinstance(value, list):
             self._value = value + self._value
         else:
+            if isinstance(value, (_PackageOption, SettingsItem)):
+                raise ConanException(f"Invalid 'conf' type, please use Python types (int, str, ...)")
             self._value.insert(0, value)
 
     def compose_conf_value(self, other):
diff --git a/conan/internal/model/layout.py b/conan/internal/model/layout.py
index 5ddacf543..f03c42efd 100644
--- a/conan/internal/model/layout.py
+++ b/conan/internal/model/layout.py
@@ -32,7 +32,7 @@ class Layouts(object):
         self.package = PartialLayout()
 
 
-class Folders(object):
+class Folders:
 
     def __init__(self):
         self._base_source = None
@@ -60,9 +60,6 @@ class Folders(object):
         self.subproject = None
         self.build_folder_vars = None
 
-    def __repr__(self):
-        return str(self.__dict__)
-
     def set_base_folders(self, conanfile_folder, output_folder):
         """ this methods can be used for defining all the base folders in the
         local flow (conan install, source, build), where only the current conanfile location
diff --git a/conan/tools/microsoft/layout.py b/conan/tools/microsoft/layout.py
index 6ac491f47..6a4c73133 100644
--- a/conan/tools/microsoft/layout.py
+++ b/conan/tools/microsoft/layout.py
@@ -26,11 +26,11 @@ def vs_layout(conanfile):
         raise ConanException("The 'vs_layout' requires the 'arch' setting")
 
     if arch != "None" and arch != "x86":
-        arch = msbuild_arch(arch)
-        if not arch:
-            raise ConanException("The 'vs_layout' doesn't "
-                                 "work with the arch '{}'".format(arch))
-        bindirs = os.path.join(arch, build_type)
+        msvc_arch = msbuild_arch(arch)
+        if not msvc_arch:
+            raise ConanException(f"The 'vs_layout' doesn't work with the arch '{arch}'. "
+                                 "Accepted architectures: 'x86', 'x86_64', 'armv7', 'armv8'")
+        bindirs = os.path.join(msvc_arch, build_type)
     else:
         bindirs = build_type
 
diff --git a/conans/client/graph/proxy.py b/conans/client/graph/proxy.py
index 72b927516..c2d416437 100644
--- a/conans/client/graph/proxy.py
+++ b/conans/client/graph/proxy.py
@@ -38,7 +38,9 @@ class ConanProxy:
 
         # check if it there's any revision of this recipe in the local cache
         try:
-            recipe_layout = self._cache.recipe_layout(reference)
+            cache_ref = self._cache.get_latest_recipe_reference(reference) \
+                if reference.revision is None else reference
+            recipe_layout = self._cache.recipe_layout(cache_ref)
             ref = recipe_layout.reference  # latest revision if it was not defined
         except ConanException:
             # NOT in disk, must be retrieved from remotes


Test patch:
diff --git a/conan/test/utils/tools.py b/conan/test/utils/tools.py
index 27fdb204c..ac819dcf5 100644
--- a/conan/test/utils/tools.py
+++ b/conan/test/utils/tools.py
@@ -700,6 +700,8 @@ class TestClient:
 
     def get_latest_ref_layout(self, ref) -> RecipeLayout:
         """Get the latest RecipeLayout given a file reference"""
+        if not ref.revision:
+            ref = self.cache.get_latest_recipe_reference(ref)
         ref_layout = self.cache.recipe_layout(ref)
         return ref_layout
 
diff --git a/test/functional/toolchains/cmake/cmakedeps/test_link_order.py b/test/functional/toolchains/cmake/cmakedeps/test_link_order.py
index 61f7332e9..16f1cb4f1 100644
--- a/test/functional/toolchains/cmake/cmakedeps/test_link_order.py
+++ b/test/functional/toolchains/cmake/cmakedeps/test_link_order.py
@@ -216,8 +216,8 @@ def _validate_link_order(libs):
     assert libs.index(prefix + 'Z2' + ext) < libs.index('system_lib' + ext_system)
 
     if platform.system() == "Darwin":
-       assert libs.index('liblibz.a') < libs.index('Carbon')
-       assert libs.index('libZ2.a') < libs.index('Carbon')
+        assert libs.index('liblibz.a') < libs.index('Carbon')
+        assert libs.index('libZ2.a') < libs.index('Carbon')
 
 
 def _get_link_order_from_cmake(content):
@@ -263,12 +263,13 @@ def _get_link_order_from_xcode(content):
 
     # Find the right Release block in the XCode file
     results = re.finditer(r'/\* Release \*/ = {', content)
+    header_found = False
     for r in results:
         release_section = content[r.start():].split("name = Release;", 1)[0]
         if "-headerpad_max_install_names" in release_section:
+            header_found = True
             break
-    else:
-        raise Exception("Cannot find the Release block linking the expected libraries")
+    assert header_found, "Cannot find the Release block linking the expected libraries"
 
     start_key = '-Wl,-headerpad_max_install_names'
     end_key = ');'
diff --git a/test/integration/command/custom_commands_test.py b/test/integration/command/custom_commands_test.py
index cea9c844b..a8824f3e1 100644
--- a/test/integration/command/custom_commands_test.py
+++ b/test/integration/command/custom_commands_test.py
@@ -10,7 +10,7 @@ from conan.test.utils.tools import TestClient
 from conan.test.utils.env import environment_update
 
 
-class TestCustomCommands:
+class TestCustomCommandsErrors:
 
     def test_import_error_custom_command(self):
         mycommand = textwrap.dedent("""
@@ -48,6 +48,25 @@ class TestCustomCommands:
         client.run("config home")
         assert client.cache_folder in client.out
 
+    def test_import_error_bad_name(self):
+        mycommand = textwrap.dedent("""
+            from conan.cli.command import conan_command, conan_subcommand
+            @conan_command(group="custom commands")
+            def mycommand(conan_api, parser, *args, **kwargs):
+                \""" custom \"""
+            @conan_subcommand()
+            def mysubcmd(conan_api, parser, *args, **kwargs):
+                \""" mysubcmd \"""
+            """)
+
+        c = TestClient()
+        c.save_home({"extensions/commands/cmd_mycommand.py": mycommand})
+        # Call to any other command, it will fail loading the custom command
+        c.run("list *")
+        assert "The name for the subcommand method should begin with the main command name" in c.out
+
+
+class TestCustomCommands:
     def test_simple_custom_command(self):
         mycommand = textwrap.dedent("""
             import json
diff --git a/test/integration/command/help_test.py b/test/integration/command/help_test.py
index 6003a8cda..2af1c84a0 100644
--- a/test/integration/command/help_test.py
+++ b/test/integration/command/help_test.py
@@ -20,6 +20,11 @@ class TestHelp:
         assert "The most similar command is" in c.out
         assert "install" in c.out
 
+        c.run("remole", assert_error=True)
+        assert "The most similar commands are" in c.out
+        assert "remove" in c.out
+        assert "remote" in c.out
+
     def test_help(self):
         c = TestClient()
         c.run("-h")
diff --git a/test/integration/conan_api/test_cli.py b/test/integration/conan_api/test_cli.py
index f6c5877e8..ebdfcdcd8 100644
--- a/test/integration/conan_api/test_cli.py
+++ b/test/integration/conan_api/test_cli.py
@@ -1,5 +1,10 @@
+import os
+
+import pytest
+
 from conan.api.conan_api import ConanAPI
-from conan.cli.cli import Cli
+from conan.cli.cli import Cli, main
+from conan.test.utils.env import environment_update
 from conan.test.utils.mocks import RedirectedTestOutput
 from conan.test.utils.test_files import temp_folder
 from conan.test.utils.tools import redirect_output
@@ -22,6 +27,13 @@ def test_cli():
         cli2.run(["list", "*"])
         cli.run(["list", "*"])
 
+    stdout = RedirectedTestOutput()
+    stderr = RedirectedTestOutput()
+    with redirect_output(stderr, stdout):
+        cli.run()
+    # Running without args shows help, but doesn't error
+    assert "Consumer commands" in stdout.getvalue()
+
 
 def test_basic_api():
     api = ConanAPI(cache_folder=temp_folder())
@@ -36,3 +48,12 @@ def test_api_command():
     cli.add_commands()
     result = api.command.run(["remote", "list"])
     assert result[0].name == "conancenter"
+
+
+def test_main():
+    cache_folder = os.path.join(temp_folder(), "custom")
+    with environment_update({"CONAN_HOME": cache_folder}):
+        with pytest.raises(SystemExit) as e:
+            main(["list", "*"])
+        assert e.type == SystemExit
+        assert e.value.code == 0  # success
diff --git a/test/integration/configuration/conf/test_conf_from_br.py b/test/integration/configuration/conf/test_conf_from_br.py
index 4b9aafe9e..a0d067526 100644
--- a/test/integration/configuration/conf/test_conf_from_br.py
+++ b/test/integration/configuration/conf/test_conf_from_br.py
@@ -1,5 +1,7 @@
 import textwrap
 
+import pytest
+
 from conan.test.assets.genconanfile import GenConanfile
 from conan.test.utils.tools import TestClient
 
@@ -266,3 +268,28 @@ def test_conf_both_build_and_host():
     client.run("install app --build=missing -s:h build_type=Debug -s:b build_type=Release")
     assert "myprotobuf/0.1: MYCONF build: Release" in client.out
     assert "myprotobuf/0.1: MYCONF host: Debug" in client.out
+
+
+@pytest.mark.parametrize("conf", ['define("user.myorg:myconf", self.options.myopt)',
+                                  'define("user.myorg:myconf", self.settings.os)',
+                                  'append("user.myorg:myconf", self.settings.os)',
+                                  'prepend("user.myorg:myconf", self.settings.os)'])
+def test_error_bad_types(conf):
+    c = TestClient(light=True)
+    conanfile = textwrap.dedent(f"""
+        from conan import ConanFile
+        class Pkg(ConanFile):
+            name = "tool"
+            version = "0.1"
+            settings = "os"
+            options = {{"myopt": [True, False]}}
+            default_options = {{"myopt": True}}
+            def package_info(self):
+                self.conf_info.{conf}
+        """)
+    c.save({"conanfile.py": conanfile})
+    c.run("create .", assert_error=True)
+    assert "ERROR: tool/0.1: Error in package_info() method, line 10"
+    assert f'self.conf_info.{conf}' in c.out
+    assert "Invalid 'conf' type, please use Python types (int, str, ...)" in c.out
+
diff --git a/test/integration/toolchains/microsoft/test_vs_layout.py b/test/integration/toolchains/microsoft/test_vs_layout.py
index d406440cd..196dcf25f 100644
--- a/test/integration/toolchains/microsoft/test_vs_layout.py
+++ b/test/integration/toolchains/microsoft/test_vs_layout.py
@@ -20,3 +20,41 @@ def test_vs_layout_subproject():
     c.save({"pkg/conanfile.py": conanfile})
     c.run("install pkg")
     assert os.path.isfile(os.path.join(c.current_folder, "pkg", "conan", "conantoolchain.props"))
+
+
+def test_vs_layout_error():
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.microsoft import vs_layout
+        class Pkg(ConanFile):
+            settings = "os", "compiler", "arch"
+            def layout(self):
+                vs_layout(self)
+        """)
+    c.save({"conanfile.py": conanfile})
+    c.run("install .", assert_error=True)
+    assert "The 'vs_layout' requires the 'build_type' setting" in c.out
+    conanfile = textwrap.dedent("""
+            from conan import ConanFile
+            from conan.tools.microsoft import vs_layout
+            class Pkg(ConanFile):
+                settings = "os", "compiler", "build_type"
+                def layout(self):
+                    vs_layout(self)
+            """)
+    c.save({"conanfile.py": conanfile})
+    c.run("install .", assert_error=True)
+    assert "The 'vs_layout' requires the 'arch' setting" in c.out
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.microsoft import vs_layout
+        class Pkg(ConanFile):
+            settings = "os", "compiler", "build_type", "arch"
+            def layout(self):
+                vs_layout(self)
+        """)
+    c.save({"conanfile.py": conanfile})
+    c.run("install . -s arch=riscv64", assert_error=True)
+    assert "The 'vs_layout' doesn't work with the arch 'riscv64'" in c.out
+    assert "Accepted architectures: 'x86', 'x86_64', 'armv7', 'armv8'" in c.out
diff --git a/test/unittests/client/build/cpp_std_flags_test.py b/test/unittests/client/build/cpp_std_flags_test.py
index f4f54c6d5..b90cfa8a4 100644
--- a/test/unittests/client/build/cpp_std_flags_test.py
+++ b/test/unittests/client/build/cpp_std_flags_test.py
@@ -304,11 +304,15 @@ class CompilerFlagsTest(unittest.TestCase):
         self.assertEqual(_make_cppstd_default("msvc", "193"), "14")
 
     def test_intel_cppstd_flag(self):
-        self.assertEqual(_make_cppstd_flag("intel-cc", "19.1", "gnu98"), '-std=gnu++98')
-        self.assertEqual(_make_cppstd_flag("intel-cc", "19.1", "11"), '-std=c++11')
-        self.assertEqual(_make_cppstd_flag("intel-cc", "19.1", "14"), '-std=c++14')
-        self.assertEqual(_make_cppstd_flag("intel-cc", "19.1", "17"), '-std=c++17')
-        self.assertEqual(_make_cppstd_flag("intel-cc", "19.1", "20"), '-std=c++20')
+        self.assertEqual(_make_cppstd_flag("intel-cc", "2024.1", "gnu98"), '-std=gnu++98')
+        self.assertEqual(_make_cppstd_flag("intel-cc", "2024.1", "11"), '-std=c++11')
+        self.assertEqual(_make_cppstd_flag("intel-cc", "2024.1", "14"), '-std=c++14')
+        self.assertEqual(_make_cppstd_flag("intel-cc", "2024.1", "17"), '-std=c++17')
+        self.assertEqual(_make_cppstd_flag("intel-cc", "2024.1", "20"), '-std=c++20')
+
+        self.assertEqual(_make_cppstd_flag("intel-cc", "2025.1", "17"), '-std=c++17')
+        self.assertEqual(_make_cppstd_flag("intel-cc", "2025.1", "20"), '-std=c++20')
+        self.assertEqual(_make_cppstd_flag("intel-cc", "2025.1", "23"), '-std=c++2b')
 
     def test_mcst_lcc_cppstd_defaults(self):
         self.assertEqual(_make_cppstd_default("mcst-lcc", "1.19"), "gnu98")
diff --git a/test/unittests/model/test_conf.py b/test/unittests/model/test_conf.py
index a20b132b0..9afa00248 100644
--- a/test/unittests/model/test_conf.py
+++ b/test/unittests/model/test_conf.py
@@ -298,7 +298,7 @@ def test_conf_get_check_type_and_default():
     assert c.get("user.company.cpu:jobs", check_type=str) == "5"  # smart conversion
     with pytest.raises(ConanException) as exc_info:
         c.get("user.company.cpu:jobs", check_type=list)
-        assert "[conf] user.company.cpu:jobs must be a list-like object." in str(exc_info.value)
+    assert "[conf] user.company.cpu:jobs must be a list-like object." in str(exc_info.value)
     # Check type does not affect to default value
     assert c.get("user.non:existing", default=0, check_type=dict) == 0
     assert c.get("zlib:user.company.check:shared") is None  # unset value
@@ -315,16 +315,16 @@ def test_conf_get_check_type_and_default():
             "The value 'True' introduced is a bool object") in str(exc_info.value)
     with pytest.raises(ConanException) as exc_info:
         c.get("user:bad_value_0", check_type=bool)
-    assert ("[conf] user:bad_value_0 must be a boolean-like object (true/false, 1/0, on/off) and value 'Fasle' does not match it.") in str(exc_info.value)
+    assert "[conf] user:bad_value_0 must be a boolean-like object (true/false, 1/0, on/off) and value 'Fasle' does not match it." in str(exc_info.value)
     with pytest.raises(ConanException) as exc_info:
         c.get("user:bad_value_1", check_type=bool)
-    assert ("[conf] user:bad_value_1 must be a boolean-like object (true/false, 1/0, on/off) and value 'ture' does not match it.") in str(exc_info.value)
+    assert "[conf] user:bad_value_1 must be a boolean-like object (true/false, 1/0, on/off) and value 'ture' does not match it." in str(exc_info.value)
     with pytest.raises(ConanException) as exc_info:
         c.get("user:bad_value_2", check_type=bool)
-    assert ("[conf] user:bad_value_2 must be a boolean-like object (true/false, 1/0, on/off) and value '10' does not match it.") in str(exc_info.value)
+    assert "[conf] user:bad_value_2 must be a boolean-like object (true/false, 1/0, on/off) and value '10' does not match it." in str(exc_info.value)
     with pytest.raises(ConanException) as exc_info:
         c.get("user:bad_value_3", check_type=bool)
-    assert ("[conf] user:bad_value_3 must be a boolean-like object (true/false, 1/0, on/off) and value '\'00\'' does not match it.") in str(exc_info.value)
+    assert "[conf] user:bad_value_3 must be a boolean-like object (true/false, 1/0, on/off) and value '\'00\'' does not match it." in str(exc_info.value)
 
 
 def test_conf_pop():
diff --git a/test/unittests/tools/cmake/test_cmaketoolchain.py b/test/unittests/tools/cmake/test_cmaketoolchain.py
index 26169dece..2850f1fb4 100644
--- a/test/unittests/tools/cmake/test_cmaketoolchain.py
+++ b/test/unittests/tools/cmake/test_cmaketoolchain.py
@@ -644,7 +644,7 @@ def test_apple_cmake_osx_sysroot_sdk_mandatory(os, arch, expected_sdk):
 
     with pytest.raises(ConanException) as excinfo:
         CMakeToolchain(c).content()
-        assert "Please, specify a suitable value for os.sdk." % expected_sdk in str(excinfo.value)
+    assert "Please, specify a suitable value for os.sdk." in str(excinfo.value)
 
 
 def test_compilers_block(conanfile):