Repository: conan-io/conan
Pull request #15999 (2024-04-01 22:57:52)

test_package only if build missing
Changelog: Feature: New ``conan create --test-missing`` syntax to optionally run the ``test_package`` only when the package is actually created (useful with ``--build=missing``)
Docs: https://github.com/conan-io/docs/pull/XXXX

Close https://github.com/conan-io/conan/issues/15967


Patch:
diff --git a/conan/cli/commands/create.py b/conan/cli/commands/create.py
index 36c42372f..36b967882 100644
--- a/conan/cli/commands/create.py
+++ b/conan/cli/commands/create.py
@@ -9,6 +9,7 @@ from conan.cli.formatters.graph import format_graph_json
 from conan.cli.printers import print_profiles
 from conan.cli.printers.graph import print_graph_packages, print_graph_basic
 from conan.errors import ConanException
+from conans.client.graph.graph import BINARY_BUILD
 from conans.util.files import mkdir
 
 
@@ -26,11 +27,17 @@ def create(conan_api, parser, *args):
     parser.add_argument("-tf", "--test-folder", action=OnceArgument,
                         help='Alternative test folder name. By default it is "test_package". '
                              'Use "" to skip the test stage')
+    parser.add_argument("-tm", "--test-missing", action='store_true', default=False,
+                        help='Run the test_package checks only if the package is built from source'
+                             ' but not if it already existed (using --build=missing)')
     parser.add_argument("-bt", "--build-test", action="append",
                         help="Same as '--build' but only for the test_package requires. By default"
                              " if not specified it will take the '--build' value if specified")
     args = parser.parse_args(*args)
 
+    if args.test_missing and args.test_folder == "":
+        raise ConanException('--test-folder="" is incompatible with --test-missing')
+
     cwd = os.getcwd()
     path = conan_api.local.get_conanfile_path(args.path, cwd, py=True)
     test_conanfile_path = _get_test_conanfile_path(args.test_folder, path)
@@ -91,6 +98,11 @@ def create(conan_api, parser, *args):
         lockfile = conan_api.lockfile.update_lockfile(lockfile, deps_graph, args.lockfile_packages,
                                                       clean=args.lockfile_clean)
 
+    # If the user provide --test-missing and the binary was not built from source, skip test_package
+    if args.test_missing and deps_graph.root.dependencies\
+            and deps_graph.root.dependencies[0].dst.binary != BINARY_BUILD:
+        test_conanfile_path = None  # disable it
+
     if test_conanfile_path:
         # TODO: We need arguments for:
         #  - decide update policy "--test_package_update"


Test patch:
diff --git a/conans/test/integration/command/create_test.py b/conans/test/integration/command/create_test.py
index 1a8218d24..9158370df 100644
--- a/conans/test/integration/command/create_test.py
+++ b/conans/test/integration/command/create_test.py
@@ -811,3 +811,53 @@ def test_python_requires_with_test_package():
     c.run("create .")
     # Ensure that creating a deps graph does not break the testing
     assert "pyreq/1.0 (test package): 42!!!" in c.out
+
+
+def test_create_test_package_only_build():
+    c = TestClient()
+    c.save({"conanfile.py": GenConanfile("pkg", "0.1"),
+            "test_package/conanfile.py": GenConanfile().with_test("self.output.info('TEST1!!!')"),
+            "test_package2/conanfile.py": GenConanfile().with_test("self.output.info('TEST2!!!')")})
+    # As it doesn't exist, it builds and test it
+    c.run("create . -tm")
+    assert "Testing the package" in c.out
+    assert "TEST1!!!" in c.out
+    # this will not create the binary, so it won't test it
+    c.run("create . --build=missing --test-missing")
+    assert "Testing the package" not in c.out
+    assert "TEST" not in c.out
+    c.run("create . -tf=test_package2 -tm")
+    assert "Testing the package" in c.out
+    assert "TEST2!!!" in c.out
+    assert "TEST1!!!" not in c.out
+    c.run("create . -tf=test_package2 --build=missing --test-missing")
+    assert "Testing the package" not in c.out
+    assert "TEST2!!!" not in c.out
+    assert "TEST1!!!" not in c.out
+
+    # error
+    c.run("create . -tm -tf=", assert_error=True)
+    assert '--test-folder="" is incompatible with --test-missing' in c.out
+
+
+def test_create_test_package_only_build_python_require():
+    c = TestClient()
+    test = textwrap.dedent("""
+        from conan import ConanFile
+
+        class Tool(ConanFile):
+            python_requires = "tested_reference_str"
+            def test(self):
+                self.output.info("TEST!!!!")
+        """)
+    c.save({"conanfile.py": GenConanfile("pkg", "0.1").with_package_type("python-require"),
+            "test_package/conanfile.py": test})
+    c.run("create .")
+    assert "Testing the package" in c.out
+    assert "pkg/0.1 (test package): TEST!!!" in c.out
+    c.run("create . -tm")
+    assert "Testing the package" in c.out
+    assert "pkg/0.1 (test package): TEST!!!" in c.out
+    c.run("create . -tm --build=missing")
+    assert "Testing the package" in c.out
+    assert "pkg/0.1 (test package): TEST!!!" in c.out