From 2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:47:55 +0200 Subject: Adding upstream version 0.70.1+ds1. Signed-off-by: Daniel Baumann --- tests/testsuite/advanced_env.rs | 35 + tests/testsuite/alt_registry.rs | 1434 +++++ tests/testsuite/artifact_dep.rs | 2898 +++++++++ tests/testsuite/bad_config.rs | 1488 +++++ tests/testsuite/bad_manifest_path.rs | 386 ++ tests/testsuite/bench.rs | 1673 +++++ tests/testsuite/binary_name.rs | 301 + tests/testsuite/build.rs | 6404 ++++++++++++++++++++ tests/testsuite/build_plan.rs | 222 + tests/testsuite/build_script.rs | 5168 ++++++++++++++++ tests/testsuite/build_script_env.rs | 241 + tests/testsuite/build_script_extra_link_arg.rs | 376 ++ tests/testsuite/cache_messages.rs | 488 ++ tests/testsuite/cargo_add/add-basic.in/Cargo.toml | 5 + tests/testsuite/cargo_add/add-basic.in/src/lib.rs | 0 tests/testsuite/cargo_add/add_basic/in | 1 + tests/testsuite/cargo_add/add_basic/mod.rs | 25 + tests/testsuite/cargo_add/add_basic/out/Cargo.toml | 8 + tests/testsuite/cargo_add/add_basic/stderr.log | 2 + tests/testsuite/cargo_add/add_basic/stdout.log | 0 tests/testsuite/cargo_add/add_multiple/in | 1 + tests/testsuite/cargo_add/add_multiple/mod.rs | 25 + .../cargo_add/add_multiple/out/Cargo.toml | 9 + tests/testsuite/cargo_add/add_multiple/stderr.log | 3 + tests/testsuite/cargo_add/add_multiple/stdout.log | 0 .../cargo_add/add_normalized_name_external/in | 1 + .../cargo_add/add_normalized_name_external/mod.rs | 25 + .../add_normalized_name_external/out/Cargo.toml | 9 + .../add_normalized_name_external/stderr.log | 18 + .../add_normalized_name_external/stdout.log | 0 tests/testsuite/cargo_add/build/in | 1 + tests/testsuite/cargo_add/build/mod.rs | 25 + tests/testsuite/cargo_add/build/out/Cargo.toml | 9 + tests/testsuite/cargo_add/build/stderr.log | 3 + tests/testsuite/cargo_add/build/stdout.log | 0 .../build_prefer_existing_version/in/Cargo.toml | 9 + .../in/dependency/Cargo.toml | 9 + .../in/dependency/src/lib.rs | 0 .../build_prefer_existing_version/in/src/lib.rs | 0 .../cargo_add/build_prefer_existing_version/mod.rs | 28 + .../build_prefer_existing_version/out/Cargo.toml | 12 + .../out/dependency/Cargo.toml | 9 + .../build_prefer_existing_version/stderr.log | 4 + .../build_prefer_existing_version/stdout.log | 0 .../cargo_add/change_rename_target/in/Cargo.toml | 8 + .../cargo_add/change_rename_target/in/src/lib.rs | 0 .../cargo_add/change_rename_target/mod.rs | 25 + .../cargo_add/change_rename_target/out/Cargo.toml | 8 + .../cargo_add/change_rename_target/stderr.log | 2 + .../cargo_add/change_rename_target/stdout.log | 0 tests/testsuite/cargo_add/default_features/in | 1 + tests/testsuite/cargo_add/default_features/mod.rs | 25 + .../cargo_add/default_features/out/Cargo.toml | 9 + .../cargo_add/default_features/stderr.log | 3 + .../cargo_add/default_features/stdout.log | 0 .../deprecated_default_features/in/Cargo.toml | 8 + .../deprecated_default_features/in/src/lib.rs | 0 .../cargo_add/deprecated_default_features/mod.rs | 25 + .../deprecated_default_features/out/Cargo.toml | 8 + .../deprecated_default_features/stderr.log | 1 + .../deprecated_default_features/stdout.log | 0 .../cargo_add/deprecated_section/in/Cargo.toml | 11 + .../cargo_add/deprecated_section/in/src/lib.rs | 0 .../testsuite/cargo_add/deprecated_section/mod.rs | 25 + .../cargo_add/deprecated_section/out/Cargo.toml | 11 + .../cargo_add/deprecated_section/stderr.log | 1 + .../cargo_add/deprecated_section/stdout.log | 0 .../detect_workspace_inherit/in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 3 + .../in/dependency/src/lib.rs | 0 .../detect_workspace_inherit/in/primary/Cargo.toml | 3 + .../detect_workspace_inherit/in/primary/src/lib.rs | 0 .../cargo_add/detect_workspace_inherit/mod.rs | 25 + .../detect_workspace_inherit/out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 3 + .../out/primary/Cargo.toml | 6 + .../cargo_add/detect_workspace_inherit/stderr.log | 1 + .../cargo_add/detect_workspace_inherit/stdout.log | 0 .../in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 14 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 3 + .../in/primary/src/lib.rs | 0 .../detect_workspace_inherit_features/mod.rs | 25 + .../out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 14 + .../out/primary/Cargo.toml | 6 + .../detect_workspace_inherit_features/stderr.log | 10 + .../detect_workspace_inherit_features/stdout.log | 0 .../in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 3 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 3 + .../in/primary/src/lib.rs | 0 .../detect_workspace_inherit_optional/mod.rs | 25 + .../out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 3 + .../out/primary/Cargo.toml | 6 + .../detect_workspace_inherit_optional/stderr.log | 1 + .../detect_workspace_inherit_optional/stdout.log | 0 tests/testsuite/cargo_add/dev/in | 1 + tests/testsuite/cargo_add/dev/mod.rs | 25 + tests/testsuite/cargo_add/dev/out/Cargo.toml | 9 + tests/testsuite/cargo_add/dev/stderr.log | 3 + tests/testsuite/cargo_add/dev/stdout.log | 0 tests/testsuite/cargo_add/dev_build_conflict/in | 1 + .../testsuite/cargo_add/dev_build_conflict/mod.rs | 25 + .../cargo_add/dev_build_conflict/out/Cargo.toml | 5 + .../cargo_add/dev_build_conflict/stderr.log | 7 + .../cargo_add/dev_build_conflict/stdout.log | 0 .../dev_prefer_existing_version/in/Cargo.toml | 9 + .../in/dependency/Cargo.toml | 9 + .../in/dependency/src/lib.rs | 0 .../dev_prefer_existing_version/in/src/lib.rs | 0 .../cargo_add/dev_prefer_existing_version/mod.rs | 25 + .../dev_prefer_existing_version/out/Cargo.toml | 12 + .../out/dependency/Cargo.toml | 9 + .../dev_prefer_existing_version/stderr.log | 4 + .../dev_prefer_existing_version/stdout.log | 0 tests/testsuite/cargo_add/dry_run/in | 1 + tests/testsuite/cargo_add/dry_run/mod.rs | 25 + tests/testsuite/cargo_add/dry_run/out/Cargo.toml | 5 + tests/testsuite/cargo_add/dry_run/stderr.log | 3 + tests/testsuite/cargo_add/dry_run/stdout.log | 0 tests/testsuite/cargo_add/features/in | 1 + tests/testsuite/cargo_add/features/mod.rs | 25 + tests/testsuite/cargo_add/features/out/Cargo.toml | 8 + tests/testsuite/cargo_add/features/stderr.log | 7 + tests/testsuite/cargo_add/features/stdout.log | 0 tests/testsuite/cargo_add/features_empty/in | 1 + tests/testsuite/cargo_add/features_empty/mod.rs | 25 + .../cargo_add/features_empty/out/Cargo.toml | 8 + .../testsuite/cargo_add/features_empty/stderr.log | 7 + .../testsuite/cargo_add/features_empty/stdout.log | 0 .../cargo_add/features_multiple_occurrences/in | 1 + .../cargo_add/features_multiple_occurrences/mod.rs | 25 + .../features_multiple_occurrences/out/Cargo.toml | 8 + .../features_multiple_occurrences/stderr.log | 7 + .../features_multiple_occurrences/stdout.log | 0 .../cargo_add/features_preserve/in/Cargo.toml | 8 + .../cargo_add/features_preserve/in/src/lib.rs | 0 tests/testsuite/cargo_add/features_preserve/mod.rs | 25 + .../cargo_add/features_preserve/out/Cargo.toml | 8 + .../cargo_add/features_preserve/stderr.log | 7 + .../cargo_add/features_preserve/stdout.log | 0 .../testsuite/cargo_add/features_spaced_values/in | 1 + .../cargo_add/features_spaced_values/mod.rs | 25 + .../features_spaced_values/out/Cargo.toml | 8 + .../cargo_add/features_spaced_values/stderr.log | 7 + .../cargo_add/features_spaced_values/stdout.log | 0 tests/testsuite/cargo_add/features_unknown/in | 1 + tests/testsuite/cargo_add/features_unknown/mod.rs | 25 + .../cargo_add/features_unknown/out/Cargo.toml | 5 + .../cargo_add/features_unknown/stderr.log | 5 + .../cargo_add/features_unknown/stdout.log | 0 .../cargo_add/features_unknown_no_features/in | 1 + .../cargo_add/features_unknown_no_features/mod.rs | 25 + .../features_unknown_no_features/out/Cargo.toml | 5 + .../features_unknown_no_features/stderr.log | 4 + .../features_unknown_no_features/stdout.log | 0 tests/testsuite/cargo_add/git/in | 1 + tests/testsuite/cargo_add/git/mod.rs | 34 + tests/testsuite/cargo_add/git/out/Cargo.toml | 8 + tests/testsuite/cargo_add/git/stderr.log | 3 + tests/testsuite/cargo_add/git/stdout.log | 0 tests/testsuite/cargo_add/git_branch/in | 1 + tests/testsuite/cargo_add/git_branch/mod.rs | 37 + .../testsuite/cargo_add/git_branch/out/Cargo.toml | 8 + tests/testsuite/cargo_add/git_branch/stderr.log | 3 + tests/testsuite/cargo_add/git_branch/stdout.log | 0 tests/testsuite/cargo_add/git_conflicts_namever/in | 1 + .../cargo_add/git_conflicts_namever/mod.rs | 29 + .../cargo_add/git_conflicts_namever/out/Cargo.toml | 5 + .../cargo_add/git_conflicts_namever/stderr.log | 1 + .../cargo_add/git_conflicts_namever/stdout.log | 0 tests/testsuite/cargo_add/git_dev/in | 1 + tests/testsuite/cargo_add/git_dev/mod.rs | 34 + tests/testsuite/cargo_add/git_dev/out/Cargo.toml | 8 + tests/testsuite/cargo_add/git_dev/stderr.log | 3 + tests/testsuite/cargo_add/git_dev/stdout.log | 0 tests/testsuite/cargo_add/git_inferred_name/in | 1 + tests/testsuite/cargo_add/git_inferred_name/mod.rs | 34 + .../cargo_add/git_inferred_name/out/Cargo.toml | 8 + .../cargo_add/git_inferred_name/stderr.log | 4 + .../cargo_add/git_inferred_name/stdout.log | 0 .../cargo_add/git_inferred_name_multiple/in | 1 + .../cargo_add/git_inferred_name_multiple/mod.rs | 74 + .../git_inferred_name_multiple/out/Cargo.toml | 5 + .../git_inferred_name_multiple/stderr.log | 5 + .../git_inferred_name_multiple/stdout.log | 0 tests/testsuite/cargo_add/git_multiple_names/in | 1 + .../testsuite/cargo_add/git_multiple_names/mod.rs | 39 + .../cargo_add/git_multiple_names/out/Cargo.toml | 9 + .../cargo_add/git_multiple_names/stderr.log | 4 + .../cargo_add/git_multiple_names/stdout.log | 0 tests/testsuite/cargo_add/git_normalized_name/in | 1 + .../testsuite/cargo_add/git_normalized_name/mod.rs | 34 + .../cargo_add/git_normalized_name/out/Cargo.toml | 5 + .../cargo_add/git_normalized_name/stderr.log | 2 + .../cargo_add/git_normalized_name/stdout.log | 0 .../testsuite/cargo_add/git_registry/in/Cargo.toml | 5 + .../testsuite/cargo_add/git_registry/in/src/lib.rs | 0 tests/testsuite/cargo_add/git_registry/mod.rs | 40 + .../cargo_add/git_registry/out/Cargo.toml | 8 + tests/testsuite/cargo_add/git_registry/stderr.log | 6 + tests/testsuite/cargo_add/git_registry/stdout.log | 0 tests/testsuite/cargo_add/git_rev/in | 1 + tests/testsuite/cargo_add/git_rev/mod.rs | 36 + tests/testsuite/cargo_add/git_rev/out/Cargo.toml | 8 + tests/testsuite/cargo_add/git_rev/stderr.log | 3 + tests/testsuite/cargo_add/git_rev/stdout.log | 0 tests/testsuite/cargo_add/git_tag/in | 1 + tests/testsuite/cargo_add/git_tag/mod.rs | 36 + tests/testsuite/cargo_add/git_tag/out/Cargo.toml | 8 + tests/testsuite/cargo_add/git_tag/stderr.log | 3 + tests/testsuite/cargo_add/git_tag/stdout.log | 0 tests/testsuite/cargo_add/infer_prerelease/in | 1 + tests/testsuite/cargo_add/infer_prerelease/mod.rs | 25 + .../cargo_add/infer_prerelease/out/Cargo.toml | 8 + .../cargo_add/infer_prerelease/stderr.log | 2 + .../cargo_add/infer_prerelease/stdout.log | 0 tests/testsuite/cargo_add/invalid_arg/in | 1 + tests/testsuite/cargo_add/invalid_arg/mod.rs | 26 + .../testsuite/cargo_add/invalid_arg/out/Cargo.toml | 5 + tests/testsuite/cargo_add/invalid_arg/stderr.log | 9 + tests/testsuite/cargo_add/invalid_arg/stdout.log | 0 tests/testsuite/cargo_add/invalid_git_external/in | 1 + .../cargo_add/invalid_git_external/mod.rs | 28 + .../cargo_add/invalid_git_external/out/Cargo.toml | 5 + .../cargo_add/invalid_git_external/stderr.log | 12 + .../cargo_add/invalid_git_external/stdout.log | 0 tests/testsuite/cargo_add/invalid_git_name/in | 1 + tests/testsuite/cargo_add/invalid_git_name/mod.rs | 34 + .../cargo_add/invalid_git_name/out/Cargo.toml | 5 + .../cargo_add/invalid_git_name/stderr.log | 2 + .../cargo_add/invalid_git_name/stdout.log | 0 .../invalid_key_inherit_dependency/in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 3 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 3 + .../in/primary/src/lib.rs | 0 .../invalid_key_inherit_dependency/mod.rs | 23 + .../invalid_key_inherit_dependency/out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 3 + .../out/primary/Cargo.toml | 3 + .../invalid_key_inherit_dependency/stderr.log | 1 + .../invalid_key_inherit_dependency/stdout.log | 0 .../in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 3 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 6 + .../in/primary/src/lib.rs | 0 .../mod.rs | 23 + .../out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 3 + .../out/primary/Cargo.toml | 6 + .../stderr.log | 1 + .../stdout.log | 0 .../in/Cargo.toml | 5 + .../in/dependency-alt/Cargo.toml | 3 + .../in/dependency-alt/src/lib.rs | 0 .../in/dependency/Cargo.toml | 3 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 3 + .../in/primary/src/lib.rs | 0 .../invalid_key_rename_inherit_dependency/mod.rs | 23 + .../out/Cargo.toml | 5 + .../out/dependency-alt/Cargo.toml | 3 + .../out/dependency/Cargo.toml | 3 + .../out/primary/Cargo.toml | 3 + .../stderr.log | 1 + .../stdout.log | 0 .../cargo_add/invalid_manifest/in/Cargo.toml | 8 + .../cargo_add/invalid_manifest/in/src/lib.rs | 0 tests/testsuite/cargo_add/invalid_manifest/mod.rs | 25 + .../cargo_add/invalid_manifest/out/Cargo.toml | 8 + .../cargo_add/invalid_manifest/stderr.log | 12 + .../cargo_add/invalid_manifest/stdout.log | 0 tests/testsuite/cargo_add/invalid_name_external/in | 1 + .../cargo_add/invalid_name_external/mod.rs | 25 + .../cargo_add/invalid_name_external/out/Cargo.toml | 5 + .../cargo_add/invalid_name_external/stderr.log | 2 + .../cargo_add/invalid_name_external/stdout.log | 0 tests/testsuite/cargo_add/invalid_path/in | 1 + tests/testsuite/cargo_add/invalid_path/mod.rs | 25 + .../cargo_add/invalid_path/out/Cargo.toml | 5 + tests/testsuite/cargo_add/invalid_path/stderr.log | 10 + tests/testsuite/cargo_add/invalid_path/stdout.log | 0 .../invalid_path_name/in/dependency/Cargo.toml | 5 + .../invalid_path_name/in/dependency/src/lib.rs | 0 .../invalid_path_name/in/primary/Cargo.toml | 5 + .../invalid_path_name/in/primary/src/lib.rs | 0 tests/testsuite/cargo_add/invalid_path_name/mod.rs | 25 + .../invalid_path_name/out/dependency/Cargo.toml | 5 + .../invalid_path_name/out/primary/Cargo.toml | 5 + .../cargo_add/invalid_path_name/stderr.log | 1 + .../cargo_add/invalid_path_name/stdout.log | 0 tests/testsuite/cargo_add/invalid_path_self/in | 1 + tests/testsuite/cargo_add/invalid_path_self/mod.rs | 25 + .../cargo_add/invalid_path_self/out/Cargo.toml | 5 + .../cargo_add/invalid_path_self/stderr.log | 2 + .../cargo_add/invalid_path_self/stdout.log | 0 tests/testsuite/cargo_add/invalid_target_empty/in | 1 + .../cargo_add/invalid_target_empty/mod.rs | 25 + .../cargo_add/invalid_target_empty/out/Cargo.toml | 5 + .../cargo_add/invalid_target_empty/stderr.log | 3 + .../cargo_add/invalid_target_empty/stdout.log | 0 tests/testsuite/cargo_add/invalid_vers/in | 1 + tests/testsuite/cargo_add/invalid_vers/mod.rs | 25 + .../cargo_add/invalid_vers/out/Cargo.toml | 5 + tests/testsuite/cargo_add/invalid_vers/stderr.log | 4 + tests/testsuite/cargo_add/invalid_vers/stdout.log | 0 tests/testsuite/cargo_add/list_features/in | 1 + tests/testsuite/cargo_add/list_features/mod.rs | 25 + .../cargo_add/list_features/out/Cargo.toml | 8 + tests/testsuite/cargo_add/list_features/stderr.log | 7 + tests/testsuite/cargo_add/list_features/stdout.log | 0 .../cargo_add/list_features_path/in/Cargo.toml | 2 + .../list_features_path/in/dependency/Cargo.toml | 13 + .../list_features_path/in/dependency/src/lib.rs | 0 .../list_features_path/in/optional/Cargo.toml | 6 + .../list_features_path/in/optional/src/lib.rs | 0 .../list_features_path/in/primary/Cargo.toml | 3 + .../list_features_path/in/primary/src/lib.rs | 0 .../testsuite/cargo_add/list_features_path/mod.rs | 25 + .../cargo_add/list_features_path/out/Cargo.toml | 2 + .../list_features_path/out/dependency/Cargo.toml | 13 + .../list_features_path/out/primary/Cargo.toml | 6 + .../cargo_add/list_features_path/stderr.log | 7 + .../cargo_add/list_features_path/stdout.log | 0 .../list_features_path_no_default/in/Cargo.toml | 2 + .../in/dependency/Cargo.toml | 13 + .../in/dependency/src/lib.rs | 0 .../in/optional/Cargo.toml | 6 + .../in/optional/src/lib.rs | 0 .../in/primary/Cargo.toml | 3 + .../in/primary/src/lib.rs | 0 .../cargo_add/list_features_path_no_default/mod.rs | 30 + .../list_features_path_no_default/out/Cargo.toml | 2 + .../out/dependency/Cargo.toml | 13 + .../out/primary/Cargo.toml | 6 + .../list_features_path_no_default/stderr.log | 7 + .../list_features_path_no_default/stdout.log | 0 tests/testsuite/cargo_add/locked_changed/in | 1 + tests/testsuite/cargo_add/locked_changed/mod.rs | 25 + .../cargo_add/locked_changed/out/Cargo.toml | 5 + .../testsuite/cargo_add/locked_changed/stderr.log | 3 + .../testsuite/cargo_add/locked_changed/stdout.log | 0 .../cargo_add/locked_unchanged/in/Cargo.lock | 16 + .../cargo_add/locked_unchanged/in/Cargo.toml | 8 + .../cargo_add/locked_unchanged/in/src/lib.rs | 0 tests/testsuite/cargo_add/locked_unchanged/mod.rs | 25 + .../cargo_add/locked_unchanged/out/Cargo.toml | 8 + .../cargo_add/locked_unchanged/stderr.log | 2 + .../cargo_add/locked_unchanged/stdout.log | 0 .../cargo_add/lockfile_updated/in/Cargo.lock | 17 + .../cargo_add/lockfile_updated/in/Cargo.toml | 8 + .../cargo_add/lockfile_updated/in/src/lib.rs | 0 tests/testsuite/cargo_add/lockfile_updated/mod.rs | 25 + .../cargo_add/lockfile_updated/out/Cargo.lock | 23 + .../cargo_add/lockfile_updated/out/Cargo.toml | 9 + .../cargo_add/lockfile_updated/stderr.log | 2 + .../cargo_add/lockfile_updated/stdout.log | 0 .../cargo_add/manifest_path_package/in/Cargo.toml | 2 + .../manifest_path_package/in/dependency/Cargo.toml | 3 + .../manifest_path_package/in/dependency/src/lib.rs | 0 .../manifest_path_package/in/primary/Cargo.toml | 3 + .../manifest_path_package/in/primary/src/lib.rs | 0 .../cargo_add/manifest_path_package/mod.rs | 31 + .../cargo_add/manifest_path_package/out/Cargo.toml | 2 + .../out/dependency/Cargo.toml | 3 + .../manifest_path_package/out/primary/Cargo.toml | 6 + .../cargo_add/manifest_path_package/stderr.log | 1 + .../cargo_add/manifest_path_package/stdout.log | 0 .../merge_activated_features/in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 14 + .../in/dependency/src/lib.rs | 0 .../merge_activated_features/in/primary/Cargo.toml | 6 + .../merge_activated_features/in/primary/src/lib.rs | 0 .../cargo_add/merge_activated_features/mod.rs | 23 + .../merge_activated_features/out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 14 + .../out/primary/Cargo.toml | 6 + .../cargo_add/merge_activated_features/stderr.log | 10 + .../cargo_add/merge_activated_features/stdout.log | 0 tests/testsuite/cargo_add/mod.rs | 196 + .../cargo_add/multiple_conflicts_with_features/in | 1 + .../multiple_conflicts_with_features/mod.rs | 25 + .../out/Cargo.toml | 5 + .../multiple_conflicts_with_features/stderr.log | 1 + .../multiple_conflicts_with_features/stdout.log | 0 .../cargo_add/multiple_conflicts_with_rename/in | 1 + .../multiple_conflicts_with_rename/mod.rs | 25 + .../multiple_conflicts_with_rename/out/Cargo.toml | 5 + .../multiple_conflicts_with_rename/stderr.log | 1 + .../multiple_conflicts_with_rename/stdout.log | 0 tests/testsuite/cargo_add/namever/in | 1 + tests/testsuite/cargo_add/namever/mod.rs | 25 + tests/testsuite/cargo_add/namever/out/Cargo.toml | 10 + tests/testsuite/cargo_add/namever/stderr.log | 4 + tests/testsuite/cargo_add/namever/stdout.log | 0 tests/testsuite/cargo_add/no_args/in | 1 + tests/testsuite/cargo_add/no_args/mod.rs | 24 + tests/testsuite/cargo_add/no_args/out/Cargo.toml | 5 + tests/testsuite/cargo_add/no_args/stderr.log | 8 + tests/testsuite/cargo_add/no_args/stdout.log | 0 tests/testsuite/cargo_add/no_default_features/in | 1 + .../testsuite/cargo_add/no_default_features/mod.rs | 25 + .../cargo_add/no_default_features/out/Cargo.toml | 9 + .../cargo_add/no_default_features/stderr.log | 3 + .../cargo_add/no_default_features/stdout.log | 0 tests/testsuite/cargo_add/no_optional/in | 1 + tests/testsuite/cargo_add/no_optional/mod.rs | 25 + .../testsuite/cargo_add/no_optional/out/Cargo.toml | 9 + tests/testsuite/cargo_add/no_optional/stderr.log | 3 + tests/testsuite/cargo_add/no_optional/stdout.log | 0 tests/testsuite/cargo_add/offline_empty_cache/in | 1 + .../testsuite/cargo_add/offline_empty_cache/mod.rs | 25 + .../cargo_add/offline_empty_cache/out/Cargo.toml | 5 + .../cargo_add/offline_empty_cache/stderr.log | 1 + .../cargo_add/offline_empty_cache/stdout.log | 0 tests/testsuite/cargo_add/optional/in | 1 + tests/testsuite/cargo_add/optional/mod.rs | 25 + tests/testsuite/cargo_add/optional/out/Cargo.toml | 9 + tests/testsuite/cargo_add/optional/stderr.log | 3 + tests/testsuite/cargo_add/optional/stdout.log | 0 .../overwrite_default_features/in/Cargo.toml | 9 + .../overwrite_default_features/in/src/lib.rs | 0 .../cargo_add/overwrite_default_features/mod.rs | 25 + .../overwrite_default_features/out/Cargo.toml | 9 + .../overwrite_default_features/stderr.log | 3 + .../overwrite_default_features/stdout.log | 0 .../in/Cargo.toml | 9 + .../in/src/lib.rs | 0 .../mod.rs | 25 + .../out/Cargo.toml | 9 + .../stderr.log | 3 + .../stdout.log | 0 .../cargo_add/overwrite_features/in/Cargo.toml | 8 + .../cargo_add/overwrite_features/in/src/lib.rs | 0 .../testsuite/cargo_add/overwrite_features/mod.rs | 25 + .../cargo_add/overwrite_features/out/Cargo.toml | 8 + .../cargo_add/overwrite_features/stderr.log | 7 + .../cargo_add/overwrite_features/stdout.log | 0 .../in/dependency/Cargo.toml | 5 + .../in/dependency/src/lib.rs | 0 .../overwrite_git_with_path/in/primary/Cargo.toml | 8 + .../overwrite_git_with_path/in/primary/src/lib.rs | 0 .../cargo_add/overwrite_git_with_path/mod.rs | 25 + .../out/dependency/Cargo.toml | 5 + .../overwrite_git_with_path/out/primary/Cargo.toml | 8 + .../cargo_add/overwrite_git_with_path/stderr.log | 1 + .../cargo_add/overwrite_git_with_path/stdout.log | 0 .../overwrite_inherit_features_noop/in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 6 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 6 + .../in/primary/src/lib.rs | 0 .../overwrite_inherit_features_noop/mod.rs | 23 + .../overwrite_inherit_features_noop/out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 6 + .../out/primary/Cargo.toml | 6 + .../overwrite_inherit_features_noop/stderr.log | 3 + .../overwrite_inherit_features_noop/stdout.log | 0 .../cargo_add/overwrite_inherit_noop/in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 3 + .../in/dependency/src/lib.rs | 0 .../overwrite_inherit_noop/in/primary/Cargo.toml | 6 + .../overwrite_inherit_noop/in/primary/src/lib.rs | 0 .../cargo_add/overwrite_inherit_noop/mod.rs | 25 + .../overwrite_inherit_noop/out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 3 + .../overwrite_inherit_noop/out/primary/Cargo.toml | 6 + .../cargo_add/overwrite_inherit_noop/stderr.log | 1 + .../cargo_add/overwrite_inherit_noop/stdout.log | 0 .../overwrite_inherit_optional_noop/in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 3 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 6 + .../in/primary/src/lib.rs | 0 .../overwrite_inherit_optional_noop/mod.rs | 25 + .../overwrite_inherit_optional_noop/out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 3 + .../out/primary/Cargo.toml | 6 + .../overwrite_inherit_optional_noop/stderr.log | 1 + .../overwrite_inherit_optional_noop/stdout.log | 0 .../overwrite_inline_features/in/Cargo.toml | 8 + .../overwrite_inline_features/in/src/lib.rs | 0 .../cargo_add/overwrite_inline_features/mod.rs | 27 + .../overwrite_inline_features/out/Cargo.toml | 9 + .../cargo_add/overwrite_inline_features/stderr.log | 8 + .../cargo_add/overwrite_inline_features/stdout.log | 0 .../overwrite_name_dev_noop/in/Cargo.toml | 9 + .../in/dependency/Cargo.toml | 9 + .../in/dependency/src/lib.rs | 0 .../overwrite_name_dev_noop/in/src/lib.rs | 0 .../cargo_add/overwrite_name_dev_noop/mod.rs | 25 + .../overwrite_name_dev_noop/out/Cargo.toml | 9 + .../out/dependency/Cargo.toml | 9 + .../cargo_add/overwrite_name_dev_noop/stderr.log | 4 + .../cargo_add/overwrite_name_dev_noop/stdout.log | 0 .../cargo_add/overwrite_name_noop/in/Cargo.toml | 9 + .../overwrite_name_noop/in/dependency/Cargo.toml | 9 + .../overwrite_name_noop/in/dependency/src/lib.rs | 0 .../cargo_add/overwrite_name_noop/in/src/lib.rs | 0 .../testsuite/cargo_add/overwrite_name_noop/mod.rs | 25 + .../cargo_add/overwrite_name_noop/out/Cargo.toml | 9 + .../overwrite_name_noop/out/dependency/Cargo.toml | 9 + .../cargo_add/overwrite_name_noop/stderr.log | 4 + .../cargo_add/overwrite_name_noop/stdout.log | 0 .../overwrite_no_default_features/in/Cargo.toml | 9 + .../overwrite_no_default_features/in/src/lib.rs | 0 .../cargo_add/overwrite_no_default_features/mod.rs | 25 + .../overwrite_no_default_features/out/Cargo.toml | 9 + .../overwrite_no_default_features/stderr.log | 3 + .../overwrite_no_default_features/stdout.log | 0 .../in/Cargo.toml | 9 + .../in/src/lib.rs | 0 .../mod.rs | 25 + .../out/Cargo.toml | 9 + .../stderr.log | 3 + .../stdout.log | 0 .../cargo_add/overwrite_no_optional/in/Cargo.toml | 9 + .../cargo_add/overwrite_no_optional/in/src/lib.rs | 0 .../cargo_add/overwrite_no_optional/mod.rs | 25 + .../cargo_add/overwrite_no_optional/out/Cargo.toml | 9 + .../cargo_add/overwrite_no_optional/stderr.log | 3 + .../cargo_add/overwrite_no_optional/stdout.log | 0 .../in/Cargo.toml | 9 + .../in/src/lib.rs | 0 .../overwrite_no_optional_with_optional/mod.rs | 25 + .../out/Cargo.toml | 9 + .../overwrite_no_optional_with_optional/stderr.log | 3 + .../overwrite_no_optional_with_optional/stdout.log | 0 .../cargo_add/overwrite_optional/in/Cargo.toml | 9 + .../cargo_add/overwrite_optional/in/src/lib.rs | 0 .../testsuite/cargo_add/overwrite_optional/mod.rs | 25 + .../cargo_add/overwrite_optional/out/Cargo.toml | 9 + .../cargo_add/overwrite_optional/stderr.log | 3 + .../cargo_add/overwrite_optional/stdout.log | 0 .../in/Cargo.toml | 13 + .../in/src/lib.rs | 0 .../overwrite_optional_with_no_optional/mod.rs | 25 + .../out/Cargo.toml | 13 + .../overwrite_optional_with_no_optional/stderr.log | 8 + .../overwrite_optional_with_no_optional/stdout.log | 0 .../cargo_add/overwrite_path_noop/in/Cargo.toml | 9 + .../overwrite_path_noop/in/dependency/Cargo.toml | 9 + .../overwrite_path_noop/in/dependency/src/lib.rs | 0 .../cargo_add/overwrite_path_noop/in/src/lib.rs | 0 .../testsuite/cargo_add/overwrite_path_noop/mod.rs | 25 + .../cargo_add/overwrite_path_noop/out/Cargo.toml | 9 + .../overwrite_path_noop/out/dependency/Cargo.toml | 9 + .../cargo_add/overwrite_path_noop/stderr.log | 4 + .../cargo_add/overwrite_path_noop/stdout.log | 0 .../in/dependency/Cargo.toml | 5 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 8 + .../in/primary/src/lib.rs | 0 .../cargo_add/overwrite_path_with_version/mod.rs | 25 + .../out/dependency/Cargo.toml | 5 + .../out/primary/Cargo.toml | 8 + .../overwrite_path_with_version/stderr.log | 2 + .../overwrite_path_with_version/stdout.log | 0 .../overwrite_preserves_inline_table/in/Cargo.toml | 8 + .../overwrite_preserves_inline_table/in/src/lib.rs | 0 .../overwrite_preserves_inline_table/mod.rs | 25 + .../out/Cargo.toml | 8 + .../overwrite_preserves_inline_table/stderr.log | 7 + .../overwrite_preserves_inline_table/stdout.log | 0 .../overwrite_rename_with_no_rename/in/Cargo.toml | 8 + .../overwrite_rename_with_no_rename/in/src/lib.rs | 0 .../overwrite_rename_with_no_rename/mod.rs | 25 + .../overwrite_rename_with_no_rename/out/Cargo.toml | 9 + .../overwrite_rename_with_no_rename/stderr.log | 2 + .../overwrite_rename_with_no_rename/stdout.log | 0 .../overwrite_rename_with_rename/in/Cargo.toml | 8 + .../overwrite_rename_with_rename/in/src/lib.rs | 0 .../cargo_add/overwrite_rename_with_rename/mod.rs | 25 + .../overwrite_rename_with_rename/out/Cargo.toml | 9 + .../overwrite_rename_with_rename/stderr.log | 2 + .../overwrite_rename_with_rename/stdout.log | 0 .../in/Cargo.toml | 8 + .../in/src/lib.rs | 0 .../overwrite_rename_with_rename_noop/mod.rs | 25 + .../out/Cargo.toml | 8 + .../overwrite_rename_with_rename_noop/stderr.log | 2 + .../overwrite_rename_with_rename_noop/stdout.log | 0 .../overwrite_version_with_git/in/Cargo.toml | 8 + .../overwrite_version_with_git/in/src/lib.rs | 0 .../cargo_add/overwrite_version_with_git/mod.rs | 34 + .../overwrite_version_with_git/out/Cargo.toml | 8 + .../overwrite_version_with_git/stderr.log | 3 + .../overwrite_version_with_git/stdout.log | 0 .../in/dependency/Cargo.toml | 5 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 8 + .../in/primary/src/lib.rs | 0 .../cargo_add/overwrite_version_with_path/mod.rs | 25 + .../out/dependency/Cargo.toml | 5 + .../out/primary/Cargo.toml | 8 + .../overwrite_version_with_path/stderr.log | 1 + .../overwrite_version_with_path/stdout.log | 0 .../cargo_add/overwrite_with_rename/in/Cargo.toml | 8 + .../cargo_add/overwrite_with_rename/in/src/lib.rs | 0 .../cargo_add/overwrite_with_rename/mod.rs | 25 + .../cargo_add/overwrite_with_rename/out/Cargo.toml | 9 + .../cargo_add/overwrite_with_rename/stderr.log | 2 + .../cargo_add/overwrite_with_rename/stdout.log | 0 .../overwrite_workspace_dep/in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 3 + .../in/dependency/src/lib.rs | 0 .../overwrite_workspace_dep/in/primary/Cargo.toml | 6 + .../overwrite_workspace_dep/in/primary/src/lib.rs | 0 .../cargo_add/overwrite_workspace_dep/mod.rs | 25 + .../overwrite_workspace_dep/out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 3 + .../overwrite_workspace_dep/out/primary/Cargo.toml | 6 + .../cargo_add/overwrite_workspace_dep/stderr.log | 1 + .../cargo_add/overwrite_workspace_dep/stdout.log | 0 .../overwrite_workspace_dep_features/in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 14 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 6 + .../in/primary/src/lib.rs | 0 .../overwrite_workspace_dep_features/mod.rs | 25 + .../out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 14 + .../out/primary/Cargo.toml | 6 + .../overwrite_workspace_dep_features/stderr.log | 10 + .../overwrite_workspace_dep_features/stdout.log | 0 .../cargo_add/path/in/dependency/Cargo.toml | 5 + .../cargo_add/path/in/dependency/src/lib.rs | 0 .../testsuite/cargo_add/path/in/primary/Cargo.toml | 5 + .../testsuite/cargo_add/path/in/primary/src/lib.rs | 0 tests/testsuite/cargo_add/path/mod.rs | 25 + .../cargo_add/path/out/dependency/Cargo.toml | 5 + .../cargo_add/path/out/primary/Cargo.toml | 8 + tests/testsuite/cargo_add/path/stderr.log | 1 + tests/testsuite/cargo_add/path/stdout.log | 0 .../cargo_add/path_dev/in/dependency/Cargo.toml | 5 + .../cargo_add/path_dev/in/dependency/src/lib.rs | 0 .../cargo_add/path_dev/in/primary/Cargo.toml | 5 + .../cargo_add/path_dev/in/primary/src/lib.rs | 0 tests/testsuite/cargo_add/path_dev/mod.rs | 25 + .../cargo_add/path_dev/out/dependency/Cargo.toml | 5 + .../cargo_add/path_dev/out/primary/Cargo.toml | 8 + tests/testsuite/cargo_add/path_dev/stderr.log | 1 + tests/testsuite/cargo_add/path_dev/stdout.log | 0 .../path_inferred_name/in/dependency/Cargo.toml | 5 + .../path_inferred_name/in/dependency/src/lib.rs | 0 .../path_inferred_name/in/primary/Cargo.toml | 5 + .../path_inferred_name/in/primary/src/lib.rs | 0 .../testsuite/cargo_add/path_inferred_name/mod.rs | 25 + .../path_inferred_name/out/dependency/Cargo.toml | 5 + .../path_inferred_name/out/primary/Cargo.toml | 8 + .../cargo_add/path_inferred_name/stderr.log | 1 + .../cargo_add/path_inferred_name/stdout.log | 0 .../in/Cargo.toml | 2 + .../in/dependency/Cargo.toml | 14 + .../in/dependency/src/lib.rs | 0 .../in/optional/Cargo.toml | 7 + .../in/optional/src/lib.rs | 0 .../in/primary/Cargo.toml | 3 + .../in/primary/src/lib.rs | 0 .../mod.rs | 25 + .../out/Cargo.toml | 2 + .../out/dependency/Cargo.toml | 14 + .../out/primary/Cargo.toml | 3 + .../stderr.log | 1 + .../stdout.log | 0 .../path_normalized_name/in/dependency/Cargo.toml | 5 + .../path_normalized_name/in/dependency/src/lib.rs | 0 .../path_normalized_name/in/primary/Cargo.toml | 5 + .../path_normalized_name/in/primary/src/lib.rs | 0 .../cargo_add/path_normalized_name/mod.rs | 25 + .../path_normalized_name/out/dependency/Cargo.toml | 5 + .../path_normalized_name/out/primary/Cargo.toml | 5 + .../cargo_add/path_normalized_name/stderr.log | 1 + .../cargo_add/path_normalized_name/stdout.log | 0 .../cargo_add/preserve_sorted/in/Cargo.toml | 9 + .../cargo_add/preserve_sorted/in/src/lib.rs | 0 tests/testsuite/cargo_add/preserve_sorted/mod.rs | 25 + .../cargo_add/preserve_sorted/out/Cargo.toml | 10 + .../testsuite/cargo_add/preserve_sorted/stderr.log | 2 + .../testsuite/cargo_add/preserve_sorted/stdout.log | 0 .../cargo_add/preserve_unsorted/in/Cargo.toml | 9 + .../cargo_add/preserve_unsorted/in/src/lib.rs | 0 tests/testsuite/cargo_add/preserve_unsorted/mod.rs | 25 + .../cargo_add/preserve_unsorted/out/Cargo.toml | 10 + .../cargo_add/preserve_unsorted/stderr.log | 2 + .../cargo_add/preserve_unsorted/stdout.log | 0 tests/testsuite/cargo_add/quiet/in | 1 + tests/testsuite/cargo_add/quiet/mod.rs | 25 + tests/testsuite/cargo_add/quiet/out/Cargo.toml | 8 + tests/testsuite/cargo_add/quiet/stderr.log | 0 tests/testsuite/cargo_add/quiet/stdout.log | 0 tests/testsuite/cargo_add/registry/in/Cargo.toml | 5 + tests/testsuite/cargo_add/registry/in/src/lib.rs | 0 tests/testsuite/cargo_add/registry/mod.rs | 25 + tests/testsuite/cargo_add/registry/out/Cargo.toml | 9 + tests/testsuite/cargo_add/registry/stderr.log | 3 + tests/testsuite/cargo_add/registry/stdout.log | 0 tests/testsuite/cargo_add/rename/in | 1 + tests/testsuite/cargo_add/rename/mod.rs | 25 + tests/testsuite/cargo_add/rename/out/Cargo.toml | 8 + tests/testsuite/cargo_add/rename/stderr.log | 2 + tests/testsuite/cargo_add/rename/stdout.log | 0 .../testsuite/cargo_add/require_weak/in/Cargo.toml | 11 + .../testsuite/cargo_add/require_weak/in/src/lib.rs | 0 tests/testsuite/cargo_add/require_weak/mod.rs | 25 + .../cargo_add/require_weak/out/Cargo.toml | 11 + tests/testsuite/cargo_add/require_weak/stderr.log | 7 + tests/testsuite/cargo_add/require_weak/stdout.log | 0 .../sorted_table_with_dotted_item/in/Cargo.toml | 13 + .../sorted_table_with_dotted_item/in/src/lib.rs | 0 .../cargo_add/sorted_table_with_dotted_item/mod.rs | 25 + .../sorted_table_with_dotted_item/out/Cargo.toml | 14 + .../sorted_table_with_dotted_item/stderr.log | 2 + .../sorted_table_with_dotted_item/stdout.log | 0 tests/testsuite/cargo_add/target/in | 1 + tests/testsuite/cargo_add/target/mod.rs | 25 + tests/testsuite/cargo_add/target/out/Cargo.toml | 9 + tests/testsuite/cargo_add/target/stderr.log | 3 + tests/testsuite/cargo_add/target/stdout.log | 0 tests/testsuite/cargo_add/target_cfg/in | 1 + tests/testsuite/cargo_add/target_cfg/mod.rs | 25 + .../testsuite/cargo_add/target_cfg/out/Cargo.toml | 9 + tests/testsuite/cargo_add/target_cfg/stderr.log | 3 + tests/testsuite/cargo_add/target_cfg/stdout.log | 0 .../unknown_inherited_feature/in/Cargo.toml | 5 + .../in/dependency/Cargo.toml | 20 + .../in/dependency/src/lib.rs | 0 .../in/primary/Cargo.toml | 6 + .../in/primary/src/lib.rs | 0 .../cargo_add/unknown_inherited_feature/mod.rs | 23 + .../unknown_inherited_feature/out/Cargo.toml | 5 + .../out/dependency/Cargo.toml | 20 + .../out/primary/Cargo.toml | 6 + .../cargo_add/unknown_inherited_feature/stderr.log | 7 + .../cargo_add/unknown_inherited_feature/stdout.log | 0 tests/testsuite/cargo_add/vers/in | 1 + tests/testsuite/cargo_add/vers/mod.rs | 25 + tests/testsuite/cargo_add/vers/out/Cargo.toml | 8 + tests/testsuite/cargo_add/vers/stderr.log | 2 + tests/testsuite/cargo_add/vers/stdout.log | 0 .../cargo_add/workspace_name/in/Cargo.toml | 2 + .../workspace_name/in/dependency/Cargo.toml | 3 + .../workspace_name/in/dependency/src/lib.rs | 0 .../cargo_add/workspace_name/in/primary/Cargo.toml | 3 + .../cargo_add/workspace_name/in/primary/src/lib.rs | 0 tests/testsuite/cargo_add/workspace_name/mod.rs | 25 + .../cargo_add/workspace_name/out/Cargo.toml | 2 + .../workspace_name/out/dependency/Cargo.toml | 3 + .../workspace_name/out/primary/Cargo.toml | 6 + .../testsuite/cargo_add/workspace_name/stderr.log | 1 + .../testsuite/cargo_add/workspace_name/stdout.log | 0 .../cargo_add/workspace_path/in/Cargo.toml | 2 + .../workspace_path/in/dependency/Cargo.toml | 3 + .../workspace_path/in/dependency/src/lib.rs | 0 .../cargo_add/workspace_path/in/primary/Cargo.toml | 3 + .../cargo_add/workspace_path/in/primary/src/lib.rs | 0 tests/testsuite/cargo_add/workspace_path/mod.rs | 25 + .../cargo_add/workspace_path/out/Cargo.toml | 2 + .../workspace_path/out/dependency/Cargo.toml | 3 + .../workspace_path/out/primary/Cargo.toml | 6 + .../testsuite/cargo_add/workspace_path/stderr.log | 1 + .../testsuite/cargo_add/workspace_path/stdout.log | 0 .../cargo_add/workspace_path_dev/in/Cargo.toml | 2 + .../workspace_path_dev/in/dependency/Cargo.toml | 3 + .../workspace_path_dev/in/dependency/src/lib.rs | 0 .../workspace_path_dev/in/primary/Cargo.toml | 3 + .../workspace_path_dev/in/primary/src/lib.rs | 0 .../testsuite/cargo_add/workspace_path_dev/mod.rs | 25 + .../cargo_add/workspace_path_dev/out/Cargo.toml | 2 + .../workspace_path_dev/out/dependency/Cargo.toml | 3 + .../workspace_path_dev/out/primary/Cargo.toml | 6 + .../cargo_add/workspace_path_dev/stderr.log | 1 + .../cargo_add/workspace_path_dev/stdout.log | 0 tests/testsuite/cargo_alias_config.rs | 434 ++ tests/testsuite/cargo_command.rs | 535 ++ tests/testsuite/cargo_config.rs | 520 ++ tests/testsuite/cargo_env_config.rs | 181 + tests/testsuite/cargo_features.rs | 711 +++ tests/testsuite/cargo_remove/avoid_empty_tables/in | 1 + .../cargo_remove/avoid_empty_tables/mod.rs | 25 + .../cargo_remove/avoid_empty_tables/out/Cargo.toml | 23 + .../cargo_remove/avoid_empty_tables/stderr.log | 2 + .../cargo_remove/avoid_empty_tables/stdout.log | 0 tests/testsuite/cargo_remove/build/in | 1 + tests/testsuite/cargo_remove/build/mod.rs | 25 + tests/testsuite/cargo_remove/build/out/Cargo.toml | 21 + tests/testsuite/cargo_remove/build/stderr.log | 2 + tests/testsuite/cargo_remove/build/stdout.log | 0 tests/testsuite/cargo_remove/dev/in | 1 + tests/testsuite/cargo_remove/dev/mod.rs | 25 + tests/testsuite/cargo_remove/dev/out/Cargo.toml | 23 + tests/testsuite/cargo_remove/dev/stderr.log | 2 + tests/testsuite/cargo_remove/dev/stdout.log | 0 tests/testsuite/cargo_remove/dry_run/in | 1 + tests/testsuite/cargo_remove/dry_run/mod.rs | 25 + .../testsuite/cargo_remove/dry_run/out/Cargo.toml | 24 + .../testsuite/cargo_remove/dry_run/out/src/lib.rs | 1 + tests/testsuite/cargo_remove/dry_run/stderr.log | 2 + tests/testsuite/cargo_remove/dry_run/stdout.log | 0 tests/testsuite/cargo_remove/gc_patch/mod.rs | 72 + .../testsuite/cargo_remove/gc_patch/out/Cargo.toml | 9 + .../testsuite/cargo_remove/gc_patch/out/src/lib.rs | 0 tests/testsuite/cargo_remove/gc_patch/stderr.log | 3 + tests/testsuite/cargo_remove/gc_patch/stdout.log | 0 .../cargo_remove/gc_profile/in/Cargo.toml | 36 + .../cargo_remove/gc_profile/in/src/lib.rs | 1 + tests/testsuite/cargo_remove/gc_profile/mod.rs | 25 + .../cargo_remove/gc_profile/out/Cargo.toml | 32 + tests/testsuite/cargo_remove/gc_profile/stderr.log | 2 + tests/testsuite/cargo_remove/gc_profile/stdout.log | 0 .../cargo_remove/gc_replace/in/Cargo.toml | 5 + .../gc_replace/in/my-package/Cargo.toml | 26 + .../gc_replace/in/my-package/src/main.rs | 1 + tests/testsuite/cargo_remove/gc_replace/mod.rs | 25 + .../cargo_remove/gc_replace/out/Cargo.toml | 2 + .../gc_replace/out/my-package/Cargo.toml | 25 + .../gc_replace/out/my-package/src/main.rs | 1 + tests/testsuite/cargo_remove/gc_replace/stderr.log | 2 + tests/testsuite/cargo_remove/gc_replace/stdout.log | 0 tests/testsuite/cargo_remove/invalid_arg/in | 1 + tests/testsuite/cargo_remove/invalid_arg/mod.rs | 26 + .../cargo_remove/invalid_arg/out/Cargo.toml | 24 + .../testsuite/cargo_remove/invalid_arg/stderr.log | 7 + .../testsuite/cargo_remove/invalid_arg/stdout.log | 0 tests/testsuite/cargo_remove/invalid_dep/in | 1 + tests/testsuite/cargo_remove/invalid_dep/mod.rs | 25 + .../cargo_remove/invalid_dep/out/Cargo.toml | 24 + .../testsuite/cargo_remove/invalid_dep/stderr.log | 2 + .../testsuite/cargo_remove/invalid_dep/stdout.log | 0 tests/testsuite/cargo_remove/invalid_package/in | 1 + .../testsuite/cargo_remove/invalid_package/mod.rs | 25 + .../cargo_remove/invalid_package/out/Cargo.toml | 5 + .../invalid_package/out/dep-a/Cargo.toml | 23 + .../invalid_package/out/dep-a/src/lib.rs | 1 + .../invalid_package/out/dep-b/Cargo.toml | 23 + .../invalid_package/out/dep-b/src/lib.rs | 1 + .../cargo_remove/invalid_package/stderr.log | 1 + .../cargo_remove/invalid_package/stdout.log | 0 .../cargo_remove/invalid_package_multiple/in | 1 + .../cargo_remove/invalid_package_multiple/mod.rs | 25 + .../invalid_package_multiple/out/Cargo.toml | 5 + .../invalid_package_multiple/out/dep-a/Cargo.toml | 23 + .../invalid_package_multiple/out/dep-a/src/lib.rs | 1 + .../invalid_package_multiple/out/dep-b/Cargo.toml | 23 + .../invalid_package_multiple/out/dep-b/src/lib.rs | 1 + .../invalid_package_multiple/stderr.log | 2 + .../invalid_package_multiple/stdout.log | 0 tests/testsuite/cargo_remove/invalid_section/in | 1 + .../testsuite/cargo_remove/invalid_section/mod.rs | 25 + .../cargo_remove/invalid_section/out/Cargo.toml | 24 + .../cargo_remove/invalid_section/stderr.log | 2 + .../cargo_remove/invalid_section/stdout.log | 0 .../testsuite/cargo_remove/invalid_section_dep/in | 1 + .../cargo_remove/invalid_section_dep/mod.rs | 25 + .../invalid_section_dep/out/Cargo.toml | 24 + .../cargo_remove/invalid_section_dep/stderr.log | 2 + .../cargo_remove/invalid_section_dep/stdout.log | 0 tests/testsuite/cargo_remove/invalid_target/in | 1 + tests/testsuite/cargo_remove/invalid_target/mod.rs | 25 + .../cargo_remove/invalid_target/out/Cargo.toml | 33 + .../cargo_remove/invalid_target/stderr.log | 2 + .../cargo_remove/invalid_target/stdout.log | 0 tests/testsuite/cargo_remove/invalid_target_dep/in | 1 + .../cargo_remove/invalid_target_dep/mod.rs | 25 + .../cargo_remove/invalid_target_dep/out/Cargo.toml | 33 + .../cargo_remove/invalid_target_dep/stderr.log | 2 + .../cargo_remove/invalid_target_dep/stdout.log | 0 tests/testsuite/cargo_remove/mod.rs | 88 + tests/testsuite/cargo_remove/multiple_deps/in | 1 + tests/testsuite/cargo_remove/multiple_deps/mod.rs | 25 + .../cargo_remove/multiple_deps/out/Cargo.toml | 22 + .../cargo_remove/multiple_deps/stderr.log | 3 + .../cargo_remove/multiple_deps/stdout.log | 0 tests/testsuite/cargo_remove/multiple_dev/in | 1 + tests/testsuite/cargo_remove/multiple_dev/mod.rs | 25 + .../cargo_remove/multiple_dev/out/Cargo.toml | 20 + .../testsuite/cargo_remove/multiple_dev/stderr.log | 3 + .../testsuite/cargo_remove/multiple_dev/stdout.log | 0 tests/testsuite/cargo_remove/no_arg/in | 1 + tests/testsuite/cargo_remove/no_arg/mod.rs | 24 + tests/testsuite/cargo_remove/no_arg/out/Cargo.toml | 24 + tests/testsuite/cargo_remove/no_arg/stderr.log | 6 + tests/testsuite/cargo_remove/no_arg/stdout.log | 0 tests/testsuite/cargo_remove/offline/in | 1 + tests/testsuite/cargo_remove/offline/mod.rs | 32 + .../testsuite/cargo_remove/offline/out/Cargo.toml | 23 + tests/testsuite/cargo_remove/offline/stderr.log | 1 + tests/testsuite/cargo_remove/offline/stdout.log | 0 .../testsuite/cargo_remove/optional_dep_feature/in | 1 + .../cargo_remove/optional_dep_feature/mod.rs | 25 + .../optional_dep_feature/out/Cargo.toml | 23 + .../cargo_remove/optional_dep_feature/stderr.log | 2 + .../cargo_remove/optional_dep_feature/stdout.log | 0 tests/testsuite/cargo_remove/optional_feature/in | 1 + .../testsuite/cargo_remove/optional_feature/mod.rs | 25 + .../cargo_remove/optional_feature/out/Cargo.toml | 23 + .../cargo_remove/optional_feature/stderr.log | 2 + .../cargo_remove/optional_feature/stdout.log | 0 tests/testsuite/cargo_remove/package/in | 1 + tests/testsuite/cargo_remove/package/mod.rs | 25 + .../testsuite/cargo_remove/package/out/Cargo.toml | 5 + .../cargo_remove/package/out/dep-a/Cargo.toml | 22 + .../cargo_remove/package/out/dep-a/src/lib.rs | 1 + .../cargo_remove/package/out/dep-b/Cargo.toml | 23 + .../cargo_remove/package/out/dep-b/src/lib.rs | 1 + tests/testsuite/cargo_remove/package/stderr.log | 2 + tests/testsuite/cargo_remove/package/stdout.log | 0 .../cargo_remove/remove-basic.in/Cargo.toml | 24 + .../cargo_remove/remove-basic.in/src/lib.rs | 1 + .../cargo_remove/remove-package.in/Cargo.toml | 5 + .../remove-package.in/dep-a/Cargo.toml | 23 + .../remove-package.in/dep-a/src/lib.rs | 1 + .../remove-package.in/dep-b/Cargo.toml | 23 + .../remove-package.in/dep-b/src/lib.rs | 1 + .../cargo_remove/remove-target.in/Cargo.toml | 33 + tests/testsuite/cargo_remove/remove_basic/in | 1 + tests/testsuite/cargo_remove/remove_basic/mod.rs | 25 + .../cargo_remove/remove_basic/out/Cargo.toml | 23 + .../testsuite/cargo_remove/remove_basic/stderr.log | 2 + .../testsuite/cargo_remove/remove_basic/stdout.log | 0 tests/testsuite/cargo_remove/target/in | 1 + tests/testsuite/cargo_remove/target/mod.rs | 25 + tests/testsuite/cargo_remove/target/out/Cargo.toml | 30 + tests/testsuite/cargo_remove/target/stderr.log | 2 + tests/testsuite/cargo_remove/target/stdout.log | 0 tests/testsuite/cargo_remove/target_build/in | 1 + tests/testsuite/cargo_remove/target_build/mod.rs | 25 + .../cargo_remove/target_build/out/Cargo.toml | 30 + .../testsuite/cargo_remove/target_build/stderr.log | 2 + .../testsuite/cargo_remove/target_build/stdout.log | 0 tests/testsuite/cargo_remove/target_dev/in | 1 + tests/testsuite/cargo_remove/target_dev/mod.rs | 25 + .../cargo_remove/target_dev/out/Cargo.toml | 30 + tests/testsuite/cargo_remove/target_dev/stderr.log | 2 + tests/testsuite/cargo_remove/target_dev/stdout.log | 0 .../cargo_remove/update_lock_file/in/Cargo.lock | 58 + .../cargo_remove/update_lock_file/in/Cargo.toml | 24 + .../cargo_remove/update_lock_file/in/src/main.rs | 1 + .../testsuite/cargo_remove/update_lock_file/mod.rs | 25 + .../cargo_remove/update_lock_file/out/Cargo.lock | 51 + .../cargo_remove/update_lock_file/out/Cargo.toml | 23 + .../cargo_remove/update_lock_file/out/src/main.rs | 1 + .../cargo_remove/update_lock_file/stderr.log | 2 + .../cargo_remove/update_lock_file/stdout.log | 0 .../testsuite/cargo_remove/workspace/in/Cargo.toml | 5 + .../workspace/in/my-package/Cargo.toml | 24 + .../workspace/in/my-package/src/main.rs | 1 + tests/testsuite/cargo_remove/workspace/mod.rs | 25 + .../cargo_remove/workspace/out/Cargo.toml | 2 + .../workspace/out/my-package/Cargo.toml | 21 + .../workspace/out/my-package/src/main.rs | 1 + tests/testsuite/cargo_remove/workspace/stderr.log | 2 + tests/testsuite/cargo_remove/workspace/stdout.log | 0 .../workspace_non_virtual/in/Cargo.toml | 30 + .../workspace_non_virtual/in/my-member/Cargo.toml | 6 + .../workspace_non_virtual/in/my-member/src/main.rs | 0 .../cargo_remove/workspace_non_virtual/mod.rs | 25 + .../workspace_non_virtual/out/Cargo.toml | 24 + .../workspace_non_virtual/out/my-member/Cargo.toml | 6 + .../out/my-member/src/main.rs | 0 .../cargo_remove/workspace_non_virtual/stderr.log | 2 + .../cargo_remove/workspace_non_virtual/stdout.log | 0 .../cargo_remove/workspace_preserved/in/Cargo.toml | 5 + .../in/my-other-package/Cargo.toml | 22 + .../in/my-other-package/src/main.rs | 1 + .../workspace_preserved/in/my-package/Cargo.toml | 24 + .../workspace_preserved/in/my-package/src/main.rs | 1 + .../cargo_remove/workspace_preserved/mod.rs | 25 + .../workspace_preserved/out/Cargo.toml | 5 + .../out/my-other-package/Cargo.toml | 22 + .../out/my-other-package/src/main.rs | 1 + .../workspace_preserved/out/my-package/Cargo.toml | 21 + .../workspace_preserved/out/my-package/src/main.rs | 1 + .../cargo_remove/workspace_preserved/stderr.log | 2 + .../cargo_remove/workspace_preserved/stdout.log | 0 tests/testsuite/cargo_targets.rs | 68 + tests/testsuite/cfg.rs | 515 ++ tests/testsuite/check.rs | 1521 +++++ tests/testsuite/check_cfg.rs | 588 ++ tests/testsuite/clean.rs | 675 +++ tests/testsuite/collisions.rs | 550 ++ tests/testsuite/concurrent.rs | 507 ++ tests/testsuite/config.rs | 1596 +++++ tests/testsuite/config_cli.rs | 564 ++ tests/testsuite/config_include.rs | 285 + tests/testsuite/corrupt_git.rs | 159 + tests/testsuite/credential_process.rs | 495 ++ tests/testsuite/cross_compile.rs | 1342 ++++ tests/testsuite/cross_publish.rs | 119 + tests/testsuite/custom_target.rs | 250 + tests/testsuite/death.rs | 101 + tests/testsuite/dep_info.rs | 600 ++ tests/testsuite/directory.rs | 774 +++ tests/testsuite/doc.rs | 2503 ++++++++ tests/testsuite/docscrape.rs | 637 ++ tests/testsuite/edition.rs | 124 + tests/testsuite/error.rs | 19 + tests/testsuite/features.rs | 2084 +++++++ tests/testsuite/features2.rs | 2553 ++++++++ tests/testsuite/features_namespaced.rs | 1209 ++++ tests/testsuite/fetch.rs | 135 + tests/testsuite/fix.rs | 1855 ++++++ tests/testsuite/freshness.rs | 2816 +++++++++ tests/testsuite/future_incompat_report.rs | 391 ++ tests/testsuite/generate_lockfile.rs | 220 + tests/testsuite/git.rs | 3656 +++++++++++ tests/testsuite/git_auth.rs | 398 ++ tests/testsuite/git_gc.rs | 111 + tests/testsuite/glob_targets.rs | 539 ++ tests/testsuite/help.rs | 219 + tests/testsuite/https.rs | 152 + tests/testsuite/inheritable_workspace_fields.rs | 1702 ++++++ tests/testsuite/init/auto_git/in | 1 + tests/testsuite/init/auto_git/mod.rs | 22 + tests/testsuite/init/auto_git/out/.gitignore | 2 + tests/testsuite/init/auto_git/out/Cargo.toml | 8 + tests/testsuite/init/auto_git/out/src/lib.rs | 14 + tests/testsuite/init/auto_git/stderr.log | 1 + tests/testsuite/init/auto_git/stdout.log | 0 .../bin_already_exists_explicit/in/src/main.rs | 4 + .../init/bin_already_exists_explicit/mod.rs | 21 + .../bin_already_exists_explicit/out/Cargo.toml | 8 + .../bin_already_exists_explicit/out/src/main.rs | 4 + .../init/bin_already_exists_explicit/stderr.log | 1 + .../init/bin_already_exists_explicit/stdout.log | 0 .../bin_already_exists_explicit_nosrc/in/main.rs | 4 + .../init/bin_already_exists_explicit_nosrc/mod.rs | 22 + .../out/Cargo.toml | 12 + .../bin_already_exists_explicit_nosrc/out/main.rs | 4 + .../bin_already_exists_explicit_nosrc/stderr.log | 1 + .../bin_already_exists_explicit_nosrc/stdout.log | 0 .../bin_already_exists_implicit/in/src/main.rs | 4 + .../init/bin_already_exists_implicit/mod.rs | 21 + .../bin_already_exists_implicit/out/Cargo.toml | 8 + .../bin_already_exists_implicit/out/src/main.rs | 4 + .../init/bin_already_exists_implicit/stderr.log | 1 + .../init/bin_already_exists_implicit/stdout.log | 0 .../in/case.rs | 4 + .../bin_already_exists_implicit_namenosrc/mod.rs | 22 + .../out/Cargo.toml | 12 + .../out/case.rs | 4 + .../stderr.log | 1 + .../stdout.log | 0 .../in/src/case.rs | 4 + .../bin_already_exists_implicit_namesrc/mod.rs | 22 + .../out/Cargo.toml | 12 + .../out/src/case.rs | 4 + .../bin_already_exists_implicit_namesrc/stderr.log | 1 + .../bin_already_exists_implicit_namesrc/stdout.log | 0 .../bin_already_exists_implicit_nosrc/in/main.rs | 4 + .../init/bin_already_exists_implicit_nosrc/mod.rs | 22 + .../out/Cargo.toml | 12 + .../bin_already_exists_implicit_nosrc/out/main.rs | 4 + .../bin_already_exists_implicit_nosrc/stderr.log | 1 + .../bin_already_exists_implicit_nosrc/stdout.log | 0 tests/testsuite/init/both_lib_and_bin/mod.rs | 19 + tests/testsuite/init/both_lib_and_bin/stderr.log | 1 + tests/testsuite/init/both_lib_and_bin/stdout.log | 0 .../in/case.rs | 1 + .../in/lib.rs | 1 + .../mod.rs | 18 + .../stderr.log | 1 + .../stdout.log | 0 .../init/confused_by_multiple_lib_files/in/lib.rs | 1 + .../confused_by_multiple_lib_files/in/src/lib.rs | 1 + .../init/confused_by_multiple_lib_files/mod.rs | 22 + .../init/confused_by_multiple_lib_files/out/lib.rs | 1 + .../confused_by_multiple_lib_files/out/src/lib.rs | 1 + .../init/confused_by_multiple_lib_files/stderr.log | 1 + .../init/confused_by_multiple_lib_files/stdout.log | 0 .../in/case.rs | 1 + .../in/lib.rs | 1 + .../creates_binary_when_both_binlib_present/mod.rs | 21 + .../out/Cargo.toml | 16 + .../out/case.rs | 1 + .../out/lib.rs | 1 + .../stderr.log | 1 + .../stdout.log | 0 .../in/case.rs | 1 + .../mod.rs | 21 + .../out/Cargo.toml | 12 + .../out/case.rs | 1 + .../stderr.log | 2 + .../stdout.log | 0 .../in/case.rs | 1 + .../mod.rs | 21 + .../out/Cargo.toml | 12 + .../out/case.rs | 1 + .../stderr.log | 2 + .../stdout.log | 0 tests/testsuite/init/empty_dir/.keep | 0 tests/testsuite/init/empty_dir/mod.rs | 7 + tests/testsuite/init/explicit_bin_with_git/in | 1 + tests/testsuite/init/explicit_bin_with_git/mod.rs | 21 + .../init/explicit_bin_with_git/out/.gitignore | 1 + .../init/explicit_bin_with_git/out/Cargo.toml | 8 + .../init/explicit_bin_with_git/out/src/main.rs | 3 + .../init/explicit_bin_with_git/stderr.log | 1 + .../init/explicit_bin_with_git/stdout.log | 0 .../testsuite/init/formats_source/in/rustfmt.toml | 1 + tests/testsuite/init/formats_source/mod.rs | 29 + tests/testsuite/init/formats_source/out/Cargo.toml | 8 + .../testsuite/init/formats_source/out/rustfmt.toml | 1 + tests/testsuite/init/formats_source/out/src/lib.rs | 14 + tests/testsuite/init/formats_source/stderr.log | 1 + tests/testsuite/init/formats_source/stdout.log | 0 .../init/fossil_autodetect/in/.fossil/.keep | 0 tests/testsuite/init/fossil_autodetect/mod.rs | 22 + .../out/.fossil-settings/clean-glob | 2 + .../out/.fossil-settings/ignore-glob | 2 + .../init/fossil_autodetect/out/Cargo.toml | 8 + .../init/fossil_autodetect/out/src/lib.rs | 14 + tests/testsuite/init/fossil_autodetect/stderr.log | 1 + tests/testsuite/init/fossil_autodetect/stdout.log | 0 tests/testsuite/init/git_autodetect/mod.rs | 24 + tests/testsuite/init/git_autodetect/out/.gitignore | 2 + tests/testsuite/init/git_autodetect/out/Cargo.toml | 8 + tests/testsuite/init/git_autodetect/out/src/lib.rs | 14 + tests/testsuite/init/git_autodetect/stderr.log | 1 + tests/testsuite/init/git_autodetect/stdout.log | 0 .../in/.gitignore | 1 + .../mod.rs | 22 + .../out/.gitignore | 6 + .../out/Cargo.toml | 8 + .../out/src/lib.rs | 14 + .../stderr.log | 1 + .../stdout.log | 0 .../in/rustfmt.toml | 1 + .../init/ignores_failure_to_format_source/mod.rs | 22 + .../out/Cargo.toml | 8 + .../out/rustfmt.toml | 1 + .../out/src/lib.rs | 14 + .../ignores_failure_to_format_source/stderr.log | 1 + .../ignores_failure_to_format_source/stdout.log | 0 .../init/inferred_bin_with_git/in/main.rs | 1 + tests/testsuite/init/inferred_bin_with_git/mod.rs | 21 + .../init/inferred_bin_with_git/out/.gitignore | 1 + .../init/inferred_bin_with_git/out/Cargo.toml | 12 + .../init/inferred_bin_with_git/out/main.rs | 1 + .../init/inferred_bin_with_git/stderr.log | 1 + .../init/inferred_bin_with_git/stdout.log | 0 .../testsuite/init/inferred_lib_with_git/in/lib.rs | 1 + tests/testsuite/init/inferred_lib_with_git/mod.rs | 21 + .../init/inferred_lib_with_git/out/.gitignore | 2 + .../init/inferred_lib_with_git/out/Cargo.toml | 12 + .../init/inferred_lib_with_git/out/lib.rs | 1 + .../init/inferred_lib_with_git/stderr.log | 1 + .../init/inferred_lib_with_git/stdout.log | 0 tests/testsuite/init/invalid_dir_name/mod.rs | 21 + tests/testsuite/init/invalid_dir_name/stderr.log | 8 + tests/testsuite/init/invalid_dir_name/stdout.log | 0 .../init/lib_already_exists_nosrc/in/lib.rs | 0 .../testsuite/init/lib_already_exists_nosrc/mod.rs | 22 + .../init/lib_already_exists_nosrc/out/Cargo.toml | 12 + .../init/lib_already_exists_nosrc/out/lib.rs | 0 .../init/lib_already_exists_nosrc/stderr.log | 1 + .../init/lib_already_exists_nosrc/stdout.log | 0 .../init/lib_already_exists_src/in/src/lib.rs | 1 + tests/testsuite/init/lib_already_exists_src/mod.rs | 22 + .../init/lib_already_exists_src/out/Cargo.toml | 8 + .../init/lib_already_exists_src/out/src/lib.rs | 1 + .../init/lib_already_exists_src/stderr.log | 1 + .../init/lib_already_exists_src/stdout.log | 0 .../init/mercurial_autodetect/in/.hg/.keep | 0 tests/testsuite/init/mercurial_autodetect/mod.rs | 22 + .../init/mercurial_autodetect/out/.hgignore | 2 + .../init/mercurial_autodetect/out/Cargo.toml | 8 + .../init/mercurial_autodetect/out/src/lib.rs | 14 + .../testsuite/init/mercurial_autodetect/stderr.log | 1 + .../testsuite/init/mercurial_autodetect/stdout.log | 0 tests/testsuite/init/mod.rs | 42 + .../init/multibin_project_name_clash/in/case.rs | 1 + .../init/multibin_project_name_clash/in/main.rs | 1 + .../init/multibin_project_name_clash/mod.rs | 22 + .../init/multibin_project_name_clash/out/case.rs | 1 + .../init/multibin_project_name_clash/out/main.rs | 1 + .../init/multibin_project_name_clash/stderr.log | 4 + .../init/multibin_project_name_clash/stdout.log | 0 tests/testsuite/init/no_filename/mod.rs | 16 + tests/testsuite/init/no_filename/stderr.log | 1 + tests/testsuite/init/no_filename/stdout.log | 0 .../init/path_contains_separator/in/.keep | 0 .../testsuite/init/path_contains_separator/mod.rs | 26 + .../init/path_contains_separator/out/Cargo.toml | 8 + .../init/path_contains_separator/out/src/main.rs | 3 + .../init/path_contains_separator/stderr.log | 3 + .../init/path_contains_separator/stdout.log | 0 .../init/pijul_autodetect/in/.pijul/.keep | 0 tests/testsuite/init/pijul_autodetect/mod.rs | 22 + tests/testsuite/init/pijul_autodetect/out/.ignore | 2 + .../testsuite/init/pijul_autodetect/out/Cargo.toml | 8 + .../testsuite/init/pijul_autodetect/out/src/lib.rs | 14 + tests/testsuite/init/pijul_autodetect/stderr.log | 1 + tests/testsuite/init/pijul_autodetect/stdout.log | 0 tests/testsuite/init/reserved_name/mod.rs | 21 + tests/testsuite/init/reserved_name/stderr.log | 8 + tests/testsuite/init/reserved_name/stdout.log | 0 tests/testsuite/init/simple_bin/in | 1 + tests/testsuite/init/simple_bin/mod.rs | 29 + tests/testsuite/init/simple_bin/out/Cargo.toml | 8 + tests/testsuite/init/simple_bin/out/src/main.rs | 3 + tests/testsuite/init/simple_bin/stderr.log | 1 + tests/testsuite/init/simple_bin/stdout.log | 0 tests/testsuite/init/simple_git/in | 1 + tests/testsuite/init/simple_git/mod.rs | 22 + tests/testsuite/init/simple_git/out/.gitignore | 2 + tests/testsuite/init/simple_git/out/Cargo.toml | 8 + tests/testsuite/init/simple_git/out/src/lib.rs | 14 + tests/testsuite/init/simple_git/stderr.log | 1 + tests/testsuite/init/simple_git/stdout.log | 0 .../init/simple_git_ignore_exists/in/.gitignore | 2 + .../testsuite/init/simple_git_ignore_exists/mod.rs | 28 + .../init/simple_git_ignore_exists/out/.gitignore | 9 + .../init/simple_git_ignore_exists/out/Cargo.toml | 8 + .../init/simple_git_ignore_exists/out/src/lib.rs | 14 + .../init/simple_git_ignore_exists/stderr.log | 1 + .../init/simple_git_ignore_exists/stdout.log | 0 tests/testsuite/init/simple_hg/in | 1 + tests/testsuite/init/simple_hg/mod.rs | 22 + tests/testsuite/init/simple_hg/out/.hgignore | 2 + tests/testsuite/init/simple_hg/out/Cargo.toml | 8 + tests/testsuite/init/simple_hg/out/src/lib.rs | 14 + tests/testsuite/init/simple_hg/stderr.log | 1 + tests/testsuite/init/simple_hg/stdout.log | 0 .../init/simple_hg_ignore_exists/in/.hg/.keep | 0 .../init/simple_hg_ignore_exists/in/.hgignore | 1 + .../testsuite/init/simple_hg_ignore_exists/mod.rs | 22 + .../init/simple_hg_ignore_exists/out/.hgignore | 6 + .../init/simple_hg_ignore_exists/out/Cargo.toml | 8 + .../init/simple_hg_ignore_exists/out/src/lib.rs | 14 + .../init/simple_hg_ignore_exists/stderr.log | 1 + .../init/simple_hg_ignore_exists/stdout.log | 0 tests/testsuite/init/simple_lib/in | 1 + tests/testsuite/init/simple_lib/mod.rs | 29 + tests/testsuite/init/simple_lib/out/Cargo.toml | 8 + tests/testsuite/init/simple_lib/out/src/lib.rs | 14 + tests/testsuite/init/simple_lib/stderr.log | 1 + tests/testsuite/init/simple_lib/stdout.log | 0 tests/testsuite/init/unknown_flags/mod.rs | 16 + tests/testsuite/init/unknown_flags/stderr.log | 7 + tests/testsuite/init/unknown_flags/stdout.log | 0 tests/testsuite/init/with_argument/in/foo/.keep | 0 tests/testsuite/init/with_argument/mod.rs | 21 + .../init/with_argument/out/foo/Cargo.toml | 8 + .../init/with_argument/out/foo/src/main.rs | 3 + tests/testsuite/init/with_argument/stderr.log | 1 + tests/testsuite/init/with_argument/stdout.log | 0 tests/testsuite/install.rs | 2208 +++++++ tests/testsuite/install_upgrade.rs | 862 +++ tests/testsuite/jobserver.rs | 250 + tests/testsuite/list_availables.rs | 232 + tests/testsuite/local_registry.rs | 528 ++ tests/testsuite/locate_project.rs | 76 + tests/testsuite/lockfile_compat.rs | 890 +++ tests/testsuite/login.rs | 364 ++ tests/testsuite/logout.rs | 91 + tests/testsuite/lto.rs | 850 +++ tests/testsuite/main.rs | 145 + tests/testsuite/member_discovery.rs | 44 + tests/testsuite/member_errors.rs | 164 + tests/testsuite/message_format.rs | 133 + tests/testsuite/messages.rs | 144 + tests/testsuite/metabuild.rs | 771 +++ tests/testsuite/metadata.rs | 4192 +++++++++++++ tests/testsuite/minimal_versions.rs | 38 + tests/testsuite/mock-std/Cargo.toml | 8 + tests/testsuite/mock-std/library/alloc/Cargo.toml | 8 + tests/testsuite/mock-std/library/alloc/src/lib.rs | 11 + .../mock-std/library/compiler_builtins/Cargo.toml | 5 + .../mock-std/library/compiler_builtins/src/lib.rs | 1 + tests/testsuite/mock-std/library/core/Cargo.toml | 5 + tests/testsuite/mock-std/library/core/src/lib.rs | 9 + .../mock-std/library/panic_unwind/Cargo.toml | 5 + .../mock-std/library/panic_unwind/src/lib.rs | 5 + .../mock-std/library/proc_macro/Cargo.toml | 5 + .../mock-std/library/proc_macro/src/lib.rs | 11 + .../library/rustc-std-workspace-alloc/Cargo.toml | 11 + .../library/rustc-std-workspace-alloc/lib.rs | 3 + .../library/rustc-std-workspace-core/Cargo.toml | 11 + .../library/rustc-std-workspace-core/lib.rs | 3 + .../library/rustc-std-workspace-std/Cargo.toml | 11 + .../library/rustc-std-workspace-std/lib.rs | 1 + tests/testsuite/mock-std/library/std/Cargo.toml | 11 + tests/testsuite/mock-std/library/std/src/lib.rs | 12 + tests/testsuite/mock-std/library/test/Cargo.toml | 18 + tests/testsuite/mock-std/library/test/src/lib.rs | 10 + tests/testsuite/multitarget.rs | 231 + tests/testsuite/net_config.rs | 74 + tests/testsuite/new.rs | 543 ++ tests/testsuite/offline.rs | 715 +++ tests/testsuite/old_cargos.rs | 679 +++ tests/testsuite/out_dir.rs | 317 + tests/testsuite/owner.rs | 192 + tests/testsuite/package.rs | 2764 +++++++++ tests/testsuite/package_features.rs | 704 +++ tests/testsuite/patch.rs | 2543 ++++++++ tests/testsuite/path.rs | 1139 ++++ tests/testsuite/paths.rs | 226 + tests/testsuite/pkgid.rs | 128 + tests/testsuite/plugins.rs | 421 ++ tests/testsuite/proc_macro.rs | 560 ++ tests/testsuite/profile_config.rs | 519 ++ tests/testsuite/profile_custom.rs | 731 +++ tests/testsuite/profile_overrides.rs | 515 ++ tests/testsuite/profile_targets.rs | 674 ++ tests/testsuite/profiles.rs | 744 +++ tests/testsuite/progress.rs | 159 + tests/testsuite/pub_priv.rs | 199 + tests/testsuite/publish.rs | 2704 +++++++++ tests/testsuite/publish_lockfile.rs | 592 ++ tests/testsuite/read_manifest.rs | 206 + tests/testsuite/registry.rs | 2913 +++++++++ tests/testsuite/registry_auth.rs | 519 ++ tests/testsuite/rename_deps.rs | 391 ++ tests/testsuite/replace.rs | 1300 ++++ tests/testsuite/required_features.rs | 1452 +++++ tests/testsuite/run.rs | 1510 +++++ tests/testsuite/rust_version.rs | 194 + tests/testsuite/rustc.rs | 794 +++ tests/testsuite/rustc_info_cache.rs | 186 + tests/testsuite/rustdoc.rs | 252 + tests/testsuite/rustdoc_extern_html.rs | 426 ++ tests/testsuite/rustdocflags.rs | 155 + tests/testsuite/rustflags.rs | 1673 +++++ tests/testsuite/search.rs | 192 + tests/testsuite/shell_quoting.rs | 37 + tests/testsuite/source_replacement.rs | 247 + tests/testsuite/ssh.rs | 554 ++ tests/testsuite/standard_lib.rs | 657 ++ tests/testsuite/test.rs | 4820 +++++++++++++++ tests/testsuite/timings.rs | 53 + tests/testsuite/tool_paths.rs | 402 ++ tests/testsuite/tree.rs | 2150 +++++++ tests/testsuite/tree_graph_features.rs | 362 ++ tests/testsuite/unit_graph.rs | 233 + tests/testsuite/update.rs | 832 +++ tests/testsuite/vendor.rs | 1153 ++++ tests/testsuite/verify_project.rs | 73 + tests/testsuite/version.rs | 54 + tests/testsuite/warn_on_failure.rs | 111 + tests/testsuite/weak_dep_features.rs | 629 ++ tests/testsuite/workspaces.rs | 2531 ++++++++ tests/testsuite/yank.rs | 202 + 1349 files changed, 122374 insertions(+) create mode 100644 tests/testsuite/advanced_env.rs create mode 100644 tests/testsuite/alt_registry.rs create mode 100644 tests/testsuite/artifact_dep.rs create mode 100644 tests/testsuite/bad_config.rs create mode 100644 tests/testsuite/bad_manifest_path.rs create mode 100644 tests/testsuite/bench.rs create mode 100644 tests/testsuite/binary_name.rs create mode 100644 tests/testsuite/build.rs create mode 100644 tests/testsuite/build_plan.rs create mode 100644 tests/testsuite/build_script.rs create mode 100644 tests/testsuite/build_script_env.rs create mode 100644 tests/testsuite/build_script_extra_link_arg.rs create mode 100644 tests/testsuite/cache_messages.rs create mode 100644 tests/testsuite/cargo_add/add-basic.in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/add-basic.in/src/lib.rs create mode 120000 tests/testsuite/cargo_add/add_basic/in create mode 100644 tests/testsuite/cargo_add/add_basic/mod.rs create mode 100644 tests/testsuite/cargo_add/add_basic/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/add_basic/stderr.log create mode 100644 tests/testsuite/cargo_add/add_basic/stdout.log create mode 120000 tests/testsuite/cargo_add/add_multiple/in create mode 100644 tests/testsuite/cargo_add/add_multiple/mod.rs create mode 100644 tests/testsuite/cargo_add/add_multiple/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/add_multiple/stderr.log create mode 100644 tests/testsuite/cargo_add/add_multiple/stdout.log create mode 120000 tests/testsuite/cargo_add/add_normalized_name_external/in create mode 100644 tests/testsuite/cargo_add/add_normalized_name_external/mod.rs create mode 100644 tests/testsuite/cargo_add/add_normalized_name_external/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/add_normalized_name_external/stderr.log create mode 100644 tests/testsuite/cargo_add/add_normalized_name_external/stdout.log create mode 120000 tests/testsuite/cargo_add/build/in create mode 100644 tests/testsuite/cargo_add/build/mod.rs create mode 100644 tests/testsuite/cargo_add/build/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/build/stderr.log create mode 100644 tests/testsuite/cargo_add/build/stdout.log create mode 100644 tests/testsuite/cargo_add/build_prefer_existing_version/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/build_prefer_existing_version/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/build_prefer_existing_version/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/build_prefer_existing_version/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs create mode 100644 tests/testsuite/cargo_add/build_prefer_existing_version/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/build_prefer_existing_version/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/build_prefer_existing_version/stderr.log create mode 100644 tests/testsuite/cargo_add/build_prefer_existing_version/stdout.log create mode 100644 tests/testsuite/cargo_add/change_rename_target/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/change_rename_target/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/change_rename_target/mod.rs create mode 100644 tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/change_rename_target/stderr.log create mode 100644 tests/testsuite/cargo_add/change_rename_target/stdout.log create mode 120000 tests/testsuite/cargo_add/default_features/in create mode 100644 tests/testsuite/cargo_add/default_features/mod.rs create mode 100644 tests/testsuite/cargo_add/default_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/default_features/stderr.log create mode 100644 tests/testsuite/cargo_add/default_features/stdout.log create mode 100644 tests/testsuite/cargo_add/deprecated_default_features/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/deprecated_default_features/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/deprecated_default_features/mod.rs create mode 100644 tests/testsuite/cargo_add/deprecated_default_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/deprecated_default_features/stderr.log create mode 100644 tests/testsuite/cargo_add/deprecated_default_features/stdout.log create mode 100644 tests/testsuite/cargo_add/deprecated_section/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/deprecated_section/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/deprecated_section/mod.rs create mode 100644 tests/testsuite/cargo_add/deprecated_section/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/deprecated_section/stderr.log create mode 100644 tests/testsuite/cargo_add/deprecated_section/stdout.log create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/stderr.log create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit/stdout.log create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/stderr.log create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_features/stdout.log create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/stderr.log create mode 100644 tests/testsuite/cargo_add/detect_workspace_inherit_optional/stdout.log create mode 120000 tests/testsuite/cargo_add/dev/in create mode 100644 tests/testsuite/cargo_add/dev/mod.rs create mode 100644 tests/testsuite/cargo_add/dev/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/dev/stderr.log create mode 100644 tests/testsuite/cargo_add/dev/stdout.log create mode 120000 tests/testsuite/cargo_add/dev_build_conflict/in create mode 100644 tests/testsuite/cargo_add/dev_build_conflict/mod.rs create mode 100644 tests/testsuite/cargo_add/dev_build_conflict/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/dev_build_conflict/stderr.log create mode 100644 tests/testsuite/cargo_add/dev_build_conflict/stdout.log create mode 100644 tests/testsuite/cargo_add/dev_prefer_existing_version/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/dev_prefer_existing_version/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/dev_prefer_existing_version/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/dev_prefer_existing_version/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs create mode 100644 tests/testsuite/cargo_add/dev_prefer_existing_version/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/dev_prefer_existing_version/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/dev_prefer_existing_version/stderr.log create mode 100644 tests/testsuite/cargo_add/dev_prefer_existing_version/stdout.log create mode 120000 tests/testsuite/cargo_add/dry_run/in create mode 100644 tests/testsuite/cargo_add/dry_run/mod.rs create mode 100644 tests/testsuite/cargo_add/dry_run/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/dry_run/stderr.log create mode 100644 tests/testsuite/cargo_add/dry_run/stdout.log create mode 120000 tests/testsuite/cargo_add/features/in create mode 100644 tests/testsuite/cargo_add/features/mod.rs create mode 100644 tests/testsuite/cargo_add/features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features/stderr.log create mode 100644 tests/testsuite/cargo_add/features/stdout.log create mode 120000 tests/testsuite/cargo_add/features_empty/in create mode 100644 tests/testsuite/cargo_add/features_empty/mod.rs create mode 100644 tests/testsuite/cargo_add/features_empty/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_empty/stderr.log create mode 100644 tests/testsuite/cargo_add/features_empty/stdout.log create mode 120000 tests/testsuite/cargo_add/features_multiple_occurrences/in create mode 100644 tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs create mode 100644 tests/testsuite/cargo_add/features_multiple_occurrences/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_multiple_occurrences/stderr.log create mode 100644 tests/testsuite/cargo_add/features_multiple_occurrences/stdout.log create mode 100644 tests/testsuite/cargo_add/features_preserve/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_preserve/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/features_preserve/mod.rs create mode 100644 tests/testsuite/cargo_add/features_preserve/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_preserve/stderr.log create mode 100644 tests/testsuite/cargo_add/features_preserve/stdout.log create mode 120000 tests/testsuite/cargo_add/features_spaced_values/in create mode 100644 tests/testsuite/cargo_add/features_spaced_values/mod.rs create mode 100644 tests/testsuite/cargo_add/features_spaced_values/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_spaced_values/stderr.log create mode 100644 tests/testsuite/cargo_add/features_spaced_values/stdout.log create mode 120000 tests/testsuite/cargo_add/features_unknown/in create mode 100644 tests/testsuite/cargo_add/features_unknown/mod.rs create mode 100644 tests/testsuite/cargo_add/features_unknown/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_unknown/stderr.log create mode 100644 tests/testsuite/cargo_add/features_unknown/stdout.log create mode 120000 tests/testsuite/cargo_add/features_unknown_no_features/in create mode 100644 tests/testsuite/cargo_add/features_unknown_no_features/mod.rs create mode 100644 tests/testsuite/cargo_add/features_unknown_no_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_unknown_no_features/stderr.log create mode 100644 tests/testsuite/cargo_add/features_unknown_no_features/stdout.log create mode 120000 tests/testsuite/cargo_add/git/in create mode 100644 tests/testsuite/cargo_add/git/mod.rs create mode 100644 tests/testsuite/cargo_add/git/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git/stderr.log create mode 100644 tests/testsuite/cargo_add/git/stdout.log create mode 120000 tests/testsuite/cargo_add/git_branch/in create mode 100644 tests/testsuite/cargo_add/git_branch/mod.rs create mode 100644 tests/testsuite/cargo_add/git_branch/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_branch/stderr.log create mode 100644 tests/testsuite/cargo_add/git_branch/stdout.log create mode 120000 tests/testsuite/cargo_add/git_conflicts_namever/in create mode 100644 tests/testsuite/cargo_add/git_conflicts_namever/mod.rs create mode 100644 tests/testsuite/cargo_add/git_conflicts_namever/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_conflicts_namever/stderr.log create mode 100644 tests/testsuite/cargo_add/git_conflicts_namever/stdout.log create mode 120000 tests/testsuite/cargo_add/git_dev/in create mode 100644 tests/testsuite/cargo_add/git_dev/mod.rs create mode 100644 tests/testsuite/cargo_add/git_dev/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_dev/stderr.log create mode 100644 tests/testsuite/cargo_add/git_dev/stdout.log create mode 120000 tests/testsuite/cargo_add/git_inferred_name/in create mode 100644 tests/testsuite/cargo_add/git_inferred_name/mod.rs create mode 100644 tests/testsuite/cargo_add/git_inferred_name/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_inferred_name/stderr.log create mode 100644 tests/testsuite/cargo_add/git_inferred_name/stdout.log create mode 120000 tests/testsuite/cargo_add/git_inferred_name_multiple/in create mode 100644 tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs create mode 100644 tests/testsuite/cargo_add/git_inferred_name_multiple/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_inferred_name_multiple/stderr.log create mode 100644 tests/testsuite/cargo_add/git_inferred_name_multiple/stdout.log create mode 120000 tests/testsuite/cargo_add/git_multiple_names/in create mode 100644 tests/testsuite/cargo_add/git_multiple_names/mod.rs create mode 100644 tests/testsuite/cargo_add/git_multiple_names/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_multiple_names/stderr.log create mode 100644 tests/testsuite/cargo_add/git_multiple_names/stdout.log create mode 120000 tests/testsuite/cargo_add/git_normalized_name/in create mode 100644 tests/testsuite/cargo_add/git_normalized_name/mod.rs create mode 100644 tests/testsuite/cargo_add/git_normalized_name/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_normalized_name/stderr.log create mode 100644 tests/testsuite/cargo_add/git_normalized_name/stdout.log create mode 100644 tests/testsuite/cargo_add/git_registry/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_registry/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/git_registry/mod.rs create mode 100644 tests/testsuite/cargo_add/git_registry/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_registry/stderr.log create mode 100644 tests/testsuite/cargo_add/git_registry/stdout.log create mode 120000 tests/testsuite/cargo_add/git_rev/in create mode 100644 tests/testsuite/cargo_add/git_rev/mod.rs create mode 100644 tests/testsuite/cargo_add/git_rev/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_rev/stderr.log create mode 100644 tests/testsuite/cargo_add/git_rev/stdout.log create mode 120000 tests/testsuite/cargo_add/git_tag/in create mode 100644 tests/testsuite/cargo_add/git_tag/mod.rs create mode 100644 tests/testsuite/cargo_add/git_tag/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/git_tag/stderr.log create mode 100644 tests/testsuite/cargo_add/git_tag/stdout.log create mode 120000 tests/testsuite/cargo_add/infer_prerelease/in create mode 100644 tests/testsuite/cargo_add/infer_prerelease/mod.rs create mode 100644 tests/testsuite/cargo_add/infer_prerelease/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/infer_prerelease/stderr.log create mode 100644 tests/testsuite/cargo_add/infer_prerelease/stdout.log create mode 120000 tests/testsuite/cargo_add/invalid_arg/in create mode 100644 tests/testsuite/cargo_add/invalid_arg/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_arg/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_arg/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_arg/stdout.log create mode 120000 tests/testsuite/cargo_add/invalid_git_external/in create mode 100644 tests/testsuite/cargo_add/invalid_git_external/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_git_external/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_git_external/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_git_external/stdout.log create mode 120000 tests/testsuite/cargo_add/invalid_git_name/in create mode 100644 tests/testsuite/cargo_add/invalid_git_name/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_git_name/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_git_name/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_git_name/stdout.log create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_key_inherit_dependency/stdout.log create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/stdout.log create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency-alt/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency-alt/src/lib.rs create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/dependency-alt/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/stdout.log create mode 100644 tests/testsuite/cargo_add/invalid_manifest/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_manifest/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/invalid_manifest/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_manifest/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_manifest/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_manifest/stdout.log create mode 120000 tests/testsuite/cargo_add/invalid_name_external/in create mode 100644 tests/testsuite/cargo_add/invalid_name_external/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_name_external/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_name_external/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_name_external/stdout.log create mode 120000 tests/testsuite/cargo_add/invalid_path/in create mode 100644 tests/testsuite/cargo_add/invalid_path/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_path/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_path/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_path/stdout.log create mode 100644 tests/testsuite/cargo_add/invalid_path_name/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_path_name/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/invalid_path_name/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_path_name/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/invalid_path_name/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_path_name/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_path_name/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_path_name/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_path_name/stdout.log create mode 120000 tests/testsuite/cargo_add/invalid_path_self/in create mode 100644 tests/testsuite/cargo_add/invalid_path_self/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_path_self/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_path_self/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_path_self/stdout.log create mode 120000 tests/testsuite/cargo_add/invalid_target_empty/in create mode 100644 tests/testsuite/cargo_add/invalid_target_empty/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_target_empty/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_target_empty/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_target_empty/stdout.log create mode 120000 tests/testsuite/cargo_add/invalid_vers/in create mode 100644 tests/testsuite/cargo_add/invalid_vers/mod.rs create mode 100644 tests/testsuite/cargo_add/invalid_vers/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/invalid_vers/stderr.log create mode 100644 tests/testsuite/cargo_add/invalid_vers/stdout.log create mode 120000 tests/testsuite/cargo_add/list_features/in create mode 100644 tests/testsuite/cargo_add/list_features/mod.rs create mode 100644 tests/testsuite/cargo_add/list_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features/stderr.log create mode 100644 tests/testsuite/cargo_add/list_features/stdout.log create mode 100644 tests/testsuite/cargo_add/list_features_path/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/list_features_path/in/optional/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path/in/optional/src/lib.rs create mode 100644 tests/testsuite/cargo_add/list_features_path/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/list_features_path/mod.rs create mode 100644 tests/testsuite/cargo_add/list_features_path/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path/stderr.log create mode 100644 tests/testsuite/cargo_add/list_features_path/stdout.log create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/in/optional/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/in/optional/src/lib.rs create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/mod.rs create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/stderr.log create mode 100644 tests/testsuite/cargo_add/list_features_path_no_default/stdout.log create mode 120000 tests/testsuite/cargo_add/locked_changed/in create mode 100644 tests/testsuite/cargo_add/locked_changed/mod.rs create mode 100644 tests/testsuite/cargo_add/locked_changed/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/locked_changed/stderr.log create mode 100644 tests/testsuite/cargo_add/locked_changed/stdout.log create mode 100644 tests/testsuite/cargo_add/locked_unchanged/in/Cargo.lock create mode 100644 tests/testsuite/cargo_add/locked_unchanged/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/locked_unchanged/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/locked_unchanged/mod.rs create mode 100644 tests/testsuite/cargo_add/locked_unchanged/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/locked_unchanged/stderr.log create mode 100644 tests/testsuite/cargo_add/locked_unchanged/stdout.log create mode 100644 tests/testsuite/cargo_add/lockfile_updated/in/Cargo.lock create mode 100644 tests/testsuite/cargo_add/lockfile_updated/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/lockfile_updated/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/lockfile_updated/mod.rs create mode 100644 tests/testsuite/cargo_add/lockfile_updated/out/Cargo.lock create mode 100644 tests/testsuite/cargo_add/lockfile_updated/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/lockfile_updated/stderr.log create mode 100644 tests/testsuite/cargo_add/lockfile_updated/stdout.log create mode 100644 tests/testsuite/cargo_add/manifest_path_package/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/manifest_path_package/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/manifest_path_package/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/manifest_path_package/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/manifest_path_package/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/manifest_path_package/mod.rs create mode 100644 tests/testsuite/cargo_add/manifest_path_package/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/manifest_path_package/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/manifest_path_package/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/manifest_path_package/stderr.log create mode 100644 tests/testsuite/cargo_add/manifest_path_package/stdout.log create mode 100644 tests/testsuite/cargo_add/merge_activated_features/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/merge_activated_features/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/merge_activated_features/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/merge_activated_features/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/merge_activated_features/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/merge_activated_features/mod.rs create mode 100644 tests/testsuite/cargo_add/merge_activated_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/merge_activated_features/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/merge_activated_features/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/merge_activated_features/stderr.log create mode 100644 tests/testsuite/cargo_add/merge_activated_features/stdout.log create mode 100644 tests/testsuite/cargo_add/mod.rs create mode 120000 tests/testsuite/cargo_add/multiple_conflicts_with_features/in create mode 100644 tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs create mode 100644 tests/testsuite/cargo_add/multiple_conflicts_with_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/multiple_conflicts_with_features/stderr.log create mode 100644 tests/testsuite/cargo_add/multiple_conflicts_with_features/stdout.log create mode 120000 tests/testsuite/cargo_add/multiple_conflicts_with_rename/in create mode 100644 tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs create mode 100644 tests/testsuite/cargo_add/multiple_conflicts_with_rename/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/multiple_conflicts_with_rename/stderr.log create mode 100644 tests/testsuite/cargo_add/multiple_conflicts_with_rename/stdout.log create mode 120000 tests/testsuite/cargo_add/namever/in create mode 100644 tests/testsuite/cargo_add/namever/mod.rs create mode 100644 tests/testsuite/cargo_add/namever/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/namever/stderr.log create mode 100644 tests/testsuite/cargo_add/namever/stdout.log create mode 120000 tests/testsuite/cargo_add/no_args/in create mode 100644 tests/testsuite/cargo_add/no_args/mod.rs create mode 100644 tests/testsuite/cargo_add/no_args/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/no_args/stderr.log create mode 100644 tests/testsuite/cargo_add/no_args/stdout.log create mode 120000 tests/testsuite/cargo_add/no_default_features/in create mode 100644 tests/testsuite/cargo_add/no_default_features/mod.rs create mode 100644 tests/testsuite/cargo_add/no_default_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/no_default_features/stderr.log create mode 100644 tests/testsuite/cargo_add/no_default_features/stdout.log create mode 120000 tests/testsuite/cargo_add/no_optional/in create mode 100644 tests/testsuite/cargo_add/no_optional/mod.rs create mode 100644 tests/testsuite/cargo_add/no_optional/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/no_optional/stderr.log create mode 100644 tests/testsuite/cargo_add/no_optional/stdout.log create mode 120000 tests/testsuite/cargo_add/offline_empty_cache/in create mode 100644 tests/testsuite/cargo_add/offline_empty_cache/mod.rs create mode 100644 tests/testsuite/cargo_add/offline_empty_cache/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/offline_empty_cache/stderr.log create mode 100644 tests/testsuite/cargo_add/offline_empty_cache/stdout.log create mode 120000 tests/testsuite/cargo_add/optional/in create mode 100644 tests/testsuite/cargo_add/optional/mod.rs create mode 100644 tests/testsuite/cargo_add/optional/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/optional/stderr.log create mode 100644 tests/testsuite/cargo_add/optional/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_default_features/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_default_features/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_default_features/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_default_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_default_features/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_default_features/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_features/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_features/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_features/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_features/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_features/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_git_with_path/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_git_with_path/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_git_with_path/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_git_with_path/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_git_with_path/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_git_with_path/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_git_with_path/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_features_noop/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_noop/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_inherit_optional_noop/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_inline_features/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inline_features/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inline_features/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_inline_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_inline_features/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_inline_features/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_name_dev_noop/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_name_dev_noop/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_name_dev_noop/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_name_dev_noop/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_name_dev_noop/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_name_dev_noop/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_name_dev_noop/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_name_dev_noop/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_name_noop/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_name_noop/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_name_noop/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_name_noop/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_name_noop/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_name_noop/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_name_noop/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_name_noop/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_optional/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_optional/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_optional/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_optional/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_optional/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_no_optional/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_path_noop/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_path_noop/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_path_noop/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_path_noop/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_path_noop/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_path_noop/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_path_noop/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_path_noop/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_path_with_version/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_path_with_version/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_path_with_version/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_path_with_version/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_path_with_version/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_path_with_version/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_path_with_version/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_preserves_inline_table/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_preserves_inline_table/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_preserves_inline_table/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_preserves_inline_table/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_preserves_inline_table/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_no_rename/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_no_rename/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_no_rename/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_no_rename/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_no_rename/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_git/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_git/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_git/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_git/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_path/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_path/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_path/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_path/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_path/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_path/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_version_with_path/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_with_rename/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_with_rename/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_with_rename/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_with_rename/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_with_rename/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_with_rename/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep/stdout.log create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_workspace_dep_features/stdout.log create mode 100644 tests/testsuite/cargo_add/path/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path/mod.rs create mode 100644 tests/testsuite/cargo_add/path/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path/stderr.log create mode 100644 tests/testsuite/cargo_add/path/stdout.log create mode 100644 tests/testsuite/cargo_add/path_dev/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_dev/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path_dev/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_dev/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path_dev/mod.rs create mode 100644 tests/testsuite/cargo_add/path_dev/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_dev/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_dev/stderr.log create mode 100644 tests/testsuite/cargo_add/path_dev/stdout.log create mode 100644 tests/testsuite/cargo_add/path_inferred_name/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path_inferred_name/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path_inferred_name/mod.rs create mode 100644 tests/testsuite/cargo_add/path_inferred_name/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name/stderr.log create mode 100644 tests/testsuite/cargo_add/path_inferred_name/stdout.log create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/optional/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/optional/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/stderr.log create mode 100644 tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/stdout.log create mode 100644 tests/testsuite/cargo_add/path_normalized_name/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_normalized_name/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path_normalized_name/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_normalized_name/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/path_normalized_name/mod.rs create mode 100644 tests/testsuite/cargo_add/path_normalized_name/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_normalized_name/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/path_normalized_name/stderr.log create mode 100644 tests/testsuite/cargo_add/path_normalized_name/stdout.log create mode 100644 tests/testsuite/cargo_add/preserve_sorted/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/preserve_sorted/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/preserve_sorted/mod.rs create mode 100644 tests/testsuite/cargo_add/preserve_sorted/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/preserve_sorted/stderr.log create mode 100644 tests/testsuite/cargo_add/preserve_sorted/stdout.log create mode 100644 tests/testsuite/cargo_add/preserve_unsorted/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/preserve_unsorted/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/preserve_unsorted/mod.rs create mode 100644 tests/testsuite/cargo_add/preserve_unsorted/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/preserve_unsorted/stderr.log create mode 100644 tests/testsuite/cargo_add/preserve_unsorted/stdout.log create mode 120000 tests/testsuite/cargo_add/quiet/in create mode 100644 tests/testsuite/cargo_add/quiet/mod.rs create mode 100644 tests/testsuite/cargo_add/quiet/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/quiet/stderr.log create mode 100644 tests/testsuite/cargo_add/quiet/stdout.log create mode 100644 tests/testsuite/cargo_add/registry/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/registry/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/registry/mod.rs create mode 100644 tests/testsuite/cargo_add/registry/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/registry/stderr.log create mode 100644 tests/testsuite/cargo_add/registry/stdout.log create mode 120000 tests/testsuite/cargo_add/rename/in create mode 100644 tests/testsuite/cargo_add/rename/mod.rs create mode 100644 tests/testsuite/cargo_add/rename/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/rename/stderr.log create mode 100644 tests/testsuite/cargo_add/rename/stdout.log create mode 100644 tests/testsuite/cargo_add/require_weak/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/require_weak/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/require_weak/mod.rs create mode 100644 tests/testsuite/cargo_add/require_weak/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/require_weak/stderr.log create mode 100644 tests/testsuite/cargo_add/require_weak/stdout.log create mode 100644 tests/testsuite/cargo_add/sorted_table_with_dotted_item/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/sorted_table_with_dotted_item/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs create mode 100644 tests/testsuite/cargo_add/sorted_table_with_dotted_item/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/sorted_table_with_dotted_item/stderr.log create mode 100644 tests/testsuite/cargo_add/sorted_table_with_dotted_item/stdout.log create mode 120000 tests/testsuite/cargo_add/target/in create mode 100644 tests/testsuite/cargo_add/target/mod.rs create mode 100644 tests/testsuite/cargo_add/target/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/target/stderr.log create mode 100644 tests/testsuite/cargo_add/target/stdout.log create mode 120000 tests/testsuite/cargo_add/target_cfg/in create mode 100644 tests/testsuite/cargo_add/target_cfg/mod.rs create mode 100644 tests/testsuite/cargo_add/target_cfg/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/target_cfg/stderr.log create mode 100644 tests/testsuite/cargo_add/target_cfg/stdout.log create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/mod.rs create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/stderr.log create mode 100644 tests/testsuite/cargo_add/unknown_inherited_feature/stdout.log create mode 120000 tests/testsuite/cargo_add/vers/in create mode 100644 tests/testsuite/cargo_add/vers/mod.rs create mode 100644 tests/testsuite/cargo_add/vers/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/vers/stderr.log create mode 100644 tests/testsuite/cargo_add/vers/stdout.log create mode 100644 tests/testsuite/cargo_add/workspace_name/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_name/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_name/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/workspace_name/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_name/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/workspace_name/mod.rs create mode 100644 tests/testsuite/cargo_add/workspace_name/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_name/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_name/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_name/stderr.log create mode 100644 tests/testsuite/cargo_add/workspace_name/stdout.log create mode 100644 tests/testsuite/cargo_add/workspace_path/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/workspace_path/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/workspace_path/mod.rs create mode 100644 tests/testsuite/cargo_add/workspace_path/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path/stderr.log create mode 100644 tests/testsuite/cargo_add/workspace_path/stdout.log create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/in/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/in/dependency/src/lib.rs create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/in/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/in/primary/src/lib.rs create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/mod.rs create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/out/dependency/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/out/primary/Cargo.toml create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/stderr.log create mode 100644 tests/testsuite/cargo_add/workspace_path_dev/stdout.log create mode 100644 tests/testsuite/cargo_alias_config.rs create mode 100644 tests/testsuite/cargo_command.rs create mode 100644 tests/testsuite/cargo_config.rs create mode 100644 tests/testsuite/cargo_env_config.rs create mode 100644 tests/testsuite/cargo_features.rs create mode 120000 tests/testsuite/cargo_remove/avoid_empty_tables/in create mode 100644 tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs create mode 100644 tests/testsuite/cargo_remove/avoid_empty_tables/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/avoid_empty_tables/stderr.log create mode 100644 tests/testsuite/cargo_remove/avoid_empty_tables/stdout.log create mode 120000 tests/testsuite/cargo_remove/build/in create mode 100644 tests/testsuite/cargo_remove/build/mod.rs create mode 100644 tests/testsuite/cargo_remove/build/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/build/stderr.log create mode 100644 tests/testsuite/cargo_remove/build/stdout.log create mode 120000 tests/testsuite/cargo_remove/dev/in create mode 100644 tests/testsuite/cargo_remove/dev/mod.rs create mode 100644 tests/testsuite/cargo_remove/dev/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/dev/stderr.log create mode 100644 tests/testsuite/cargo_remove/dev/stdout.log create mode 120000 tests/testsuite/cargo_remove/dry_run/in create mode 100644 tests/testsuite/cargo_remove/dry_run/mod.rs create mode 100644 tests/testsuite/cargo_remove/dry_run/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/dry_run/out/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/dry_run/stderr.log create mode 100644 tests/testsuite/cargo_remove/dry_run/stdout.log create mode 100644 tests/testsuite/cargo_remove/gc_patch/mod.rs create mode 100644 tests/testsuite/cargo_remove/gc_patch/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/gc_patch/out/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/gc_patch/stderr.log create mode 100644 tests/testsuite/cargo_remove/gc_patch/stdout.log create mode 100644 tests/testsuite/cargo_remove/gc_profile/in/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/gc_profile/in/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/gc_profile/mod.rs create mode 100644 tests/testsuite/cargo_remove/gc_profile/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/gc_profile/stderr.log create mode 100644 tests/testsuite/cargo_remove/gc_profile/stdout.log create mode 100644 tests/testsuite/cargo_remove/gc_replace/in/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/gc_replace/in/my-package/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/gc_replace/in/my-package/src/main.rs create mode 100644 tests/testsuite/cargo_remove/gc_replace/mod.rs create mode 100644 tests/testsuite/cargo_remove/gc_replace/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/gc_replace/out/my-package/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/gc_replace/out/my-package/src/main.rs create mode 100644 tests/testsuite/cargo_remove/gc_replace/stderr.log create mode 100644 tests/testsuite/cargo_remove/gc_replace/stdout.log create mode 120000 tests/testsuite/cargo_remove/invalid_arg/in create mode 100644 tests/testsuite/cargo_remove/invalid_arg/mod.rs create mode 100644 tests/testsuite/cargo_remove/invalid_arg/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_arg/stderr.log create mode 100644 tests/testsuite/cargo_remove/invalid_arg/stdout.log create mode 120000 tests/testsuite/cargo_remove/invalid_dep/in create mode 100644 tests/testsuite/cargo_remove/invalid_dep/mod.rs create mode 100644 tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_dep/stderr.log create mode 100644 tests/testsuite/cargo_remove/invalid_dep/stdout.log create mode 120000 tests/testsuite/cargo_remove/invalid_package/in create mode 100644 tests/testsuite/cargo_remove/invalid_package/mod.rs create mode 100644 tests/testsuite/cargo_remove/invalid_package/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_package/out/dep-a/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_package/out/dep-a/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/invalid_package/out/dep-b/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_package/out/dep-b/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/invalid_package/stderr.log create mode 100644 tests/testsuite/cargo_remove/invalid_package/stdout.log create mode 120000 tests/testsuite/cargo_remove/invalid_package_multiple/in create mode 100644 tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs create mode 100644 tests/testsuite/cargo_remove/invalid_package_multiple/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-a/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-a/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-b/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-b/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/invalid_package_multiple/stderr.log create mode 100644 tests/testsuite/cargo_remove/invalid_package_multiple/stdout.log create mode 120000 tests/testsuite/cargo_remove/invalid_section/in create mode 100644 tests/testsuite/cargo_remove/invalid_section/mod.rs create mode 100644 tests/testsuite/cargo_remove/invalid_section/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_section/stderr.log create mode 100644 tests/testsuite/cargo_remove/invalid_section/stdout.log create mode 120000 tests/testsuite/cargo_remove/invalid_section_dep/in create mode 100644 tests/testsuite/cargo_remove/invalid_section_dep/mod.rs create mode 100644 tests/testsuite/cargo_remove/invalid_section_dep/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_section_dep/stderr.log create mode 100644 tests/testsuite/cargo_remove/invalid_section_dep/stdout.log create mode 120000 tests/testsuite/cargo_remove/invalid_target/in create mode 100644 tests/testsuite/cargo_remove/invalid_target/mod.rs create mode 100644 tests/testsuite/cargo_remove/invalid_target/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_target/stderr.log create mode 100644 tests/testsuite/cargo_remove/invalid_target/stdout.log create mode 120000 tests/testsuite/cargo_remove/invalid_target_dep/in create mode 100644 tests/testsuite/cargo_remove/invalid_target_dep/mod.rs create mode 100644 tests/testsuite/cargo_remove/invalid_target_dep/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_target_dep/stderr.log create mode 100644 tests/testsuite/cargo_remove/invalid_target_dep/stdout.log create mode 100644 tests/testsuite/cargo_remove/mod.rs create mode 120000 tests/testsuite/cargo_remove/multiple_deps/in create mode 100644 tests/testsuite/cargo_remove/multiple_deps/mod.rs create mode 100644 tests/testsuite/cargo_remove/multiple_deps/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/multiple_deps/stderr.log create mode 100644 tests/testsuite/cargo_remove/multiple_deps/stdout.log create mode 120000 tests/testsuite/cargo_remove/multiple_dev/in create mode 100644 tests/testsuite/cargo_remove/multiple_dev/mod.rs create mode 100644 tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/multiple_dev/stderr.log create mode 100644 tests/testsuite/cargo_remove/multiple_dev/stdout.log create mode 120000 tests/testsuite/cargo_remove/no_arg/in create mode 100644 tests/testsuite/cargo_remove/no_arg/mod.rs create mode 100644 tests/testsuite/cargo_remove/no_arg/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/no_arg/stderr.log create mode 100644 tests/testsuite/cargo_remove/no_arg/stdout.log create mode 120000 tests/testsuite/cargo_remove/offline/in create mode 100644 tests/testsuite/cargo_remove/offline/mod.rs create mode 100644 tests/testsuite/cargo_remove/offline/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/offline/stderr.log create mode 100644 tests/testsuite/cargo_remove/offline/stdout.log create mode 120000 tests/testsuite/cargo_remove/optional_dep_feature/in create mode 100644 tests/testsuite/cargo_remove/optional_dep_feature/mod.rs create mode 100644 tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/optional_dep_feature/stderr.log create mode 100644 tests/testsuite/cargo_remove/optional_dep_feature/stdout.log create mode 120000 tests/testsuite/cargo_remove/optional_feature/in create mode 100644 tests/testsuite/cargo_remove/optional_feature/mod.rs create mode 100644 tests/testsuite/cargo_remove/optional_feature/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/optional_feature/stderr.log create mode 100644 tests/testsuite/cargo_remove/optional_feature/stdout.log create mode 120000 tests/testsuite/cargo_remove/package/in create mode 100644 tests/testsuite/cargo_remove/package/mod.rs create mode 100644 tests/testsuite/cargo_remove/package/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/package/out/dep-a/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/package/out/dep-a/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/package/out/dep-b/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/package/out/dep-b/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/package/stderr.log create mode 100644 tests/testsuite/cargo_remove/package/stdout.log create mode 100644 tests/testsuite/cargo_remove/remove-basic.in/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/remove-basic.in/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/remove-package.in/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/remove-package.in/dep-a/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/remove-package.in/dep-a/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/remove-package.in/dep-b/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/remove-package.in/dep-b/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/remove-target.in/Cargo.toml create mode 120000 tests/testsuite/cargo_remove/remove_basic/in create mode 100644 tests/testsuite/cargo_remove/remove_basic/mod.rs create mode 100644 tests/testsuite/cargo_remove/remove_basic/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/remove_basic/stderr.log create mode 100644 tests/testsuite/cargo_remove/remove_basic/stdout.log create mode 120000 tests/testsuite/cargo_remove/target/in create mode 100644 tests/testsuite/cargo_remove/target/mod.rs create mode 100644 tests/testsuite/cargo_remove/target/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/target/stderr.log create mode 100644 tests/testsuite/cargo_remove/target/stdout.log create mode 120000 tests/testsuite/cargo_remove/target_build/in create mode 100644 tests/testsuite/cargo_remove/target_build/mod.rs create mode 100644 tests/testsuite/cargo_remove/target_build/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/target_build/stderr.log create mode 100644 tests/testsuite/cargo_remove/target_build/stdout.log create mode 120000 tests/testsuite/cargo_remove/target_dev/in create mode 100644 tests/testsuite/cargo_remove/target_dev/mod.rs create mode 100644 tests/testsuite/cargo_remove/target_dev/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/target_dev/stderr.log create mode 100644 tests/testsuite/cargo_remove/target_dev/stdout.log create mode 100644 tests/testsuite/cargo_remove/update_lock_file/in/Cargo.lock create mode 100644 tests/testsuite/cargo_remove/update_lock_file/in/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/update_lock_file/in/src/main.rs create mode 100644 tests/testsuite/cargo_remove/update_lock_file/mod.rs create mode 100644 tests/testsuite/cargo_remove/update_lock_file/out/Cargo.lock create mode 100644 tests/testsuite/cargo_remove/update_lock_file/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/update_lock_file/out/src/main.rs create mode 100644 tests/testsuite/cargo_remove/update_lock_file/stderr.log create mode 100644 tests/testsuite/cargo_remove/update_lock_file/stdout.log create mode 100644 tests/testsuite/cargo_remove/workspace/in/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace/in/my-package/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace/in/my-package/src/main.rs create mode 100644 tests/testsuite/cargo_remove/workspace/mod.rs create mode 100644 tests/testsuite/cargo_remove/workspace/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace/out/my-package/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace/out/my-package/src/main.rs create mode 100644 tests/testsuite/cargo_remove/workspace/stderr.log create mode 100644 tests/testsuite/cargo_remove/workspace/stdout.log create mode 100644 tests/testsuite/cargo_remove/workspace_non_virtual/in/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/src/main.rs create mode 100644 tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs create mode 100644 tests/testsuite/cargo_remove/workspace_non_virtual/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/src/main.rs create mode 100644 tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log create mode 100644 tests/testsuite/cargo_remove/workspace_non_virtual/stdout.log create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/in/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/src/main.rs create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/in/my-package/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/in/my-package/src/main.rs create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/mod.rs create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/src/main.rs create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/out/my-package/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/out/my-package/src/main.rs create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/stderr.log create mode 100644 tests/testsuite/cargo_remove/workspace_preserved/stdout.log create mode 100644 tests/testsuite/cargo_targets.rs create mode 100644 tests/testsuite/cfg.rs create mode 100644 tests/testsuite/check.rs create mode 100644 tests/testsuite/check_cfg.rs create mode 100644 tests/testsuite/clean.rs create mode 100644 tests/testsuite/collisions.rs create mode 100644 tests/testsuite/concurrent.rs create mode 100644 tests/testsuite/config.rs create mode 100644 tests/testsuite/config_cli.rs create mode 100644 tests/testsuite/config_include.rs create mode 100644 tests/testsuite/corrupt_git.rs create mode 100644 tests/testsuite/credential_process.rs create mode 100644 tests/testsuite/cross_compile.rs create mode 100644 tests/testsuite/cross_publish.rs create mode 100644 tests/testsuite/custom_target.rs create mode 100644 tests/testsuite/death.rs create mode 100644 tests/testsuite/dep_info.rs create mode 100644 tests/testsuite/directory.rs create mode 100644 tests/testsuite/doc.rs create mode 100644 tests/testsuite/docscrape.rs create mode 100644 tests/testsuite/edition.rs create mode 100644 tests/testsuite/error.rs create mode 100644 tests/testsuite/features.rs create mode 100644 tests/testsuite/features2.rs create mode 100644 tests/testsuite/features_namespaced.rs create mode 100644 tests/testsuite/fetch.rs create mode 100644 tests/testsuite/fix.rs create mode 100644 tests/testsuite/freshness.rs create mode 100644 tests/testsuite/future_incompat_report.rs create mode 100644 tests/testsuite/generate_lockfile.rs create mode 100644 tests/testsuite/git.rs create mode 100644 tests/testsuite/git_auth.rs create mode 100644 tests/testsuite/git_gc.rs create mode 100644 tests/testsuite/glob_targets.rs create mode 100644 tests/testsuite/help.rs create mode 100644 tests/testsuite/https.rs create mode 100644 tests/testsuite/inheritable_workspace_fields.rs create mode 120000 tests/testsuite/init/auto_git/in create mode 100644 tests/testsuite/init/auto_git/mod.rs create mode 100644 tests/testsuite/init/auto_git/out/.gitignore create mode 100644 tests/testsuite/init/auto_git/out/Cargo.toml create mode 100644 tests/testsuite/init/auto_git/out/src/lib.rs create mode 100644 tests/testsuite/init/auto_git/stderr.log create mode 100644 tests/testsuite/init/auto_git/stdout.log create mode 100644 tests/testsuite/init/bin_already_exists_explicit/in/src/main.rs create mode 100644 tests/testsuite/init/bin_already_exists_explicit/mod.rs create mode 100644 tests/testsuite/init/bin_already_exists_explicit/out/Cargo.toml create mode 100644 tests/testsuite/init/bin_already_exists_explicit/out/src/main.rs create mode 100644 tests/testsuite/init/bin_already_exists_explicit/stderr.log create mode 100644 tests/testsuite/init/bin_already_exists_explicit/stdout.log create mode 100644 tests/testsuite/init/bin_already_exists_explicit_nosrc/in/main.rs create mode 100644 tests/testsuite/init/bin_already_exists_explicit_nosrc/mod.rs create mode 100644 tests/testsuite/init/bin_already_exists_explicit_nosrc/out/Cargo.toml create mode 100644 tests/testsuite/init/bin_already_exists_explicit_nosrc/out/main.rs create mode 100644 tests/testsuite/init/bin_already_exists_explicit_nosrc/stderr.log create mode 100644 tests/testsuite/init/bin_already_exists_explicit_nosrc/stdout.log create mode 100644 tests/testsuite/init/bin_already_exists_implicit/in/src/main.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit/mod.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit/out/Cargo.toml create mode 100644 tests/testsuite/init/bin_already_exists_implicit/out/src/main.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit/stderr.log create mode 100644 tests/testsuite/init/bin_already_exists_implicit/stdout.log create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namenosrc/in/case.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namenosrc/mod.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/Cargo.toml create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/case.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namenosrc/stderr.log create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namenosrc/stdout.log create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namesrc/in/src/case.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namesrc/mod.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namesrc/out/Cargo.toml create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namesrc/out/src/case.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namesrc/stderr.log create mode 100644 tests/testsuite/init/bin_already_exists_implicit_namesrc/stdout.log create mode 100644 tests/testsuite/init/bin_already_exists_implicit_nosrc/in/main.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit_nosrc/mod.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit_nosrc/out/Cargo.toml create mode 100644 tests/testsuite/init/bin_already_exists_implicit_nosrc/out/main.rs create mode 100644 tests/testsuite/init/bin_already_exists_implicit_nosrc/stderr.log create mode 100644 tests/testsuite/init/bin_already_exists_implicit_nosrc/stdout.log create mode 100644 tests/testsuite/init/both_lib_and_bin/mod.rs create mode 100644 tests/testsuite/init/both_lib_and_bin/stderr.log create mode 100644 tests/testsuite/init/both_lib_and_bin/stdout.log create mode 100644 tests/testsuite/init/cant_create_library_when_both_binlib_present/in/case.rs create mode 100644 tests/testsuite/init/cant_create_library_when_both_binlib_present/in/lib.rs create mode 100644 tests/testsuite/init/cant_create_library_when_both_binlib_present/mod.rs create mode 100644 tests/testsuite/init/cant_create_library_when_both_binlib_present/stderr.log create mode 100644 tests/testsuite/init/cant_create_library_when_both_binlib_present/stdout.log create mode 100644 tests/testsuite/init/confused_by_multiple_lib_files/in/lib.rs create mode 100644 tests/testsuite/init/confused_by_multiple_lib_files/in/src/lib.rs create mode 100644 tests/testsuite/init/confused_by_multiple_lib_files/mod.rs create mode 100644 tests/testsuite/init/confused_by_multiple_lib_files/out/lib.rs create mode 100644 tests/testsuite/init/confused_by_multiple_lib_files/out/src/lib.rs create mode 100644 tests/testsuite/init/confused_by_multiple_lib_files/stderr.log create mode 100644 tests/testsuite/init/confused_by_multiple_lib_files/stdout.log create mode 100644 tests/testsuite/init/creates_binary_when_both_binlib_present/in/case.rs create mode 100644 tests/testsuite/init/creates_binary_when_both_binlib_present/in/lib.rs create mode 100644 tests/testsuite/init/creates_binary_when_both_binlib_present/mod.rs create mode 100644 tests/testsuite/init/creates_binary_when_both_binlib_present/out/Cargo.toml create mode 100644 tests/testsuite/init/creates_binary_when_both_binlib_present/out/case.rs create mode 100644 tests/testsuite/init/creates_binary_when_both_binlib_present/out/lib.rs create mode 100644 tests/testsuite/init/creates_binary_when_both_binlib_present/stderr.log create mode 100644 tests/testsuite/init/creates_binary_when_both_binlib_present/stdout.log create mode 100644 tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/in/case.rs create mode 100644 tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/mod.rs create mode 100644 tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml create mode 100644 tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/case.rs create mode 100644 tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stderr.log create mode 100644 tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stdout.log create mode 100644 tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/in/case.rs create mode 100644 tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/mod.rs create mode 100644 tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml create mode 100644 tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/case.rs create mode 100644 tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stderr.log create mode 100644 tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stdout.log create mode 100644 tests/testsuite/init/empty_dir/.keep create mode 100644 tests/testsuite/init/empty_dir/mod.rs create mode 120000 tests/testsuite/init/explicit_bin_with_git/in create mode 100644 tests/testsuite/init/explicit_bin_with_git/mod.rs create mode 100644 tests/testsuite/init/explicit_bin_with_git/out/.gitignore create mode 100644 tests/testsuite/init/explicit_bin_with_git/out/Cargo.toml create mode 100644 tests/testsuite/init/explicit_bin_with_git/out/src/main.rs create mode 100644 tests/testsuite/init/explicit_bin_with_git/stderr.log create mode 100644 tests/testsuite/init/explicit_bin_with_git/stdout.log create mode 100644 tests/testsuite/init/formats_source/in/rustfmt.toml create mode 100644 tests/testsuite/init/formats_source/mod.rs create mode 100644 tests/testsuite/init/formats_source/out/Cargo.toml create mode 100644 tests/testsuite/init/formats_source/out/rustfmt.toml create mode 100644 tests/testsuite/init/formats_source/out/src/lib.rs create mode 100644 tests/testsuite/init/formats_source/stderr.log create mode 100644 tests/testsuite/init/formats_source/stdout.log create mode 100644 tests/testsuite/init/fossil_autodetect/in/.fossil/.keep create mode 100644 tests/testsuite/init/fossil_autodetect/mod.rs create mode 100644 tests/testsuite/init/fossil_autodetect/out/.fossil-settings/clean-glob create mode 100644 tests/testsuite/init/fossil_autodetect/out/.fossil-settings/ignore-glob create mode 100644 tests/testsuite/init/fossil_autodetect/out/Cargo.toml create mode 100644 tests/testsuite/init/fossil_autodetect/out/src/lib.rs create mode 100644 tests/testsuite/init/fossil_autodetect/stderr.log create mode 100644 tests/testsuite/init/fossil_autodetect/stdout.log create mode 100644 tests/testsuite/init/git_autodetect/mod.rs create mode 100644 tests/testsuite/init/git_autodetect/out/.gitignore create mode 100644 tests/testsuite/init/git_autodetect/out/Cargo.toml create mode 100644 tests/testsuite/init/git_autodetect/out/src/lib.rs create mode 100644 tests/testsuite/init/git_autodetect/stderr.log create mode 100644 tests/testsuite/init/git_autodetect/stdout.log create mode 100644 tests/testsuite/init/git_ignore_exists_no_conflicting_entries/in/.gitignore create mode 100644 tests/testsuite/init/git_ignore_exists_no_conflicting_entries/mod.rs create mode 100644 tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/.gitignore create mode 100644 tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/Cargo.toml create mode 100644 tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/src/lib.rs create mode 100644 tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stderr.log create mode 100644 tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stdout.log create mode 100644 tests/testsuite/init/ignores_failure_to_format_source/in/rustfmt.toml create mode 100644 tests/testsuite/init/ignores_failure_to_format_source/mod.rs create mode 100644 tests/testsuite/init/ignores_failure_to_format_source/out/Cargo.toml create mode 100644 tests/testsuite/init/ignores_failure_to_format_source/out/rustfmt.toml create mode 100644 tests/testsuite/init/ignores_failure_to_format_source/out/src/lib.rs create mode 100644 tests/testsuite/init/ignores_failure_to_format_source/stderr.log create mode 100644 tests/testsuite/init/ignores_failure_to_format_source/stdout.log create mode 100644 tests/testsuite/init/inferred_bin_with_git/in/main.rs create mode 100644 tests/testsuite/init/inferred_bin_with_git/mod.rs create mode 100644 tests/testsuite/init/inferred_bin_with_git/out/.gitignore create mode 100644 tests/testsuite/init/inferred_bin_with_git/out/Cargo.toml create mode 100644 tests/testsuite/init/inferred_bin_with_git/out/main.rs create mode 100644 tests/testsuite/init/inferred_bin_with_git/stderr.log create mode 100644 tests/testsuite/init/inferred_bin_with_git/stdout.log create mode 100644 tests/testsuite/init/inferred_lib_with_git/in/lib.rs create mode 100644 tests/testsuite/init/inferred_lib_with_git/mod.rs create mode 100644 tests/testsuite/init/inferred_lib_with_git/out/.gitignore create mode 100644 tests/testsuite/init/inferred_lib_with_git/out/Cargo.toml create mode 100644 tests/testsuite/init/inferred_lib_with_git/out/lib.rs create mode 100644 tests/testsuite/init/inferred_lib_with_git/stderr.log create mode 100644 tests/testsuite/init/inferred_lib_with_git/stdout.log create mode 100644 tests/testsuite/init/invalid_dir_name/mod.rs create mode 100644 tests/testsuite/init/invalid_dir_name/stderr.log create mode 100644 tests/testsuite/init/invalid_dir_name/stdout.log create mode 100644 tests/testsuite/init/lib_already_exists_nosrc/in/lib.rs create mode 100644 tests/testsuite/init/lib_already_exists_nosrc/mod.rs create mode 100644 tests/testsuite/init/lib_already_exists_nosrc/out/Cargo.toml create mode 100644 tests/testsuite/init/lib_already_exists_nosrc/out/lib.rs create mode 100644 tests/testsuite/init/lib_already_exists_nosrc/stderr.log create mode 100644 tests/testsuite/init/lib_already_exists_nosrc/stdout.log create mode 100644 tests/testsuite/init/lib_already_exists_src/in/src/lib.rs create mode 100644 tests/testsuite/init/lib_already_exists_src/mod.rs create mode 100644 tests/testsuite/init/lib_already_exists_src/out/Cargo.toml create mode 100644 tests/testsuite/init/lib_already_exists_src/out/src/lib.rs create mode 100644 tests/testsuite/init/lib_already_exists_src/stderr.log create mode 100644 tests/testsuite/init/lib_already_exists_src/stdout.log create mode 100644 tests/testsuite/init/mercurial_autodetect/in/.hg/.keep create mode 100644 tests/testsuite/init/mercurial_autodetect/mod.rs create mode 100644 tests/testsuite/init/mercurial_autodetect/out/.hgignore create mode 100644 tests/testsuite/init/mercurial_autodetect/out/Cargo.toml create mode 100644 tests/testsuite/init/mercurial_autodetect/out/src/lib.rs create mode 100644 tests/testsuite/init/mercurial_autodetect/stderr.log create mode 100644 tests/testsuite/init/mercurial_autodetect/stdout.log create mode 100644 tests/testsuite/init/mod.rs create mode 100644 tests/testsuite/init/multibin_project_name_clash/in/case.rs create mode 100644 tests/testsuite/init/multibin_project_name_clash/in/main.rs create mode 100644 tests/testsuite/init/multibin_project_name_clash/mod.rs create mode 100644 tests/testsuite/init/multibin_project_name_clash/out/case.rs create mode 100644 tests/testsuite/init/multibin_project_name_clash/out/main.rs create mode 100644 tests/testsuite/init/multibin_project_name_clash/stderr.log create mode 100644 tests/testsuite/init/multibin_project_name_clash/stdout.log create mode 100644 tests/testsuite/init/no_filename/mod.rs create mode 100644 tests/testsuite/init/no_filename/stderr.log create mode 100644 tests/testsuite/init/no_filename/stdout.log create mode 100644 tests/testsuite/init/path_contains_separator/in/.keep create mode 100644 tests/testsuite/init/path_contains_separator/mod.rs create mode 100644 tests/testsuite/init/path_contains_separator/out/Cargo.toml create mode 100644 tests/testsuite/init/path_contains_separator/out/src/main.rs create mode 100644 tests/testsuite/init/path_contains_separator/stderr.log create mode 100644 tests/testsuite/init/path_contains_separator/stdout.log create mode 100644 tests/testsuite/init/pijul_autodetect/in/.pijul/.keep create mode 100644 tests/testsuite/init/pijul_autodetect/mod.rs create mode 100644 tests/testsuite/init/pijul_autodetect/out/.ignore create mode 100644 tests/testsuite/init/pijul_autodetect/out/Cargo.toml create mode 100644 tests/testsuite/init/pijul_autodetect/out/src/lib.rs create mode 100644 tests/testsuite/init/pijul_autodetect/stderr.log create mode 100644 tests/testsuite/init/pijul_autodetect/stdout.log create mode 100644 tests/testsuite/init/reserved_name/mod.rs create mode 100644 tests/testsuite/init/reserved_name/stderr.log create mode 100644 tests/testsuite/init/reserved_name/stdout.log create mode 120000 tests/testsuite/init/simple_bin/in create mode 100644 tests/testsuite/init/simple_bin/mod.rs create mode 100644 tests/testsuite/init/simple_bin/out/Cargo.toml create mode 100644 tests/testsuite/init/simple_bin/out/src/main.rs create mode 100644 tests/testsuite/init/simple_bin/stderr.log create mode 100644 tests/testsuite/init/simple_bin/stdout.log create mode 120000 tests/testsuite/init/simple_git/in create mode 100644 tests/testsuite/init/simple_git/mod.rs create mode 100644 tests/testsuite/init/simple_git/out/.gitignore create mode 100644 tests/testsuite/init/simple_git/out/Cargo.toml create mode 100644 tests/testsuite/init/simple_git/out/src/lib.rs create mode 100644 tests/testsuite/init/simple_git/stderr.log create mode 100644 tests/testsuite/init/simple_git/stdout.log create mode 100644 tests/testsuite/init/simple_git_ignore_exists/in/.gitignore create mode 100644 tests/testsuite/init/simple_git_ignore_exists/mod.rs create mode 100644 tests/testsuite/init/simple_git_ignore_exists/out/.gitignore create mode 100644 tests/testsuite/init/simple_git_ignore_exists/out/Cargo.toml create mode 100644 tests/testsuite/init/simple_git_ignore_exists/out/src/lib.rs create mode 100644 tests/testsuite/init/simple_git_ignore_exists/stderr.log create mode 100644 tests/testsuite/init/simple_git_ignore_exists/stdout.log create mode 120000 tests/testsuite/init/simple_hg/in create mode 100644 tests/testsuite/init/simple_hg/mod.rs create mode 100644 tests/testsuite/init/simple_hg/out/.hgignore create mode 100644 tests/testsuite/init/simple_hg/out/Cargo.toml create mode 100644 tests/testsuite/init/simple_hg/out/src/lib.rs create mode 100644 tests/testsuite/init/simple_hg/stderr.log create mode 100644 tests/testsuite/init/simple_hg/stdout.log create mode 100644 tests/testsuite/init/simple_hg_ignore_exists/in/.hg/.keep create mode 100644 tests/testsuite/init/simple_hg_ignore_exists/in/.hgignore create mode 100644 tests/testsuite/init/simple_hg_ignore_exists/mod.rs create mode 100644 tests/testsuite/init/simple_hg_ignore_exists/out/.hgignore create mode 100644 tests/testsuite/init/simple_hg_ignore_exists/out/Cargo.toml create mode 100644 tests/testsuite/init/simple_hg_ignore_exists/out/src/lib.rs create mode 100644 tests/testsuite/init/simple_hg_ignore_exists/stderr.log create mode 100644 tests/testsuite/init/simple_hg_ignore_exists/stdout.log create mode 120000 tests/testsuite/init/simple_lib/in create mode 100644 tests/testsuite/init/simple_lib/mod.rs create mode 100644 tests/testsuite/init/simple_lib/out/Cargo.toml create mode 100644 tests/testsuite/init/simple_lib/out/src/lib.rs create mode 100644 tests/testsuite/init/simple_lib/stderr.log create mode 100644 tests/testsuite/init/simple_lib/stdout.log create mode 100644 tests/testsuite/init/unknown_flags/mod.rs create mode 100644 tests/testsuite/init/unknown_flags/stderr.log create mode 100644 tests/testsuite/init/unknown_flags/stdout.log create mode 100644 tests/testsuite/init/with_argument/in/foo/.keep create mode 100644 tests/testsuite/init/with_argument/mod.rs create mode 100644 tests/testsuite/init/with_argument/out/foo/Cargo.toml create mode 100644 tests/testsuite/init/with_argument/out/foo/src/main.rs create mode 100644 tests/testsuite/init/with_argument/stderr.log create mode 100644 tests/testsuite/init/with_argument/stdout.log create mode 100644 tests/testsuite/install.rs create mode 100644 tests/testsuite/install_upgrade.rs create mode 100644 tests/testsuite/jobserver.rs create mode 100644 tests/testsuite/list_availables.rs create mode 100644 tests/testsuite/local_registry.rs create mode 100644 tests/testsuite/locate_project.rs create mode 100644 tests/testsuite/lockfile_compat.rs create mode 100644 tests/testsuite/login.rs create mode 100644 tests/testsuite/logout.rs create mode 100644 tests/testsuite/lto.rs create mode 100644 tests/testsuite/main.rs create mode 100644 tests/testsuite/member_discovery.rs create mode 100644 tests/testsuite/member_errors.rs create mode 100644 tests/testsuite/message_format.rs create mode 100644 tests/testsuite/messages.rs create mode 100644 tests/testsuite/metabuild.rs create mode 100644 tests/testsuite/metadata.rs create mode 100644 tests/testsuite/minimal_versions.rs create mode 100644 tests/testsuite/mock-std/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/alloc/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/alloc/src/lib.rs create mode 100644 tests/testsuite/mock-std/library/compiler_builtins/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/compiler_builtins/src/lib.rs create mode 100644 tests/testsuite/mock-std/library/core/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/core/src/lib.rs create mode 100644 tests/testsuite/mock-std/library/panic_unwind/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/panic_unwind/src/lib.rs create mode 100644 tests/testsuite/mock-std/library/proc_macro/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/proc_macro/src/lib.rs create mode 100644 tests/testsuite/mock-std/library/rustc-std-workspace-alloc/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/rustc-std-workspace-alloc/lib.rs create mode 100644 tests/testsuite/mock-std/library/rustc-std-workspace-core/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/rustc-std-workspace-core/lib.rs create mode 100644 tests/testsuite/mock-std/library/rustc-std-workspace-std/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/rustc-std-workspace-std/lib.rs create mode 100644 tests/testsuite/mock-std/library/std/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/std/src/lib.rs create mode 100644 tests/testsuite/mock-std/library/test/Cargo.toml create mode 100644 tests/testsuite/mock-std/library/test/src/lib.rs create mode 100644 tests/testsuite/multitarget.rs create mode 100644 tests/testsuite/net_config.rs create mode 100644 tests/testsuite/new.rs create mode 100644 tests/testsuite/offline.rs create mode 100644 tests/testsuite/old_cargos.rs create mode 100644 tests/testsuite/out_dir.rs create mode 100644 tests/testsuite/owner.rs create mode 100644 tests/testsuite/package.rs create mode 100644 tests/testsuite/package_features.rs create mode 100644 tests/testsuite/patch.rs create mode 100644 tests/testsuite/path.rs create mode 100644 tests/testsuite/paths.rs create mode 100644 tests/testsuite/pkgid.rs create mode 100644 tests/testsuite/plugins.rs create mode 100644 tests/testsuite/proc_macro.rs create mode 100644 tests/testsuite/profile_config.rs create mode 100644 tests/testsuite/profile_custom.rs create mode 100644 tests/testsuite/profile_overrides.rs create mode 100644 tests/testsuite/profile_targets.rs create mode 100644 tests/testsuite/profiles.rs create mode 100644 tests/testsuite/progress.rs create mode 100644 tests/testsuite/pub_priv.rs create mode 100644 tests/testsuite/publish.rs create mode 100644 tests/testsuite/publish_lockfile.rs create mode 100644 tests/testsuite/read_manifest.rs create mode 100644 tests/testsuite/registry.rs create mode 100644 tests/testsuite/registry_auth.rs create mode 100644 tests/testsuite/rename_deps.rs create mode 100644 tests/testsuite/replace.rs create mode 100644 tests/testsuite/required_features.rs create mode 100644 tests/testsuite/run.rs create mode 100644 tests/testsuite/rust_version.rs create mode 100644 tests/testsuite/rustc.rs create mode 100644 tests/testsuite/rustc_info_cache.rs create mode 100644 tests/testsuite/rustdoc.rs create mode 100644 tests/testsuite/rustdoc_extern_html.rs create mode 100644 tests/testsuite/rustdocflags.rs create mode 100644 tests/testsuite/rustflags.rs create mode 100644 tests/testsuite/search.rs create mode 100644 tests/testsuite/shell_quoting.rs create mode 100644 tests/testsuite/source_replacement.rs create mode 100644 tests/testsuite/ssh.rs create mode 100644 tests/testsuite/standard_lib.rs create mode 100644 tests/testsuite/test.rs create mode 100644 tests/testsuite/timings.rs create mode 100644 tests/testsuite/tool_paths.rs create mode 100644 tests/testsuite/tree.rs create mode 100644 tests/testsuite/tree_graph_features.rs create mode 100644 tests/testsuite/unit_graph.rs create mode 100644 tests/testsuite/update.rs create mode 100644 tests/testsuite/vendor.rs create mode 100644 tests/testsuite/verify_project.rs create mode 100644 tests/testsuite/version.rs create mode 100644 tests/testsuite/warn_on_failure.rs create mode 100644 tests/testsuite/weak_dep_features.rs create mode 100644 tests/testsuite/workspaces.rs create mode 100644 tests/testsuite/yank.rs (limited to 'tests/testsuite') diff --git a/tests/testsuite/advanced_env.rs b/tests/testsuite/advanced_env.rs new file mode 100644 index 0000000..8aab528 --- /dev/null +++ b/tests/testsuite/advanced_env.rs @@ -0,0 +1,35 @@ +//! -Zadvanced-env tests + +use cargo_test_support::{paths, project, registry::Package}; + +#[cargo_test] +fn source_config_env() { + // Try to define [source] with environment variables. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + somedep = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("somedep", "1.0.0") + .local(true) + .file("src/lib.rs", "") + .publish(); + + let path = paths::root().join("registry"); + + p.cargo("check -Zadvanced-env") + .masquerade_as_nightly_cargo(&["advanced-env"]) + .env("CARGO_SOURCE_crates-io_REPLACE_WITH", "my-local-source") + .env("CARGO_SOURCE_my-local-source_LOCAL_REGISTRY", path) + .run(); +} diff --git a/tests/testsuite/alt_registry.rs b/tests/testsuite/alt_registry.rs new file mode 100644 index 0000000..d9bbf47 --- /dev/null +++ b/tests/testsuite/alt_registry.rs @@ -0,0 +1,1434 @@ +//! Tests for alternative registries. + +use cargo_test_support::compare::assert_match_exact; +use cargo_test_support::publish::validate_alt_upload; +use cargo_test_support::registry::{self, Package, RegistryBuilder}; +use cargo_test_support::{basic_manifest, paths, project}; +use std::fs; + +#[cargo_test] +fn depend_on_alt_registry() { + registry::alt_init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "0.0.1" + registry = "alternative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").alternative(true).publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `alternative` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `alternative`) +[CHECKING] bar v0.0.1 (registry `alternative`) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + + p.cargo("clean").run(); + + // Don't download a second time + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.0.1 (registry `alternative`) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn depend_on_alt_registry_depends_on_same_registry_no_index() { + registry::alt_init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "0.0.1" + registry = "alternative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("baz", "0.0.1").alternative(true).publish(); + Package::new("bar", "0.0.1") + .registry_dep("baz", "0.0.1") + .alternative(true) + .publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `alternative` index +[DOWNLOADING] crates ... +[DOWNLOADED] [..] v0.0.1 (registry `alternative`) +[DOWNLOADED] [..] v0.0.1 (registry `alternative`) +[CHECKING] baz v0.0.1 (registry `alternative`) +[CHECKING] bar v0.0.1 (registry `alternative`) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn depend_on_alt_registry_depends_on_same_registry() { + registry::alt_init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "0.0.1" + registry = "alternative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("baz", "0.0.1").alternative(true).publish(); + Package::new("bar", "0.0.1") + .registry_dep("baz", "0.0.1") + .alternative(true) + .publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `alternative` index +[DOWNLOADING] crates ... +[DOWNLOADED] [..] v0.0.1 (registry `alternative`) +[DOWNLOADED] [..] v0.0.1 (registry `alternative`) +[CHECKING] baz v0.0.1 (registry `alternative`) +[CHECKING] bar v0.0.1 (registry `alternative`) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn depend_on_alt_registry_depends_on_crates_io() { + registry::alt_init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "0.0.1" + registry = "alternative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("baz", "0.0.1").publish(); + Package::new("bar", "0.0.1") + .dep("baz", "0.0.1") + .alternative(true) + .publish(); + + p.cargo("check") + .with_stderr_unordered( + "\ +[UPDATING] `alternative` index +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] baz v0.0.1 (registry `dummy-registry`) +[DOWNLOADED] bar v0.0.1 (registry `alternative`) +[CHECKING] baz v0.0.1 +[CHECKING] bar v0.0.1 (registry `alternative`) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn registry_and_path_dep_works() { + registry::alt_init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + registry = "alternative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.0.1 ([CWD]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn registry_incompatible_with_git() { + registry::alt_init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + git = "" + registry = "alternative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains( + " dependency (bar) specification is ambiguous. \ + Only one of `git` or `registry` is allowed.", + ) + .run(); +} + +#[cargo_test] +fn cannot_publish_to_crates_io_with_registry_dependency() { + let crates_io = registry::init(); + let _alternative = RegistryBuilder::new().alternative().build(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + [dependencies.bar] + version = "0.0.1" + registry = "alternative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").alternative(true).publish(); + + p.cargo("publish") + .replace_crates_io(crates_io.index_url()) + .with_status(101) + .with_stderr_contains("[ERROR] crates cannot be published to crates.io[..]") + .run(); + + p.cargo("publish") + .replace_crates_io(crates_io.index_url()) + .arg("--token") + .arg(crates_io.token()) + .arg("--index") + .arg(crates_io.index_url().as_str()) + .with_status(101) + .with_stderr_contains("[ERROR] crates cannot be published to crates.io[..]") + .run(); +} + +#[cargo_test] +fn publish_with_registry_dependency() { + let _reg = RegistryBuilder::new() + .http_api() + .http_index() + .alternative() + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "0.0.1" + registry = "alternative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").alternative(true).publish(); + + p.cargo("publish --registry alternative") + .with_stderr( + "\ +[UPDATING] `alternative` index +[WARNING] [..] +[..] +[PACKAGING] foo v0.0.1 [..] +[UPDATING] `alternative` index +[VERIFYING] foo v0.0.1 [..] +[DOWNLOADING] [..] +[DOWNLOADED] bar v0.0.1 (registry `alternative`) +[COMPILING] bar v0.0.1 (registry `alternative`) +[COMPILING] foo v0.0.1 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] foo v0.0.1 [..] +[UPDATING] `alternative` index +", + ) + .run(); + + validate_alt_upload( + r#"{ + "authors": [], + "badges": {}, + "categories": [], + "deps": [ + { + "default_features": true, + "features": [], + "kind": "normal", + "name": "bar", + "optional": false, + "target": null, + "version_req": "^0.0.1" + } + ], + "description": null, + "documentation": null, + "features": {}, + "homepage": null, + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": null, + "homepage": null, + "documentation": null, + "vers": "0.0.1" + }"#, + "foo-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + ); +} + +#[cargo_test] +fn alt_registry_and_crates_io_deps() { + registry::alt_init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + crates_io_dep = "0.0.1" + + [dependencies.alt_reg_dep] + version = "0.1.0" + registry = "alternative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("crates_io_dep", "0.0.1").publish(); + Package::new("alt_reg_dep", "0.1.0") + .alternative(true) + .publish(); + + p.cargo("check") + .with_stderr_unordered( + "\ +[UPDATING] `alternative` index +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] crates_io_dep v0.0.1 (registry `dummy-registry`) +[DOWNLOADED] alt_reg_dep v0.1.0 (registry `alternative`) +[CHECKING] alt_reg_dep v0.1.0 (registry `alternative`) +[CHECKING] crates_io_dep v0.0.1 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn block_publish_due_to_no_token() { + registry::alt_init(); + let p = project().file("src/lib.rs", "").build(); + + fs::remove_file(paths::home().join(".cargo/credentials.toml")).unwrap(); + + // Now perform the actual publish + p.cargo("publish --registry alternative") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `alternative` index +error: no token found for `alternative`, please run `cargo login --registry alternative` +or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN", + ) + .run(); +} + +#[cargo_test] +fn publish_to_alt_registry() { + let _reg = RegistryBuilder::new() + .http_api() + .http_index() + .alternative() + .build(); + + let p = project().file("src/main.rs", "fn main() {}").build(); + + // Now perform the actual publish + p.cargo("publish --registry alternative") + .with_stderr( + "\ +[UPDATING] `alternative` index +[WARNING] [..] +[..] +[PACKAGING] foo v0.0.1 [..] +[VERIFYING] foo v0.0.1 [..] +[COMPILING] foo v0.0.1 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] foo v0.0.1 [..] +[UPDATING] `alternative` index +", + ) + .run(); +} + +#[cargo_test] +fn publish_with_crates_io_dep() { + // crates.io registry. + let _dummy_reg = registry::init(); + // Alternative registry. + let _alt_reg = RegistryBuilder::new() + .http_api() + .http_index() + .alternative() + .build(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = ["me"] + license = "MIT" + description = "foo" + + [dependencies.bar] + version = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").publish(); + + p.cargo("publish --registry alternative") + .with_stderr( + "\ +[UPDATING] `alternative` index +[WARNING] [..] +[..] +[PACKAGING] foo v0.0.1 [..] +[UPDATING] `dummy-registry` index +[VERIFYING] foo v0.0.1 [..] +[DOWNLOADING] [..] +[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) +[COMPILING] bar v0.0.1 +[COMPILING] foo v0.0.1 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] foo v0.0.1 [..] +[UPDATING] `alternative` index +", + ) + .run(); + + validate_alt_upload( + r#"{ + "authors": ["me"], + "badges": {}, + "categories": [], + "deps": [ + { + "default_features": true, + "features": [], + "kind": "normal", + "name": "bar", + "optional": false, + "registry": "https://github.com/rust-lang/crates.io-index", + "target": null, + "version_req": "^0.0.1" + } + ], + "description": "foo", + "documentation": null, + "features": {}, + "homepage": null, + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": null, + "homepage": null, + "documentation": null, + "vers": "0.0.1" + }"#, + "foo-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + ); +} + +#[cargo_test] +fn passwords_in_registries_index_url_forbidden() { + registry::alt_init(); + + let config = paths::home().join(".cargo/config"); + + fs::write( + config, + r#" + [registries.alternative] + index = "ssh://git:secret@foobar.com" + "#, + ) + .unwrap(); + + let p = project().file("src/main.rs", "fn main() {}").build(); + + p.cargo("publish --registry alternative") + .with_status(101) + .with_stderr( + "\ +error: invalid index URL for registry `alternative` defined in [..]/home/.cargo/config + +Caused by: + registry URLs may not contain passwords +", + ) + .run(); +} + +#[cargo_test] +fn patch_alt_reg() { + registry::alt_init(); + Package::new("bar", "0.1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = { version = "0.1.0", registry = "alternative" } + + [patch.alternative] + bar = { path = "bar" } + "#, + ) + .file( + "src/lib.rs", + " + extern crate bar; + pub fn f() { bar::bar(); } + ", + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `alternative` index +[CHECKING] bar v0.1.0 ([CWD]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn bad_registry_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "0.0.1" + registry = "bad name" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + invalid character ` ` in registry name: `bad name`, [..]", + ) + .run(); + + for cmd in &[ + "init", + "install foo", + "login", + "owner", + "publish", + "search", + "yank --version 0.0.1", + ] { + p.cargo(cmd) + .arg("--registry") + .arg("bad name") + .with_status(101) + .with_stderr("[ERROR] invalid character ` ` in registry name: `bad name`, [..]") + .run(); + } +} + +#[cargo_test] +fn no_api() { + let _registry = RegistryBuilder::new().alternative().no_api().build(); + Package::new("bar", "0.0.1").alternative(true).publish(); + + // First check that a dependency works. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies.bar] + version = "0.0.1" + registry = "alternative" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `alternative` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `alternative`) +[CHECKING] bar v0.0.1 (registry `alternative`) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + + // Check all of the API commands. + let err = "[ERROR] registry `alternative` does not support API commands"; + + p.cargo("login --registry alternative TOKEN") + .with_status(101) + .with_stderr_contains(&err) + .run(); + + p.cargo("publish --registry alternative") + .with_status(101) + .with_stderr_contains(&err) + .run(); + + p.cargo("search --registry alternative") + .with_status(101) + .with_stderr_contains(&err) + .run(); + + p.cargo("owner --registry alternative --list") + .with_status(101) + .with_stderr_contains(&err) + .run(); + + p.cargo("yank --registry alternative --version=0.0.1 bar") + .with_status(101) + .with_stderr_contains(&err) + .run(); + + p.cargo("yank --registry alternative --version=0.0.1 bar") + .with_stderr_contains(&err) + .with_status(101) + .run(); +} + +#[cargo_test] +fn alt_reg_metadata() { + // Check for "registry" entries in `cargo metadata` with alternative registries. + registry::alt_init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + altdep = { version = "0.0.1", registry = "alternative" } + iodep = { version = "0.0.1" } + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("bar", "0.0.1").publish(); + Package::new("altdep", "0.0.1") + .dep("bar", "0.0.1") + .alternative(true) + .publish(); + Package::new("altdep2", "0.0.1").alternative(true).publish(); + Package::new("iodep", "0.0.1") + .registry_dep("altdep2", "0.0.1") + .publish(); + + // The important thing to check here is the "registry" value in `deps`. + // They should be: + // foo -> altdep: alternative-registry + // foo -> iodep: null (because it is in crates.io) + // altdep -> bar: null (because it is in crates.io) + // iodep -> altdep2: alternative-registry + p.cargo("metadata --format-version=1 --no-deps") + .with_json( + r#" + { + "packages": [ + { + "name": "foo", + "version": "0.0.1", + "id": "foo 0.0.1 (path+file:[..]/foo)", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": [ + { + "name": "altdep", + "source": "registry+file:[..]/alternative-registry", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": "file:[..]/alternative-registry" + }, + { + "name": "iodep", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]/foo/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + } + ], + "workspace_members": [ + "foo 0.0.1 (path+file:[..]/foo)" + ], + "resolve": null, + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); + + // --no-deps uses a different code path, make sure both work. + p.cargo("metadata --format-version=1") + .with_json( + r#" + { + "packages": [ + { + "name": "altdep", + "version": "0.0.1", + "id": "altdep 0.0.1 (registry+file:[..]/alternative-registry)", + "license": null, + "license_file": null, + "description": null, + "source": "registry+file:[..]/alternative-registry", + "dependencies": [ + { + "name": "bar", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]/altdep-0.0.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + }, + { + "name": "altdep2", + "version": "0.0.1", + "id": "altdep2 0.0.1 (registry+file:[..]/alternative-registry)", + "license": null, + "license_file": null, + "description": null, + "source": "registry+file:[..]/alternative-registry", + "dependencies": [], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]/altdep2-0.0.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + }, + { + "name": "bar", + "version": "0.0.1", + "id": "bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": null, + "license_file": null, + "description": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]/bar-0.0.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + }, + { + "name": "foo", + "version": "0.0.1", + "id": "foo 0.0.1 (path+file:[..]/foo)", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": [ + { + "name": "altdep", + "source": "registry+file:[..]/alternative-registry", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": "file:[..]/alternative-registry" + }, + { + "name": "iodep", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]/foo/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + }, + { + "name": "iodep", + "version": "0.0.1", + "id": "iodep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": null, + "license_file": null, + "description": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "altdep2", + "source": "registry+file:[..]/alternative-registry", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": "file:[..]/alternative-registry" + } + ], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]/iodep-0.0.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + } + ], + "workspace_members": [ + "foo 0.0.1 (path+file:[..]/foo)" + ], + "resolve": "{...}", + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); +} + +#[cargo_test] +fn unknown_registry() { + // A known registry refers to an unknown registry. + // foo -> bar(crates.io) -> baz(alt) + registry::alt_init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("baz", "0.0.1").alternative(true).publish(); + Package::new("bar", "0.0.1") + .registry_dep("baz", "0.0.1") + .publish(); + + // Remove "alternative" from config. + let cfg_path = paths::home().join(".cargo/config"); + let mut config = fs::read_to_string(&cfg_path).unwrap(); + let start = config.find("[registries.alternative]").unwrap(); + config.insert(start, '#'); + let start_index = &config[start..].find("index =").unwrap(); + config.insert(start + start_index, '#'); + fs::write(&cfg_path, config).unwrap(); + + p.cargo("check").run(); + + // Important parts: + // foo -> bar registry = null + // bar -> baz registry = alternate + p.cargo("metadata --format-version=1") + .with_json( + r#" + { + "packages": [ + { + "name": "bar", + "version": "0.0.1", + "id": "bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": null, + "license_file": null, + "description": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "baz", + "source": "registry+file://[..]/alternative-registry", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": "file:[..]/alternative-registry" + } + ], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + }, + { + "name": "baz", + "version": "0.0.1", + "id": "baz 0.0.1 (registry+file://[..]/alternative-registry)", + "license": null, + "license_file": null, + "description": null, + "source": "registry+file://[..]/alternative-registry", + "dependencies": [], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + }, + { + "name": "foo", + "version": "0.0.1", + "id": "foo 0.0.1 (path+file://[..]/foo)", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": [ + { + "name": "bar", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]/foo/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + } + ], + "workspace_members": [ + "foo 0.0.1 (path+file://[..]/foo)" + ], + "resolve": "{...}", + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + } + "#, + ) + .run(); +} + +#[cargo_test] +fn registries_index_relative_url() { + registry::alt_init(); + let config = paths::root().join(".cargo/config"); + fs::create_dir_all(config.parent().unwrap()).unwrap(); + fs::write( + &config, + r#" + [registries.relative] + index = "file:alternative-registry" + "#, + ) + .unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "0.0.1" + registry = "relative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").alternative(true).publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `relative` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `relative`) +[CHECKING] bar v0.0.1 (registry `relative`) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn registries_index_relative_path_not_allowed() { + registry::alt_init(); + let config = paths::root().join(".cargo/config"); + fs::create_dir_all(config.parent().unwrap()).unwrap(); + fs::write( + &config, + r#" + [registries.relative] + index = "alternative-registry" + "#, + ) + .unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "0.0.1" + registry = "relative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").alternative(true).publish(); + + p.cargo("check") + .with_stderr(&format!( + "\ +error: failed to parse manifest at `{root}/foo/Cargo.toml` + +Caused by: + invalid index URL for registry `relative` defined in [..]/.cargo/config + +Caused by: + invalid url `alternative-registry`: relative URL without a base +", + root = paths::root().to_str().unwrap() + )) + .with_status(101) + .run(); +} + +#[cargo_test] +fn both_index_and_registry() { + let p = project().file("src/lib.rs", "").build(); + for cmd in &["publish", "owner", "search", "yank --version 1.0.0"] { + p.cargo(cmd) + .arg("--registry=foo") + .arg("--index=foo") + .with_status(101) + .with_stderr( + "[ERROR] both `--index` and `--registry` \ + should not be set at the same time", + ) + .run(); + } +} + +#[cargo_test] +fn both_index_and_default() { + let p = project().file("src/lib.rs", "").build(); + for cmd in &[ + "publish", + "owner", + "search", + "yank --version 1.0.0", + "install foo", + ] { + p.cargo(cmd) + .env("CARGO_REGISTRY_DEFAULT", "undefined") + .arg(format!("--index=index_url")) + .with_status(101) + .with_stderr("[ERROR] invalid url `index_url`: relative URL without a base") + .run(); + } +} + +#[cargo_test] +fn sparse_lockfile() { + let _registry = registry::RegistryBuilder::new() + .http_index() + .alternative() + .build(); + Package::new("foo", "0.1.0").alternative(true).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "a" + version = "0.5.0" + authors = [] + + [dependencies] + foo = { registry = 'alternative', version = '0.1.0'} + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile").run(); + assert_match_exact( + &p.read_lockfile(), + r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "a" +version = "0.5.0" +dependencies = [ + "foo", +] + +[[package]] +name = "foo" +version = "0.1.0" +source = "sparse+http://[..]/" +checksum = "f6a200a9339fef960979d94d5c99cbbfd899b6f5a396a55d9775089119050203""#, + ); +} + +#[cargo_test] +fn publish_with_transitive_dep() { + let _alt1 = RegistryBuilder::new() + .http_api() + .http_index() + .alternative_named("Alt-1") + .build(); + let _alt2 = RegistryBuilder::new() + .http_api() + .http_index() + .alternative_named("Alt-2") + .build(); + + let p1 = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + p1.cargo("publish --registry Alt-1").run(); + + let p2 = project() + .file( + "Cargo.toml", + r#" + [package] + name = "b" + version = "0.6.0" + publish = ["Alt-2"] + + [dependencies] + a = { version = "0.5.0", registry = "Alt-1" } + "#, + ) + .file("src/lib.rs", "") + .build(); + p2.cargo("publish").run(); +} diff --git a/tests/testsuite/artifact_dep.rs b/tests/testsuite/artifact_dep.rs new file mode 100644 index 0000000..358ca89 --- /dev/null +++ b/tests/testsuite/artifact_dep.rs @@ -0,0 +1,2898 @@ +//! Tests specific to artifact dependencies, designated using +//! the new `dep = { artifact = "bin", … }` syntax in manifests. + +use cargo_test_support::compare::match_exact; +use cargo_test_support::registry::{Package, RegistryBuilder}; +use cargo_test_support::{ + basic_bin_manifest, basic_manifest, cross_compile, project, publish, registry, rustc_host, + Project, +}; + +#[cargo_test] +fn check_with_invalid_artifact_dependency() { + // invalid name + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "unknown" } + "#, + ) + .file("src/lib.rs", "extern crate bar;") // this would fail but we don't get there, artifacts are no libs + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]/Cargo.toml` + +Caused by: + 'unknown' is not a valid artifact specifier +", + ) + .with_status(101) + .run(); + + fn run_cargo_with_and_without_bindeps_feature( + p: &Project, + cmd: &str, + assert: &dyn Fn(&mut cargo_test_support::Execs), + ) { + assert( + p.cargo(&format!("{} -Z bindeps", cmd)) + .masquerade_as_nightly_cargo(&["bindeps"]), + ); + assert(&mut p.cargo(cmd)); + } + + // lib specified without artifact + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = { path = "bar/", lib = true } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + run_cargo_with_and_without_bindeps_feature(&p, "check", &|cargo| { + cargo + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]/Cargo.toml` + +Caused by: + 'lib' specifier cannot be used without an 'artifact = …' value (bar) +", + ) + .with_status(101) + .run(); + }); + + // target specified without artifact + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = { path = "bar/", target = "target" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + run_cargo_with_and_without_bindeps_feature(&p, "check", &|cargo| { + cargo + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]/Cargo.toml` + +Caused by: + 'target' specifier cannot be used without an 'artifact = …' value (bar) +", + ) + .with_status(101) + .run(); + }) +} + +#[cargo_test] +fn check_with_invalid_target_triple() { + // invalid name + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin", target = "unknown-target-triple" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains( + r#"[..]Could not find specification for target "unknown-target-triple"[..]"#, + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn build_without_nightly_aborts_with_error() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin" } + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at [..] + +Caused by: + `artifact = …` requires `-Z bindeps` (bar) +", + ) + .run(); +} + +#[cargo_test] +fn disallow_artifact_and_no_artifact_dep_to_same_package_within_the_same_dep_category() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin" } + bar_stable = { path = "bar/", package = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr("\ +[WARNING] foo v0.0.0 ([CWD]) ignoring invalid dependency `bar_stable` which is missing a lib target +[ERROR] the crate `foo v0.0.0 ([CWD])` depends on crate `bar v0.5.0 ([CWD]/bar)` multiple times with different names", + ) + .run(); +} + +#[cargo_test] +fn features_are_unified_among_lib_and_bin_dep_of_same_target() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + resolver = "2" + + [dependencies.d1] + path = "d1" + features = ["d1f1"] + artifact = "bin" + lib = true + + [dependencies.d2] + path = "d2" + features = ["d2f2"] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + d1::f1(); + d1::f2(); + d2::f1(); + d2::f2(); + } + "#, + ) + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [features] + d1f1 = ["d2"] + + [dependencies.d2] + path = "../d2" + features = ["d2f1"] + optional = true + "#, + ) + .file( + "d1/src/main.rs", + r#"fn main() { + #[cfg(feature = "d1f1")] + d2::f1(); + + // Using f2 is only possible as features are unififed across the same target. + // Our own manifest would only enable f1, and f2 comes in because a parent crate + // enables the feature in its manifest. + #[cfg(feature = "d1f1")] + d2::f2(); + }"#, + ) + .file( + "d1/src/lib.rs", + r#" + #[cfg(feature = "d2")] + extern crate d2; + /// Importing f2 here shouldn't be possible as unless features are unified. + #[cfg(feature = "d1f1")] + pub use d2::{f1, f2}; + "#, + ) + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.1" + authors = [] + + [features] + d2f1 = [] + d2f2 = [] + "#, + ) + .file( + "d2/src/lib.rs", + r#" + #[cfg(feature = "d2f1")] pub fn f1() {} + #[cfg(feature = "d2f2")] pub fn f2() {} + "#, + ) + .build(); + + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] d2 v0.0.1 ([CWD]/d2) +[COMPILING] d1 v0.0.1 ([CWD]/d1) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn features_are_not_unified_among_lib_and_bin_dep_of_different_target() { + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + &r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + resolver = "2" + + [dependencies.d1] + path = "d1" + features = ["d1f1"] + artifact = "bin" + lib = true + target = "$TARGET" + + [dependencies.d2] + path = "d2" + features = ["d2f2"] + "# + .replace("$TARGET", target), + ) + .file( + "src/main.rs", + r#" + fn main() { + // the lib = true part always builds for our current target, unifying dependencies + d1::d2::f1(); + d1::d2::f2(); + d2::f1(); + d2::f2(); + } + "#, + ) + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [features] + d1f1 = ["d2"] + + [dependencies.d2] + path = "../d2" + features = ["d2f1"] + optional = true + "#, + ) + .file("d1/src/main.rs", r#"fn main() { + // f1 we set ourselves + d2::f1(); + // As 'main' is only compiled as part of the artifact dependency and since that is not unified + // if the target differs, trying to access f2 is a compile time error as the feature isn't enabled in our dependency tree. + d2::f2(); + }"#) + .file( + "d1/src/lib.rs", + r#" + #[cfg(feature = "d2")] + pub extern crate d2; + "#, + ) + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.1" + authors = [] + + [features] + d2f1 = [] + d2f2 = [] + "#, + ) + .file( + "d2/src/lib.rs", + r#" + #[cfg(feature = "d2f1")] pub fn f1() {} + #[cfg(feature = "d2f2")] pub fn f2() {} + "#, + ) + .build(); + + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr_contains( + "error[E0425]: cannot find function `f2` in crate `d2`\n --> d1/src/main.rs:6:17", + ) + .run(); +} + +#[cargo_test] +fn feature_resolution_works_for_cfg_target_specification() { + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + &r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + resolver = "2" + + [dependencies.d1] + path = "d1" + artifact = "bin" + target = "$TARGET" + "# + .replace("$TARGET", target), + ) + .file( + "src/main.rs", + r#" + fn main() { + let _b = include_bytes!(env!("CARGO_BIN_FILE_D1")); + } + "#, + ) + .file( + "d1/Cargo.toml", + &r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [target.'$TARGET'.dependencies] + d2 = { path = "../d2" } + "# + .replace("$TARGET", target), + ) + .file( + "d1/src/main.rs", + r#"fn main() { + d1::f(); + }"#, + ) + .file("d1/build.rs", r#"fn main() { }"#) + .file( + "d1/src/lib.rs", + &r#"pub fn f() { + #[cfg(target = "$TARGET")] + d2::f(); + } + "# + .replace("$TARGET", target), + ) + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.1" + authors = [] + "#, + ) + .file("d2/build.rs", r#"fn main() { }"#) + .file("d2/src/lib.rs", "pub fn f() {}") + .build(); + + p.cargo("test -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .run(); +} + +#[cargo_test] +fn build_script_with_bin_artifacts() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies] + bar = { path = "bar/", artifact = ["bin", "staticlib", "cdylib"] } + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", r#" + fn main() { + let baz: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_baz").expect("CARGO_BIN_FILE_BAR_baz").into(); + println!("{}", baz.display()); + assert!(&baz.is_file()); + + let lib: std::path::PathBuf = std::env::var("CARGO_STATICLIB_FILE_BAR_bar").expect("CARGO_STATICLIB_FILE_BAR_bar").into(); + println!("{}", lib.display()); + assert!(&lib.is_file()); + + let lib: std::path::PathBuf = std::env::var("CARGO_CDYLIB_FILE_BAR_bar").expect("CARGO_CDYLIB_FILE_BAR_bar").into(); + println!("{}", lib.display()); + assert!(&lib.is_file()); + + let dir: std::path::PathBuf = std::env::var("CARGO_BIN_DIR_BAR").expect("CARGO_BIN_DIR_BAR").into(); + println!("{}", dir.display()); + assert!(dir.is_dir()); + + let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); + println!("{}", bar.display()); + assert!(&bar.is_file()); + + let bar2: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_bar").expect("CARGO_BIN_FILE_BAR_bar").into(); + println!("{}", bar2.display()); + assert_eq!(bar, bar2); + } + "#) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [lib] + crate-type = ["staticlib", "cdylib"] + "#, + ) + // compilation target is native for build scripts unless overridden + .file("bar/src/bin/bar.rs", &format!(r#"fn main() {{ assert_eq!(std::env::var("TARGET").unwrap(), "{}"); }}"#, cross_compile::native())) + .file("bar/src/bin/baz.rs", "fn main() {}") + .file("bar/src/lib.rs", "") + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains("[COMPILING] foo [..]") + .with_stderr_contains("[COMPILING] bar v0.5.0 ([CWD]/bar)") + .with_stderr_contains("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); + + let build_script_output = build_script_output_string(&p, "foo"); + let msg = "we need the binary directory for this artifact along with all binary paths"; + if cfg!(target_env = "msvc") { + match_exact( + "[..]/artifact/bar-[..]/bin/baz.exe\n\ + [..]/artifact/bar-[..]/staticlib/bar-[..].lib\n\ + [..]/artifact/bar-[..]/cdylib/bar.dll\n\ + [..]/artifact/bar-[..]/bin\n\ + [..]/artifact/bar-[..]/bin/bar.exe\n\ + [..]/artifact/bar-[..]/bin/bar.exe", + &build_script_output, + msg, + "", + None, + ) + .unwrap(); + } else { + match_exact( + "[..]/artifact/bar-[..]/bin/baz-[..]\n\ + [..]/artifact/bar-[..]/staticlib/libbar-[..].a\n\ + [..]/artifact/bar-[..]/cdylib/[..]bar.[..]\n\ + [..]/artifact/bar-[..]/bin\n\ + [..]/artifact/bar-[..]/bin/bar-[..]\n\ + [..]/artifact/bar-[..]/bin/bar-[..]", + &build_script_output, + msg, + "", + None, + ) + .unwrap(); + } + + assert!( + !p.bin("bar").is_file(), + "artifacts are located in their own directory, exclusively, and won't be lifted up" + ); + assert!(!p.bin("baz").is_file(),); + assert_artifact_executable_output(&p, "debug", "bar", "bar"); +} + +#[cargo_test] +fn build_script_with_bin_artifact_and_lib_false() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies] + bar = { path = "bar/", artifact = "bin" } + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + bar::doit() + } + "#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() { bar::doit(); }") + .file( + "bar/src/lib.rs", + r#" + pub fn doit() { + panic!("sentinel"); + } + "#, + ) + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr_does_not_contain("[..]sentinel[..]") + .run(); +} + +#[cargo_test] +fn lib_with_bin_artifact_and_lib_false() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin" } + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn foo() { + bar::doit() + }"#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() { bar::doit(); }") + .file( + "bar/src/lib.rs", + r#" + pub fn doit() { + panic!("sentinel"); + } + "#, + ) + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr_does_not_contain("[..]sentinel[..]") + .run(); +} + +#[cargo_test] +fn build_script_with_selected_dashed_bin_artifact_and_lib_true() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies] + bar-baz = { path = "bar/", artifact = "bin:baz-suffix", lib = true } + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", r#" + fn main() { + bar_baz::print_env() + } + "#) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar-baz" + version = "0.5.0" + authors = [] + + [[bin]] + name = "bar" + + [[bin]] + name = "baz-suffix" + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", r#" + pub fn print_env() { + let dir: std::path::PathBuf = std::env::var("CARGO_BIN_DIR_BAR_BAZ").expect("CARGO_BIN_DIR_BAR_BAZ").into(); + let bin: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_BAZ_baz-suffix").expect("CARGO_BIN_FILE_BAR_BAZ_baz-suffix").into(); + println!("{}", dir.display()); + println!("{}", bin.display()); + assert!(dir.is_dir()); + assert!(&bin.is_file()); + assert!(std::env::var("CARGO_BIN_FILE_BAR_BAZ").is_err(), "CARGO_BIN_FILE_BAR_BAZ isn't set due to name mismatch"); + assert!(std::env::var("CARGO_BIN_FILE_BAR_BAZ_bar").is_err(), "CARGO_BIN_FILE_BAR_BAZ_bar isn't set as binary isn't selected"); + } + "#) + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] bar-baz v0.5.0 ([CWD]/bar) +[COMPILING] foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + + let build_script_output = build_script_output_string(&p, "foo"); + let msg = "we need the binary directory for this artifact and the binary itself"; + + if cfg!(target_env = "msvc") { + cargo_test_support::compare::match_exact( + &format!( + "[..]/artifact/bar-baz-[..]/bin\n\ + [..]/artifact/bar-baz-[..]/bin/baz_suffix{}", + std::env::consts::EXE_SUFFIX, + ), + &build_script_output, + msg, + "", + None, + ) + .unwrap(); + } else { + cargo_test_support::compare::match_exact( + "[..]/artifact/bar-baz-[..]/bin\n\ + [..]/artifact/bar-baz-[..]/bin/baz_suffix-[..]", + &build_script_output, + msg, + "", + None, + ) + .unwrap(); + } + + assert!( + !p.bin("bar").is_file(), + "artifacts are located in their own directory, exclusively, and won't be lifted up" + ); + assert_artifact_executable_output(&p, "debug", "bar", "baz_suffix"); +} + +#[cargo_test] +fn lib_with_selected_dashed_bin_artifact_and_lib_true() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar-baz = { path = "bar/", artifact = ["bin:baz-suffix", "staticlib", "cdylib"], lib = true } + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn foo() { + bar_baz::exists(); + + env!("CARGO_BIN_DIR_BAR_BAZ"); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_BAZ_baz-suffix")); + let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR_BAZ")); + let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR_BAZ_bar-baz")); + let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR_BAZ")); + let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR_BAZ_bar-baz")); + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar-baz" + version = "0.5.0" + authors = [] + + [lib] + crate-type = ["rlib", "staticlib", "cdylib"] + + [[bin]] + name = "bar" + + [[bin]] + name = "baz-suffix" + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "pub fn exists() {}") + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] bar-baz v0.5.0 ([CWD]/bar) +[COMPILING] foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + + assert!( + !p.bin("bar").is_file(), + "artifacts are located in their own directory, exclusively, and won't be lifted up" + ); + assert_artifact_executable_output(&p, "debug", "bar", "baz_suffix"); +} + +#[cargo_test] +fn allow_artifact_and_no_artifact_dep_to_same_package_within_different_dep_categories() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin" } + + [dev-dependencies] + bar = { path = "bar/", package = "bar" } + "#, + ) + .file( + "src/lib.rs", + r#" + #[cfg(test)] extern crate bar; + pub fn foo() { + env!("CARGO_BIN_DIR_BAR"); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + }"#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "") + .build(); + p.cargo("test -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains("[COMPILING] bar v0.5.0 ([CWD]/bar)") + .with_stderr_contains("[FINISHED] test [unoptimized + debuginfo] target(s) in [..]") + .run(); +} + +#[cargo_test] +fn normal_build_deps_are_picked_up_in_presence_of_an_artifact_build_dep_to_the_same_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar", artifact = "bin:bar" } + + [build-dependencies] + bar = { path = "bar" } + "#, + ) + .file("build.rs", "fn main() { bar::f(); }") + .file( + "src/lib.rs", + r#" + pub fn foo() { + env!("CARGO_BIN_DIR_BAR"); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + }"#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "pub fn f() {}") + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .run(); +} + +#[cargo_test] +fn disallow_using_example_binaries_as_artifacts() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin:one-example" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/examples/one-example.rs", "fn main() {}") + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr(r#"[ERROR] dependency `bar` in package `foo` requires a `bin:one-example` artifact to be present."#) + .run(); +} + +/// From RFC 3028 +/// +/// > You may also specify separate dependencies with different artifact values, as well as +/// dependencies on the same crate without artifact specified; for instance, you may have a +/// build dependency on the binary of a crate and a normal dependency on the Rust library of the same crate. +#[cargo_test] +fn allow_artifact_and_non_artifact_dependency_to_same_crate() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies] + bar = { path = "bar/", artifact = "bin" } + + [dependencies] + bar = { path = "bar/" } + "#, + ) + .file("src/lib.rs", r#" + pub fn foo() { + bar::doit(); + assert!(option_env!("CARGO_BIN_FILE_BAR").is_none()); + }"#) + .file( + "build.rs", + r#" + fn main() { + assert!(option_env!("CARGO_BIN_FILE_BAR").is_none(), "no environment variables at build time"); + std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR").expect("BAR present")).status().unwrap(); + }"#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "pub fn doit() {}") + .build(); + + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains("[COMPILING] bar [..]") + .with_stderr_contains("[COMPILING] foo [..]") + .run(); +} + +#[cargo_test] +fn build_script_deps_adopt_specified_target_unconditionally() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies.bar] + path = "bar/" + artifact = "bin" + target = "{}" + "#, + target + ), + ) + .file("src/lib.rs", "") + .file("build.rs", r#" + fn main() { + let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); + assert!(&bar.is_file()); + }"#) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "pub fn doit() {}") + .build(); + + p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_does_not_contain(format!( + "[RUNNING] `rustc --crate-name build_script_build build.rs [..]--target {} [..]", + target + )) + .with_stderr_contains("[RUNNING] `rustc --crate-name build_script_build build.rs [..]") + .with_stderr_contains(format!( + "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--target {} [..]", + target + )) + .with_stderr_contains(format!( + "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..]--target {} [..]", + target + )) + .with_stderr_does_not_contain(format!( + "[RUNNING] `rustc --crate-name foo [..]--target {} [..]", + target + )) + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]") + .run(); +} + +/// inverse RFC-3176 +#[cargo_test] +fn build_script_deps_adopt_do_not_allow_multiple_targets_under_different_name_and_same_version() { + if cross_compile::disabled() { + return; + } + + let alternate = cross_compile::alternate(); + let native = cross_compile::native(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies.bar] + path = "bar/" + artifact = "bin" + target = "{}" + + [build-dependencies.bar-native] + package = "bar" + path = "bar/" + artifact = "bin" + target = "{}" + "#, + alternate, + native + ), + ) + .file("src/lib.rs", "") + .file("build.rs", r#" + fn main() { + let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); + assert!(&bar.is_file()); + let bar_native: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_NATIVE_bar").expect("CARGO_BIN_FILE_BAR_NATIVE_bar").into(); + assert!(&bar_native.is_file()); + assert_ne!(bar_native, bar, "should build different binaries due to different targets"); + }"#) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr(format!( + "error: the crate `foo v0.0.0 ([CWD])` depends on crate `bar v0.5.0 ([CWD]/bar)` multiple times with different names", + )) + .run(); +} + +#[cargo_test] +fn non_build_script_deps_adopt_specified_target_unconditionally() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies.bar] + path = "bar/" + artifact = "bin" + target = "{}" + "#, + target + ), + ) + .file( + "src/lib.rs", + r#"pub fn foo() { let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); }"#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "pub fn doit() {}") + .build(); + + p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains(format!( + "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--target {} [..]", + target + )) + .with_stderr_contains(format!( + "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..]--target {} [..]", + target + )) + .with_stderr_does_not_contain(format!( + "[RUNNING] `rustc --crate-name foo [..]--target {} [..]", + target + )) + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]") + .run(); +} + +#[cargo_test] +fn no_cross_doctests_works_with_artifacts() { + if cross_compile::disabled() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin", lib = true } + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! env!("CARGO_BIN_DIR_BAR"); + //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + //! ``` + pub fn foo() { + env!("CARGO_BIN_DIR_BAR"); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + } + "#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/lib.rs", r#"pub extern "C" fn c() {}"#) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + let target = rustc_host(); + p.cargo("test -Z bindeps --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr(&format!( + "\ +[COMPILING] bar v0.5.0 ([CWD]/bar) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/{triple}/debug/deps/foo-[..][EXE]) +[DOCTEST] foo +", + triple = target + )) + .run(); + + println!("c"); + let target = cross_compile::alternate(); + + // This will build the library, but does not build or run doc tests. + // This should probably be a warning or error. + p.cargo("test -Z bindeps -v --doc --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains(format!( + "[COMPILING] bar v0.5.0 ([CWD]/bar) +[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--target {triple} [..] +[RUNNING] `rustc --crate-name bar bar/src/main.rs [..]--target {triple} [..] +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..]", + triple = target + )) + .run(); + + if !cross_compile::can_run_on_host() { + return; + } + + // This tests the library, but does not run the doc tests. + p.cargo("test -Z bindeps -v --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains(&format!( + "[FRESH] bar v0.5.0 ([CWD]/bar) +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..]--test[..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[CWD]/target/{triple}/debug/deps/foo-[..][EXE]`", + triple = target + )) + .run(); +} + +#[cargo_test] +fn build_script_deps_adopts_target_platform_if_target_equals_target() { + if cross_compile::disabled() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies] + bar = { path = "bar/", artifact = "bin", target = "target" } + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", r#" + fn main() { + let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); + assert!(&bar.is_file()); + }"#) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "pub fn doit() {}") + .build(); + + let alternate_target = cross_compile::alternate(); + p.cargo("check -v -Z bindeps --target") + .arg(alternate_target) + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_does_not_contain(format!( + "[RUNNING] `rustc --crate-name build_script_build build.rs [..]--target {} [..]", + alternate_target + )) + .with_stderr_contains("[RUNNING] `rustc --crate-name build_script_build build.rs [..]") + .with_stderr_contains(format!( + "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--target {} [..]", + alternate_target + )) + .with_stderr_contains(format!( + "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..]--target {} [..]", + alternate_target + )) + .with_stderr_contains(format!( + "[RUNNING] `rustc --crate-name foo [..]--target {} [..]", + alternate_target + )) + .run(); +} + +#[cargo_test] +// TODO(ST): rename bar (dependency) to something else and un-ignore this with RFC-3176 +#[cfg_attr(target_env = "msvc", ignore = "msvc not working")] +fn profile_override_basic() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [build-dependencies] + bar = { path = "bar", artifact = "bin" } + + [dependencies] + bar = { path = "bar", artifact = "bin" } + + [profile.dev.build-override] + opt-level = 1 + + [profile.dev] + opt-level = 3 + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("build -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name build_script_build [..] -C opt-level=1 [..]`", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..] -C opt-level=3 [..]`", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..] -C opt-level=1 [..]`", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..] -C opt-level=1 [..]`", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..] -C opt-level=3 [..]`", + ) + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..] -C opt-level=3 [..]`") + .run(); +} + +#[cargo_test] +fn dependencies_of_dependencies_work_in_artifacts() { + Package::new("baz", "1.0.0") + .file("src/lib.rs", "pub fn baz() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies] + bar = { path = "bar/", artifact = "bin" } + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR").expect("BAR present")).status().unwrap(); + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + baz = "1.0.0" + "#, + ) + .file("bar/src/lib.rs", r#"pub fn bar() {baz::baz()}"#) + .file("bar/src/main.rs", r#"fn main() {bar::bar()}"#) + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .run(); + + // cargo tree sees artifacts as the dependency kind they are in and doesn't do anything special with it. + p.cargo("tree -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stdout( + "\ +foo v0.0.0 ([CWD]) +[build-dependencies] +└── bar v0.5.0 ([CWD]/bar) + └── baz v1.0.0 +", + ) + .run(); +} + +// TODO: Fix this potentially by reverting 887562bfeb8c540594d7d08e6e9a4ab7eb255865 which adds artifact information to the registry +// followed by 0ff93733626f7cbecaf9dce9ab62b4ced0be088e which picks it up. +// For reference, see comments by ehuss https://github.com/rust-lang/cargo/pull/9992#discussion_r801086315 and +// joshtriplett https://github.com/rust-lang/cargo/pull/9992#issuecomment-1033394197 . +#[cargo_test] +#[ignore = "broken, need artifact info in index"] +fn targets_are_picked_up_from_non_workspace_artifact_deps() { + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + Package::new("artifact", "1.0.0") + .file("src/main.rs", r#"fn main() {}"#) + .file("src/lib.rs", r#"pub fn lib() {}"#) + .publish(); + + let mut dep = registry::Dependency::new("artifact", "1.0.0"); + Package::new("uses-artifact", "1.0.0") + .file( + "src/lib.rs", + r#"pub fn uses_artifact() { let _b = include_bytes!(env!("CARGO_BIN_FILE_ARTIFACT")); }"#, + ) + .add_dep(dep.artifact("bin", Some(target.to_string()))) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + uses-artifact = { version = "1.0.0" } + "#, + ) + .file( + "src/lib.rs", + r#"pub fn foo() { uses_artifact::uses_artifact(); }"#, + ) + .build(); + + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .run(); +} + +#[cargo_test] +fn allow_dep_renames_with_multiple_versions() { + Package::new("bar", "1.0.0") + .file("src/main.rs", r#"fn main() {println!("1.0.0")}"#) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies] + bar = { path = "bar/", artifact = "bin" } + bar_stable = { package = "bar", version = "1.0.0", artifact = "bin" } + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR").expect("BAR present")).status().unwrap(); + std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR_STABLE_bar").expect("BAR STABLE present")).status().unwrap(); + } + "#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", r#"fn main() {println!("0.5.0")}"#) + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains("[COMPILING] bar [..]") + .with_stderr_contains("[COMPILING] foo [..]") + .run(); + let build_script_output = build_script_output_string(&p, "foo"); + match_exact( + "0.5.0\n1.0.0", + &build_script_output, + "build script output", + "", + None, + ) + .unwrap(); +} + +#[cargo_test] +fn allow_artifact_and_non_artifact_dependency_to_same_crate_if_these_are_not_the_same_dep_kind() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies] + bar = { path = "bar/", artifact = "bin", lib = false } + + [dependencies] + bar = { path = "bar/" } + "#, + ) + .file("src/lib.rs", r#" + pub fn foo() { + bar::doit(); + assert!(option_env!("CARGO_BIN_FILE_BAR").is_none()); + }"#) + .file( + "build.rs", + r#"fn main() { + println!("{}", std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR")); + println!("{}", std::env::var("CARGO_BIN_FILE_BAR_bar").expect("CARGO_BIN_FILE_BAR_bar")); + }"#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn doit() {}") + .file("bar/src/main.rs", "fn main() {}") + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] bar [..] +[COMPILING] foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn prevent_no_lib_warning_with_artifact_dependencies() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin" } + "#, + ) + .file( + "src/lib.rs", + r#"pub fn foo() { let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); }"#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ + [COMPILING] bar v0.5.0 ([CWD]/bar)\n\ + [CHECKING] foo v0.0.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn show_no_lib_warning_with_artifact_dependencies_that_have_no_lib_but_lib_true() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies] + bar = { path = "bar/", artifact = "bin" } + + [dependencies] + bar = { path = "bar/", artifact = "bin", lib = true } + "#, + ) + .file("src/lib.rs", "") + .file("src/build.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains("[WARNING] foo v0.0.0 ([CWD]) ignoring invalid dependency `bar` which is missing a lib target") + .with_stderr_contains("[COMPILING] bar v0.5.0 ([CWD]/bar)") + .with_stderr_contains("[CHECKING] foo [..]") + .with_stderr_contains("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); +} + +#[cargo_test] +fn resolver_2_build_dep_without_lib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + edition = "2021" + + [build-dependencies] + bar = { path = "bar/", artifact = "bin" } + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", r#" + fn main() { + let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); + assert!(&bar.is_file()); + }"#) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .run(); +} + +#[cargo_test] +fn check_missing_crate_type_in_package_fails() { + for crate_type in &["cdylib", "staticlib", "bin"] { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = {{ path = "bar/", artifact = "{}" }} + "#, + crate_type + ), + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) //no bin, just rlib + .file("bar/src/lib.rs", "") + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr( + "[ERROR] dependency `bar` in package `foo` requires a `[..]` artifact to be present.", + ) + .run(); + } +} + +#[cargo_test] +fn check_target_equals_target_in_non_build_dependency_errors() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin", target = "target" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr_contains( + " `target = \"target\"` in normal- or dev-dependencies has no effect (bar)", + ) + .run(); +} + +#[cargo_test] +fn env_vars_and_build_products_for_various_build_targets() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [lib] + doctest = true + + [build-dependencies] + bar = { path = "bar/", artifact = ["cdylib", "staticlib"] } + + [dependencies] + bar = { path = "bar/", artifact = "bin", lib = true } + + [dev-dependencies] + bar = { path = "bar/", artifact = "bin:baz" } + "#, + ) + .file("build.rs", r#" + fn main() { + let file: std::path::PathBuf = std::env::var("CARGO_CDYLIB_FILE_BAR").expect("CARGO_CDYLIB_FILE_BAR").into(); + assert!(&file.is_file()); + + let file: std::path::PathBuf = std::env::var("CARGO_STATICLIB_FILE_BAR").expect("CARGO_STATICLIB_FILE_BAR").into(); + assert!(&file.is_file()); + + assert!(std::env::var("CARGO_BIN_FILE_BAR").is_err()); + assert!(std::env::var("CARGO_BIN_FILE_BAR_baz").is_err()); + } + "#) + .file( + "src/lib.rs", + r#" + //! ``` + //! bar::c(); + //! env!("CARGO_BIN_DIR_BAR"); + //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar")); + //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz")); + //! assert!(option_env!("CARGO_STATICLIB_FILE_BAR").is_none()); + //! assert!(option_env!("CARGO_CDYLIB_FILE_BAR").is_none()); + //! ``` + pub fn foo() { + bar::c(); + env!("CARGO_BIN_DIR_BAR"); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar")); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz")); + assert!(option_env!("CARGO_STATICLIB_FILE_BAR").is_none()); + assert!(option_env!("CARGO_CDYLIB_FILE_BAR").is_none()); + } + + #[cfg(test)] + #[test] + fn env_unit() { + env!("CARGO_BIN_DIR_BAR"); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar")); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz")); + assert!(option_env!("CARGO_STATICLIB_FILE_BAR").is_none()); + assert!(option_env!("CARGO_CDYLIB_FILE_BAR").is_none()); + } + "#, + ) + .file( + "tests/main.rs", + r#" + #[test] + fn env_integration() { + env!("CARGO_BIN_DIR_BAR"); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar")); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz")); + }"#, + ) + .file("build.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [lib] + crate-type = ["staticlib", "cdylib", "rlib"] + + [[bin]] + name = "bar" + + [[bin]] + name = "baz" + "#, + ) + .file("bar/src/lib.rs", r#"pub extern "C" fn c() {}"#) + .file("bar/src/main.rs", "fn main() {}") + .build(); + p.cargo("test -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] bar [..] +[COMPILING] foo [..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] unittests [..] +[RUNNING] tests/main.rs [..] +[DOCTEST] foo +", + ) + .run(); +} + +#[cargo_test] +fn publish_artifact_dep() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + Package::new("bar", "1.0.0").publish(); + Package::new("baz", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + resolver = "2" + + [dependencies] + bar = { version = "1.0", artifact = "bin", lib = true } + + [build-dependencies] + baz = { version = "1.0", artifact = ["bin:a", "cdylib", "staticlib"], target = "target" } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish -Z bindeps --no-verify") + .replace_crates_io(registry.index_url()) + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] +[UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [{ + "default_features": true, + "features": [], + "kind": "normal", + "name": "bar", + "optional": false, + "target": null, + "version_req": "^1.0" + }, + { + "default_features": true, + "features": [], + "kind": "build", + "name": "baz", + "optional": false, + "target": null, + "version_req": "^1.0" + } + ], + "description": "foo", + "documentation": "foo", + "features": {}, + "homepage": "foo", + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": "foo", + "vers": "0.1.0" + } + "#, + "foo-0.1.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[( + "Cargo.toml", + &format!( + r#"{} +[package] +name = "foo" +version = "0.1.0" +authors = [] +description = "foo" +homepage = "foo" +documentation = "foo" +license = "MIT" +repository = "foo" +resolver = "2" + +[dependencies.bar] +version = "1.0" +artifact = ["bin"] +lib = true + +[build-dependencies.baz] +version = "1.0" +artifact = [ + "bin:a", + "cdylib", + "staticlib", +] +target = "target""#, + cargo::core::package::MANIFEST_PREAMBLE + ), + )], + ); +} + +#[cargo_test] +fn doc_lib_true() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + resolver = "2" + + [dependencies.bar] + path = "bar" + artifact = "bin" + lib = true + "#, + ) + .file("src/lib.rs", "extern crate bar; pub fn foo() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("doc -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[DOCUMENTING] bar v0.0.1 ([CWD]/bar) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/foo/index.html").is_file()); + assert!(p.root().join("target/doc/bar/index.html").is_file()); + + // Verify that it emits rmeta for the bin and lib dependency. + assert_eq!(p.glob("target/debug/artifact/*.rlib").count(), 0); + assert_eq!(p.glob("target/debug/deps/libbar-*.rmeta").count(), 2); + + p.cargo("doc -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .env("CARGO_LOG", "cargo::ops::cargo_rustc::fingerprint") + .with_stdout("") + .run(); + + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/foo/index.html").is_file()); + assert!(p.root().join("target/doc/bar/index.html").is_file()); +} + +#[cargo_test] +fn rustdoc_works_on_libs_with_artifacts_and_lib_false() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + resolver = "2" + + [dependencies.bar] + path = "bar" + artifact = ["bin", "staticlib", "cdylib"] + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn foo() { + env!("CARGO_BIN_DIR_BAR"); + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR")); + let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR_bar")); + let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR")); + let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR_bar")); + }"#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [lib] + crate-type = ["staticlib", "cdylib"] + "#, + ) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("doc -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] bar v0.5.0 ([CWD]/bar) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/foo/index.html").is_file()); + assert!( + !p.root().join("target/doc/bar/index.html").is_file(), + "bar is not a lib dependency and thus remains undocumented" + ); +} + +fn assert_artifact_executable_output( + p: &Project, + target_name: &str, + dep_name: &str, + bin_name: &str, +) { + if cfg!(target_env = "msvc") { + assert_eq!( + p.glob(format!( + "target/{}/deps/artifact/{}-*/bin/{}{}", + target_name, + dep_name, + bin_name, + std::env::consts::EXE_SUFFIX + )) + .count(), + 1, + "artifacts are placed into their own output directory to not possibly clash" + ); + } else { + assert_eq!( + p.glob(format!( + "target/{}/deps/artifact/{}-*/bin/{}-*{}", + target_name, + dep_name, + bin_name, + std::env::consts::EXE_SUFFIX + )) + .filter_map(Result::ok) + .filter(|f| f.extension().map_or(true, |ext| ext != "o" && ext != "d")) + .count(), + 1, + "artifacts are placed into their own output directory to not possibly clash" + ); + } +} + +fn build_script_output_string(p: &Project, package_name: &str) -> String { + let paths = p + .glob(format!("target/debug/build/{}-*/output", package_name)) + .collect::, _>>() + .unwrap(); + assert_eq!(paths.len(), 1); + std::fs::read_to_string(&paths[0]).unwrap() +} + +#[cargo_test] +fn build_script_features_for_shared_dependency() { + // When a build script is built and run, its features should match. Here: + // + // foo + // -> artifact on d1 with target + // -> common with features f1 + // + // d1 + // -> common with features f2 + // + // common has features f1 and f2, with a build script. + // + // When common is built as a dependency of d1, it should have features + // `f2` (for the library and the build script). + // + // When common is built as a dependency of foo, it should have features + // `f1` (for the library and the build script). + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + &r#" + [package] + name = "foo" + version = "0.0.1" + resolver = "2" + + [dependencies] + d1 = { path = "d1", artifact = "bin", target = "$TARGET" } + common = { path = "common", features = ["f1"] } + "# + .replace("$TARGET", target), + ) + .file( + "src/main.rs", + r#" + fn main() { + let _b = include_bytes!(env!("CARGO_BIN_FILE_D1")); + common::f1(); + } + "#, + ) + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + + [dependencies] + common = { path = "../common", features = ["f2"] } + "#, + ) + .file( + "d1/src/main.rs", + r#"fn main() { + common::f2(); + }"#, + ) + .file( + "common/Cargo.toml", + r#" + [package] + name = "common" + version = "0.0.1" + + [features] + f1 = [] + f2 = [] + "#, + ) + .file( + "common/src/lib.rs", + r#" + #[cfg(feature = "f1")] + pub fn f1() {} + + #[cfg(feature = "f2")] + pub fn f2() {} + "#, + ) + .file( + "common/build.rs", + &r#" + use std::env::var_os; + fn main() { + assert_eq!(var_os("CARGO_FEATURE_F1").is_some(), cfg!(feature="f1")); + assert_eq!(var_os("CARGO_FEATURE_F2").is_some(), cfg!(feature="f2")); + if std::env::var("TARGET").unwrap() == "$TARGET" { + assert!(var_os("CARGO_FEATURE_F1").is_none()); + assert!(var_os("CARGO_FEATURE_F2").is_some()); + } else { + assert!(var_os("CARGO_FEATURE_F1").is_some()); + assert!(var_os("CARGO_FEATURE_F2").is_none()); + } + } + "# + .replace("$TARGET", target), + ) + .build(); + + p.cargo("build -Z bindeps -v") + .masquerade_as_nightly_cargo(&["bindeps"]) + .run(); +} + +#[cargo_test] +fn calc_bin_artifact_fingerprint() { + // See rust-lang/cargo#10527 + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin" } + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + } + "#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", r#"fn main() { println!("foo") }"#) + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] bar v0.5.0 ([CWD]/bar) +[CHECKING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.change_file("bar/src/main.rs", r#"fn main() { println!("bar") }"#); + // Change in artifact bin dep `bar` propagates to `foo`, triggering recompile. + p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[DIRTY] bar v0.5.0 ([CWD]/bar): the file `bar/src/main.rs` has changed ([..]) +[COMPILING] bar v0.5.0 ([CWD]/bar) +[RUNNING] `rustc --crate-name bar [..]` +[DIRTY] foo v0.1.0 ([CWD]): the dependency bar was rebuilt +[CHECKING] foo v0.1.0 ([CWD]) +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + // All units are fresh. No recompile. + p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[FRESH] bar v0.5.0 ([CWD]/bar) +[FRESH] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn with_target_and_optional() { + // See rust-lang/cargo#10526 + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + &r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + [dependencies] + d1 = { path = "d1", artifact = "bin", optional = true, target = "$TARGET" } + "# + .replace("$TARGET", target), + ) + .file( + "src/main.rs", + r#" + fn main() { + let _b = include_bytes!(env!("CARGO_BIN_FILE_D1")); + } + "#, + ) + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + edition = "2021" + "#, + ) + .file("d1/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -Z bindeps -F d1 -v") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] d1 v0.0.1 [..] +[RUNNING] `rustc --crate-name d1 [..]--crate-type bin[..] +[CHECKING] foo v0.0.1 [..] +[RUNNING] `rustc --crate-name foo [..]--cfg[..]d1[..] +[FINISHED] dev [..] +", + ) + .run(); +} + +#[cargo_test] +fn with_assumed_host_target_and_optional_build_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + [build-dependencies] + d1 = { path = "d1", artifact = "bin", optional = true, target = "target" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + fn main() { + std::env::var("CARGO_BIN_FILE_D1").unwrap(); + } + "#, + ) + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + edition = "2021" + "#, + ) + .file("d1/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -Z bindeps -F d1 -v") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_unordered( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[COMPILING] d1 v0.0.1 ([CWD]/d1) +[RUNNING] `rustc --crate-name build_script_build [..]--crate-type bin[..] +[RUNNING] `rustc --crate-name d1 [..]--crate-type bin[..] +[RUNNING] `[CWD]/target/debug/build/foo-[..]/build-script-build` +[RUNNING] `rustc --crate-name foo [..]--cfg[..]d1[..] +[FINISHED] dev [..] +", + ) + .run(); +} + +#[cargo_test] +fn decouple_same_target_transitive_dep_from_artifact_dep() { + // See https://github.com/rust-lang/cargo/issues/11463 + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + + [dependencies] + a = {{ path = "a" }} + bar = {{ path = "bar", artifact = "bin", target = "{target}" }} + "# + ), + ) + .file( + "src/main.rs", + r#" + fn main() {} + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + a = { path = "../a", features = ["feature"] } + "#, + ) + .file( + "bar/src/main.rs", + r#" + fn main() {} + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2021" + + [dependencies] + b = { path = "../b" } + c = { path = "../c" } + + [features] + feature = ["c/feature"] + "#, + ) + .file( + "a/src/lib.rs", + r#" + use b::Trait as _; + + pub fn use_b_trait(x: &impl c::Trait) { + x.b(); + } + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [dependencies] + c = { path = "../c" } + "#, + ) + .file( + "b/src/lib.rs", + r#" + pub trait Trait { + fn b(&self) {} + } + + impl Trait for T {} + "#, + ) + .file( + "c/Cargo.toml", + r#" + [package] + name = "c" + version = "0.1.0" + + [features] + feature = [] + "#, + ) + .file( + "c/src/lib.rs", + r#" + pub trait Trait {} + "#, + ) + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] c v0.1.0 ([CWD]/c) +[COMPILING] b v0.1.0 ([CWD]/b) +[COMPILING] a v0.1.0 ([CWD]/a) +[COMPILING] bar v0.1.0 ([CWD]/bar) +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn decouple_same_target_transitive_dep_from_artifact_dep_lib() { + // See https://github.com/rust-lang/cargo/issues/10837 + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + + [dependencies] + a = {{ path = "a" }} + b = {{ path = "b", features = ["feature"] }} + bar = {{ path = "bar", artifact = "bin", lib = true, target = "{target}" }} + "# + ), + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + edition = "2021" + + [dependencies] + a = { path = "../a", features = ["b"] } + b = { path = "../b" } + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "bar/src/main.rs", + r#" + use b::Trait; + + fn main() { + a::A.b() + } + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + b = { path = "../b", optional = true } + "#, + ) + .file( + "a/src/lib.rs", + r#" + pub struct A; + + #[cfg(feature = "b")] + impl b::Trait for A {} + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [features] + feature = [] + "#, + ) + .file( + "b/src/lib.rs", + r#" + pub trait Trait { + fn b(&self) {} + } + "#, + ) + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] b v0.1.0 ([CWD]/b) +[COMPILING] a v0.1.0 ([CWD]/a) +[COMPILING] bar v0.1.0 ([CWD]/bar) +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn decouple_same_target_transitive_dep_from_artifact_dep_and_proc_macro() { + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + + [dependencies] + c = {{ path = "c" }} + bar = {{ path = "bar", artifact = "bin", target = "{target}" }} + "# + ), + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + b = { path = "../b" } + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + edition = "2021" + + [dependencies] + a = { path = "../a" } + + [lib] + proc-macro = true + "#, + ) + .file("b/src/lib.rs", "") + .file( + "c/Cargo.toml", + r#" + [package] + name = "c" + version = "0.1.0" + edition = "2021" + + [dependencies] + d = { path = "../d", features = ["feature"] } + a = { path = "../a" } + + [lib] + proc-macro = true + "#, + ) + .file( + "c/src/lib.rs", + r#" + use a::Trait; + + fn _c() { + d::D.a() + } + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + d = { path = "../d" } + "#, + ) + .file( + "a/src/lib.rs", + r#" + pub trait Trait { + fn a(&self) {} + } + + impl Trait for d::D {} + "#, + ) + .file( + "d/Cargo.toml", + r#" + [package] + name = "d" + version = "0.1.0" + + [features] + feature = [] + "#, + ) + .file("d/src/lib.rs", "pub struct D;") + .build(); + + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_unordered( + "\ +[COMPILING] d v0.1.0 ([CWD]/d) +[COMPILING] a v0.1.0 ([CWD]/a) +[COMPILING] b v0.1.0 ([CWD]/b) +[COMPILING] c v0.1.0 ([CWD]/c) +[COMPILING] bar v0.1.0 ([CWD]/bar) +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn same_target_artifact_dep_sharing() { + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = {{ path = "a" }} + bar = {{ path = "bar", artifact = "bin", target = "{target}" }} + "# + ), + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + a = { path = "../a" } + "#, + ) + .file( + "bar/src/main.rs", + r#" + fn main() {} + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + "#, + ) + .file("a/src/lib.rs", "") + .build(); + p.cargo(&format!("build -Z bindeps --target {target}")) + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] a v0.1.0 ([CWD]/a) +[COMPILING] bar v0.1.0 ([CWD]/bar) +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn check_transitive_artifact_dependency_with_different_target() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + + [dependencies] + bar = { path = "bar/" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.0" + + [dependencies] + baz = { path = "baz/", artifact = "bin", target = "custom-target" } + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "bar/baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.0.0" + + [dependencies] + "#, + ) + .file("bar/baz/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains( + "error: could not find specification for target `custom-target`.\n \ + Dependency `baz v0.0.0 [..]` requires to build for target `custom-target`.", + ) + .with_status(101) + .run(); +} diff --git a/tests/testsuite/bad_config.rs b/tests/testsuite/bad_config.rs new file mode 100644 index 0000000..dabdc14 --- /dev/null +++ b/tests/testsuite/bad_config.rs @@ -0,0 +1,1488 @@ +//! Tests for some invalid .cargo/config files. + +use cargo_test_support::registry::{self, Package}; +use cargo_test_support::{basic_manifest, project, rustc_host}; + +#[cargo_test] +fn bad1() { + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [target] + nonexistent-target = "foo" + "#, + ) + .build(); + p.cargo("check -v --target=nonexistent-target") + .with_status(101) + .with_stderr( + "\ +[ERROR] expected table for configuration key `target.nonexistent-target`, \ +but found string in [..]/config +", + ) + .run(); +} + +#[cargo_test] +fn bad2() { + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [http] + proxy = 3.0 + "#, + ) + .build(); + p.cargo("publish -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] could not load Cargo configuration + +Caused by: + failed to load TOML configuration from `[..]config` + +Caused by: + failed to parse key `http` + +Caused by: + failed to parse key `proxy` + +Caused by: + found TOML configuration value of unknown type `float` +", + ) + .run(); +} + +#[cargo_test] +fn bad3() { + let registry = registry::init(); + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [http] + proxy = true + "#, + ) + .build(); + Package::new("foo", "1.0.0").publish(); + + p.cargo("publish -v") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + "\ +error: failed to update registry [..] + +Caused by: + error in [..]config: `http.proxy` expected a string, but found a boolean +", + ) + .run(); +} + +#[cargo_test] +fn bad4() { + let p = project() + .file( + ".cargo/config", + r#" + [cargo-new] + vcs = false + "#, + ) + .build(); + p.cargo("new -v foo") + .with_status(101) + .with_stderr( + "\ +[ERROR] Failed to create package `foo` at `[..]` + +Caused by: + error in [..]config: `cargo-new.vcs` expected a string, but found a boolean +", + ) + .run(); +} + +#[cargo_test] +fn bad6() { + let registry = registry::init(); + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [http] + user-agent = true + "#, + ) + .build(); + Package::new("foo", "1.0.0").publish(); + + p.cargo("publish -v") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + "\ +error: failed to update registry [..] + +Caused by: + error in [..]config: `http.user-agent` expected a string, but found a boolean +", + ) + .run(); +} + +#[cargo_test] +fn invalid_global_config() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + foo = "0.1.0" + "#, + ) + .file(".cargo/config", "4") + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] could not load Cargo configuration + +Caused by: + could not parse TOML configuration in `[..]` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 1, column 2 + | + 1 | 4 + | ^ + expected `.`, `=` +", + ) + .run(); +} + +#[cargo_test] +fn bad_cargo_lock() { + let p = project() + .file("Cargo.lock", "[[package]]\nfoo = 92") + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse lock file at: [..]Cargo.lock + +Caused by: + missing field `name` + in `package` +", + ) + .run(); +} + +#[cargo_test] +fn duplicate_packages_in_cargo_lock() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file( + "Cargo.lock", + r#" + [[package]] + name = "foo" + version = "0.0.1" + dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "bar" + version = "0.1.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + + [[package]] + name = "bar" + version = "0.1.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse lock file at: [..] + +Caused by: + package `bar` is specified twice in the lockfile +", + ) + .run(); +} + +#[cargo_test] +fn bad_source_in_cargo_lock() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file( + "Cargo.lock", + r#" + [[package]] + name = "foo" + version = "0.0.1" + dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "bar" + version = "0.1.0" + source = "You shall not parse" + "#, + ) + .build(); + + p.cargo("check --verbose") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse lock file at: [..] + +Caused by: + invalid source `You shall not parse` + in `package.source` +", + ) + .run(); +} + +#[cargo_test] +fn bad_dependency_in_lockfile() { + let p = project() + .file("src/lib.rs", "") + .file( + "Cargo.lock", + r#" + [[package]] + name = "foo" + version = "0.0.1" + dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + ] + "#, + ) + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn bad_git_dependency() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + foo = { git = "file:.." } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr( + "\ +[UPDATING] git repository `file:///` +[ERROR] failed to get `foo` as a dependency of package `foo v0.0.0 [..]` + +Caused by: + failed to load source for dependency `foo` + +Caused by: + Unable to update file:/// + +Caused by: + failed to clone into: [..] + +Caused by: + [..]'file:///' is not a valid local file URI[..] +", + ) + .run(); +} + +#[cargo_test] +fn bad_crate_type() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [lib] + crate-type = ["bad_type", "rlib"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_status(101) + .with_stderr_contains( + "error: failed to run `rustc` to learn about crate-type bad_type information", + ) + .run(); +} + +#[cargo_test] +fn malformed_override() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [target.x86_64-apple-darwin.freetype] + native = { + foo: "bar" + } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 8, column 27 + | + 8 | native = { + | ^ + invalid inline table + expected `}` +", + ) + .run(); +} + +#[cargo_test] +fn duplicate_binary_names() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "qqq" + version = "0.1.0" + authors = ["A "] + + [[bin]] + name = "e" + path = "a.rs" + + [[bin]] + name = "e" + path = "b.rs" + "#, + ) + .file("a.rs", r#"fn main() -> () {}"#) + .file("b.rs", r#"fn main() -> () {}"#) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + found duplicate binary name e, but all binary targets must have a unique name +", + ) + .run(); +} + +#[cargo_test] +fn duplicate_example_names() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "qqq" + version = "0.1.0" + authors = ["A "] + + [[example]] + name = "ex" + path = "examples/ex.rs" + + [[example]] + name = "ex" + path = "examples/ex2.rs" + "#, + ) + .file("examples/ex.rs", r#"fn main () -> () {}"#) + .file("examples/ex2.rs", r#"fn main () -> () {}"#) + .build(); + + p.cargo("check --example ex") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + found duplicate example name ex, but all example targets must have a unique name +", + ) + .run(); +} + +#[cargo_test] +fn duplicate_bench_names() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "qqq" + version = "0.1.0" + authors = ["A "] + + [[bench]] + name = "ex" + path = "benches/ex.rs" + + [[bench]] + name = "ex" + path = "benches/ex2.rs" + "#, + ) + .file("benches/ex.rs", r#"fn main () {}"#) + .file("benches/ex2.rs", r#"fn main () {}"#) + .build(); + + p.cargo("bench") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + found duplicate bench name ex, but all bench targets must have a unique name +", + ) + .run(); +} + +#[cargo_test] +fn duplicate_deps() { + let p = project() + .file("shim-bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("shim-bar/src/lib.rs", "pub fn a() {}") + .file("linux-bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("linux-bar/src/lib.rs", "pub fn a() {}") + .file( + "Cargo.toml", + r#" + [package] + name = "qqq" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "shim-bar" } + + [target.x86_64-unknown-linux-gnu.dependencies] + bar = { path = "linux-bar" } + "#, + ) + .file("src/main.rs", r#"fn main () {}"#) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + Dependency 'bar' has different source paths depending on the build target. Each dependency must \ +have a single canonical source path irrespective of build target. +", + ) + .run(); +} + +#[cargo_test] +fn duplicate_deps_diff_sources() { + let p = project() + .file("shim-bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("shim-bar/src/lib.rs", "pub fn a() {}") + .file("linux-bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("linux-bar/src/lib.rs", "pub fn a() {}") + .file( + "Cargo.toml", + r#" + [package] + name = "qqq" + version = "0.0.1" + authors = [] + + [target.i686-unknown-linux-gnu.dependencies] + bar = { path = "shim-bar" } + + [target.x86_64-unknown-linux-gnu.dependencies] + bar = { path = "linux-bar" } + "#, + ) + .file("src/main.rs", r#"fn main () {}"#) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + Dependency 'bar' has different source paths depending on the build target. Each dependency must \ +have a single canonical source path irrespective of build target. +", + ) + .run(); +} + +#[cargo_test] +fn unused_keys() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [target.foo] + bar = "3" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +warning: unused manifest key: target.foo.bar +[CHECKING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + bulid = "foo" + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .build(); + p.cargo("check") + .with_stderr( + "\ +warning: unused manifest key: package.bulid +[CHECKING] foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + let p = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + build = "foo" + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .build(); + p.cargo("check") + .with_stderr( + "\ +warning: unused manifest key: lib.build +[CHECKING] foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn unused_keys_in_virtual_manifest() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + bulid = "foo" + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + p.cargo("check --workspace") + .with_stderr( + "\ +[WARNING] [..]/foo/Cargo.toml: unused manifest key: workspace.bulid +[CHECKING] bar [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn empty_dependencies() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = {} + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").publish(); + + p.cargo("check") + .with_stderr_contains( + "\ +warning: dependency (bar) specified without providing a local path, Git repository, or version \ +to use. This will be considered an error in future versions +", + ) + .run(); +} + +#[cargo_test] +fn invalid_toml_historically_allowed_fails() { + let p = project() + .file(".cargo/config", "[bar] baz = 2") + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: could not load Cargo configuration + +Caused by: + could not parse TOML configuration in `[..]` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 1, column 7 + | + 1 | [bar] baz = 2 + | ^ + invalid table header + expected newline, `#` +", + ) + .run(); +} + +#[cargo_test] +fn ambiguous_git_reference() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies.bar] + git = "http://127.0.0.1" + branch = "master" + tag = "some-tag" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + dependency (bar) specification is ambiguous. Only one of `branch`, `tag` or `rev` is allowed. +", + ) + .run(); +} + +#[cargo_test] +fn fragment_in_git_url() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies.bar] + git = "http://127.0.0.1#foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr_contains( + "\ +[WARNING] URL fragment `#foo` in git URL is ignored for dependency (bar). \ +If you were trying to specify a specific git revision, \ +use `rev = \"foo\"` in the dependency declaration. +", + ) + .run(); +} + +#[cargo_test] +fn bad_source_config1() { + let p = project() + .file("src/lib.rs", "") + .file(".cargo/config", "[source.foo]") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr("error: no source location specified for `source.foo`, need [..]") + .run(); +} + +#[cargo_test] +fn bad_source_config2() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [source.crates-io] + registry = 'http://example.com' + replace-with = 'bar' + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to get `bar` as a dependency of package `foo v0.0.0 [..]` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update registry `crates-io` + +Caused by: + could not find a configured source with the name `bar` \ + when attempting to lookup `crates-io` (configuration in [..]) +", + ) + .run(); +} + +#[cargo_test] +fn bad_source_config3() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [source.crates-io] + registry = 'https://example.com' + replace-with = 'crates-io' + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to get `bar` as a dependency of package `foo v0.0.0 [..]` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update registry `crates-io` + +Caused by: + detected a cycle of `replace-with` sources, [..] +", + ) + .run(); +} + +#[cargo_test] +fn bad_source_config4() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [source.crates-io] + replace-with = 'bar' + + [source.bar] + registry = 'https://example.com' + replace-with = 'crates-io' + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to get `bar` as a dependency of package `foo v0.0.0 ([..])` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update registry `crates-io` + +Caused by: + detected a cycle of `replace-with` sources, the source `crates-io` is \ + eventually replaced with itself (configuration in [..]) +", + ) + .run(); +} + +#[cargo_test] +fn bad_source_config5() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [source.crates-io] + registry = 'https://example.com' + replace-with = 'bar' + + [source.bar] + registry = 'not a url' + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: configuration key `source.bar.registry` specified an invalid URL (in [..]) + +Caused by: + invalid url `not a url`: [..] +", + ) + .run(); +} + +#[cargo_test] +fn both_git_and_path_specified() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies.bar] + git = "http://127.0.0.1" + path = "bar" + "#, + ) + .file("src/lib.rs", "") + .build(); + + foo.cargo("check -v") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + dependency (bar) specification is ambiguous. Only one of `git` or `path` is allowed. +", + ) + .run(); +} + +#[cargo_test] +fn bad_source_config6() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [source.crates-io] + registry = 'https://example.com' + replace-with = ['not', 'a', 'string'] + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] error in [..]/foo/.cargo/config: could not load config key `source.crates-io.replace-with` + +Caused by: + error in [..]/foo/.cargo/config: `source.crates-io.replace-with` expected a string, but found a array +" + ) + .run(); +} + +#[cargo_test] +fn ignored_git_revision() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies.bar] + path = "bar" + branch = "spam" + "#, + ) + .file("src/lib.rs", "") + .build(); + + let err_msg = "\ +error: failed to parse manifest at `[..]` + +Caused by: + key `branch` is ignored for dependency (bar). +"; + foo.cargo("check -v") + .with_status(101) + .with_stderr(err_msg) + .run(); + + // #11540, check that [target] dependencies fail the same way. + foo.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + + [target.some-target.dependencies] + bar = { path = "bar", branch = "spam" } + "#, + ); + foo.cargo("check") + .with_status(101) + .with_stderr(err_msg) + .run(); +} + +#[cargo_test] +fn bad_source_config7() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [source.foo] + registry = 'https://example.com' + local-registry = 'file:///another/file' + "#, + ) + .build(); + + Package::new("bar", "0.1.0").publish(); + + p.cargo("check") + .with_status(101) + .with_stderr("error: more than one source location specified for `source.foo`") + .run(); +} + +#[cargo_test] +fn bad_source_config8() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [source.foo] + branch = "somebranch" + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "[ERROR] source definition `source.foo` specifies `branch`, \ + but that requires a `git` key to be specified (in [..]/foo/.cargo/config)", + ) + .run(); +} + +#[cargo_test] +fn bad_dependency() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + bar = 3 + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + invalid type: integer `3`, expected a version string like [..] + in `dependencies.bar` +", + ) + .run(); +} + +#[cargo_test] +fn bad_debuginfo() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [profile.dev] + debug = 'a' + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + expected a boolean or an integer + in `profile.dev.debug` +", + ) + .run(); +} + +#[cargo_test] +fn bad_opt_level() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = 3 + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + expected a boolean or a string + in `package.build` +", + ) + .run(); +} + +#[cargo_test] +fn warn_semver_metadata() { + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [dependencies] + bar = "1.0.0+1234" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check") + .with_stderr_contains("[WARNING] version requirement `1.0.0+1234` for dependency `bar`[..]") + .run(); +} + +#[cargo_test] +fn bad_target_cfg() { + // Invalid type in a StringList. + // + // The error message is a bit unfortunate here. The type here ends up + // being essentially Value>, and each layer of "Value" + // adds some context to the error message. Also, untagged enums provide + // strange error messages. Hopefully most users will be able to untangle + // the message. + let p = project() + .file( + ".cargo/config", + r#" + [target.'cfg(not(target_os = "none"))'] + runner = false + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] error in [..]/foo/.cargo/config: \ +could not load config key `target.\"cfg(not(target_os = \\\"none\\\"))\".runner` + +Caused by: + error in [..]/foo/.cargo/config: \ + could not load config key `target.\"cfg(not(target_os = \\\"none\\\"))\".runner` + +Caused by: + invalid configuration for key `target.\"cfg(not(target_os = \\\"none\\\"))\".runner` + expected a string or array of strings, but found a boolean for \ + `target.\"cfg(not(target_os = \\\"none\\\"))\".runner` in [..]/foo/.cargo/config +", + ) + .run(); +} + +#[cargo_test] +fn bad_target_links_overrides() { + // Invalid parsing of links overrides. + // + // This error message is terrible. Nothing in the deserialization path is + // using config::Value<>, so nothing is able to report the location. I + // think this illustrates how the way things break down with how it + // currently is designed with serde. + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [target.{}.somelib] + rustc-flags = 'foo' + "#, + rustc_host() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "[ERROR] Only `-l` and `-L` flags are allowed in target config \ + `target.[..].rustc-flags` (in [..]foo/.cargo/config): `foo`", + ) + .run(); + + p.change_file( + ".cargo/config", + &format!( + "[target.{}.somelib] + warning = \"foo\" + ", + rustc_host(), + ), + ); + p.cargo("check") + .with_status(101) + .with_stderr("[ERROR] `warning` is not supported in build script overrides") + .run(); +} + +#[cargo_test] +fn redefined_sources() { + // Cannot define a source multiple times. + let p = project() + .file( + ".cargo/config", + r#" + [source.foo] + registry = "https://github.com/rust-lang/crates.io-index" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] source `foo` defines source registry `crates-io`, \ + but that source is already defined by `crates-io` +note: Sources are not allowed to be defined multiple times. +", + ) + .run(); + + p.change_file( + ".cargo/config", + r#" + [source.one] + directory = "index" + + [source.two] + directory = "index" + "#, + ); + + // Name is `[..]` because we can't guarantee the order. + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] source `[..]` defines source dir [..]/foo/index, \ + but that source is already defined by `[..]` +note: Sources are not allowed to be defined multiple times. +", + ) + .run(); +} diff --git a/tests/testsuite/bad_manifest_path.rs b/tests/testsuite/bad_manifest_path.rs new file mode 100644 index 0000000..fb214e3 --- /dev/null +++ b/tests/testsuite/bad_manifest_path.rs @@ -0,0 +1,386 @@ +//! Tests for invalid --manifest-path arguments. + +use cargo_test_support::{basic_bin_manifest, main_file, project}; + +#[track_caller] +fn assert_not_a_cargo_toml(command: &str, manifest_path_argument: &str) { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo(command) + .arg("--manifest-path") + .arg(manifest_path_argument) + .cwd(p.root().parent().unwrap()) + .with_status(101) + .with_stderr( + "[ERROR] the manifest-path must be a path \ + to a Cargo.toml file", + ) + .run(); +} + +#[track_caller] +fn assert_cargo_toml_doesnt_exist(command: &str, manifest_path_argument: &str) { + let p = project().build(); + let expected_path = manifest_path_argument + .split('/') + .collect::>() + .join("[..]"); + + p.cargo(command) + .arg("--manifest-path") + .arg(manifest_path_argument) + .cwd(p.root().parent().unwrap()) + .with_status(101) + .with_stderr(format!( + "[ERROR] manifest path `{}` does not exist", + expected_path + )) + .run(); +} + +#[cargo_test] +fn bench_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("bench", "foo"); +} + +#[cargo_test] +fn bench_dir_plus_file() { + assert_not_a_cargo_toml("bench", "foo/bar"); +} + +#[cargo_test] +fn bench_dir_plus_path() { + assert_not_a_cargo_toml("bench", "foo/bar/baz"); +} + +#[cargo_test] +fn bench_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("bench", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn build_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("check", "foo"); +} + +#[cargo_test] +fn build_dir_plus_file() { + assert_not_a_cargo_toml("bench", "foo/bar"); +} + +#[cargo_test] +fn build_dir_plus_path() { + assert_not_a_cargo_toml("bench", "foo/bar/baz"); +} + +#[cargo_test] +fn build_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("check", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn clean_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("clean", "foo"); +} + +#[cargo_test] +fn clean_dir_plus_file() { + assert_not_a_cargo_toml("clean", "foo/bar"); +} + +#[cargo_test] +fn clean_dir_plus_path() { + assert_not_a_cargo_toml("clean", "foo/bar/baz"); +} + +#[cargo_test] +fn clean_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("clean", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn doc_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("doc", "foo"); +} + +#[cargo_test] +fn doc_dir_plus_file() { + assert_not_a_cargo_toml("doc", "foo/bar"); +} + +#[cargo_test] +fn doc_dir_plus_path() { + assert_not_a_cargo_toml("doc", "foo/bar/baz"); +} + +#[cargo_test] +fn doc_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("doc", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn fetch_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("fetch", "foo"); +} + +#[cargo_test] +fn fetch_dir_plus_file() { + assert_not_a_cargo_toml("fetch", "foo/bar"); +} + +#[cargo_test] +fn fetch_dir_plus_path() { + assert_not_a_cargo_toml("fetch", "foo/bar/baz"); +} + +#[cargo_test] +fn fetch_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("fetch", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn generate_lockfile_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("generate-lockfile", "foo"); +} + +#[cargo_test] +fn generate_lockfile_dir_plus_file() { + assert_not_a_cargo_toml("generate-lockfile", "foo/bar"); +} + +#[cargo_test] +fn generate_lockfile_dir_plus_path() { + assert_not_a_cargo_toml("generate-lockfile", "foo/bar/baz"); +} + +#[cargo_test] +fn generate_lockfile_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("generate-lockfile", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn package_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("package", "foo"); +} + +#[cargo_test] +fn package_dir_plus_file() { + assert_not_a_cargo_toml("package", "foo/bar"); +} + +#[cargo_test] +fn package_dir_plus_path() { + assert_not_a_cargo_toml("package", "foo/bar/baz"); +} + +#[cargo_test] +fn package_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("package", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn pkgid_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("pkgid", "foo"); +} + +#[cargo_test] +fn pkgid_dir_plus_file() { + assert_not_a_cargo_toml("pkgid", "foo/bar"); +} + +#[cargo_test] +fn pkgid_dir_plus_path() { + assert_not_a_cargo_toml("pkgid", "foo/bar/baz"); +} + +#[cargo_test] +fn pkgid_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("pkgid", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn publish_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("publish", "foo"); +} + +#[cargo_test] +fn publish_dir_plus_file() { + assert_not_a_cargo_toml("publish", "foo/bar"); +} + +#[cargo_test] +fn publish_dir_plus_path() { + assert_not_a_cargo_toml("publish", "foo/bar/baz"); +} + +#[cargo_test] +fn publish_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("publish", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn read_manifest_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("read-manifest", "foo"); +} + +#[cargo_test] +fn read_manifest_dir_plus_file() { + assert_not_a_cargo_toml("read-manifest", "foo/bar"); +} + +#[cargo_test] +fn read_manifest_dir_plus_path() { + assert_not_a_cargo_toml("read-manifest", "foo/bar/baz"); +} + +#[cargo_test] +fn read_manifest_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("read-manifest", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn run_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("run", "foo"); +} + +#[cargo_test] +fn run_dir_plus_file() { + assert_not_a_cargo_toml("run", "foo/bar"); +} + +#[cargo_test] +fn run_dir_plus_path() { + assert_not_a_cargo_toml("run", "foo/bar/baz"); +} + +#[cargo_test] +fn run_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("run", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn rustc_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("rustc", "foo"); +} + +#[cargo_test] +fn rustc_dir_plus_file() { + assert_not_a_cargo_toml("rustc", "foo/bar"); +} + +#[cargo_test] +fn rustc_dir_plus_path() { + assert_not_a_cargo_toml("rustc", "foo/bar/baz"); +} + +#[cargo_test] +fn rustc_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("rustc", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn test_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("test", "foo"); +} + +#[cargo_test] +fn test_dir_plus_file() { + assert_not_a_cargo_toml("test", "foo/bar"); +} + +#[cargo_test] +fn test_dir_plus_path() { + assert_not_a_cargo_toml("test", "foo/bar/baz"); +} + +#[cargo_test] +fn test_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("test", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn update_dir_containing_cargo_toml() { + assert_not_a_cargo_toml("update", "foo"); +} + +#[cargo_test] +fn update_dir_plus_file() { + assert_not_a_cargo_toml("update", "foo/bar"); +} + +#[cargo_test] +fn update_dir_plus_path() { + assert_not_a_cargo_toml("update", "foo/bar/baz"); +} + +#[cargo_test] +fn update_dir_to_nonexistent_cargo_toml() { + assert_cargo_toml_doesnt_exist("update", "foo/bar/baz/Cargo.toml"); +} + +#[cargo_test] +fn verify_project_dir_containing_cargo_toml() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("verify-project --manifest-path foo") + .cwd(p.root().parent().unwrap()) + .with_status(1) + .with_stdout( + "{\"invalid\":\"the manifest-path must be a path to a Cargo.toml file\"}\ + ", + ) + .run(); +} + +#[cargo_test] +fn verify_project_dir_plus_file() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("verify-project --manifest-path foo/bar") + .cwd(p.root().parent().unwrap()) + .with_status(1) + .with_stdout( + "{\"invalid\":\"the manifest-path must be a path to a Cargo.toml file\"}\ + ", + ) + .run(); +} + +#[cargo_test] +fn verify_project_dir_plus_path() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("verify-project --manifest-path foo/bar/baz") + .cwd(p.root().parent().unwrap()) + .with_status(1) + .with_stdout( + "{\"invalid\":\"the manifest-path must be a path to a Cargo.toml file\"}\ + ", + ) + .run(); +} + +#[cargo_test] +fn verify_project_dir_to_nonexistent_cargo_toml() { + let p = project().build(); + p.cargo("verify-project --manifest-path foo/bar/baz/Cargo.toml") + .cwd(p.root().parent().unwrap()) + .with_status(1) + .with_stdout( + "{\"invalid\":\"manifest path `foo[..]bar[..]baz[..]Cargo.toml` does not exist\"}\ + ", + ) + .run(); +} diff --git a/tests/testsuite/bench.rs b/tests/testsuite/bench.rs new file mode 100644 index 0000000..60ad2b6 --- /dev/null +++ b/tests/testsuite/bench.rs @@ -0,0 +1,1673 @@ +//! Tests for the `cargo bench` command. + +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, basic_manifest, project}; + +#[cargo_test(nightly, reason = "bench")] +fn cargo_bench_simple() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + + #[bench] + fn bench_hello(_b: &mut test::Bencher) { + assert_eq!(hello(), "hello") + } + "#, + ) + .build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("hello\n").run(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test bench_hello ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_bench_implicit() { + let p = project() + .file( + "src/main.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + #[bench] fn run1(_ben: &mut test::Bencher) { } + fn main() { println!("Hello main!"); } + "#, + ) + .file( + "tests/other.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run3(_ben: &mut test::Bencher) { } + "#, + ) + .file( + "benches/mybench.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run2(_ben: &mut test::Bencher) { } + "#, + ) + .build(); + + p.cargo("bench --benches") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE]) +[RUNNING] [..] (target/release/deps/mybench-[..][EXE]) +", + ) + .with_stdout_contains("test run2 ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_bin_implicit() { + let p = project() + .file( + "src/main.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + #[bench] fn run1(_ben: &mut test::Bencher) { } + fn main() { println!("Hello main!"); } + "#, + ) + .file( + "tests/other.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run3(_ben: &mut test::Bencher) { } + "#, + ) + .file( + "benches/mybench.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run2(_ben: &mut test::Bencher) { } + "#, + ) + .build(); + + p.cargo("bench --bins") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE]) +", + ) + .with_stdout_contains("test run1 ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_tarname() { + let p = project() + .file( + "benches/bin1.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run1(_ben: &mut test::Bencher) { } + "#, + ) + .file( + "benches/bin2.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run2(_ben: &mut test::Bencher) { } + "#, + ) + .build(); + + p.cargo("bench --bench bin2") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/bin2-[..][EXE]) +", + ) + .with_stdout_contains("test run2 ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_multiple_targets() { + let p = project() + .file( + "benches/bin1.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run1(_ben: &mut test::Bencher) { } + "#, + ) + .file( + "benches/bin2.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run2(_ben: &mut test::Bencher) { } + "#, + ) + .file( + "benches/bin3.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run3(_ben: &mut test::Bencher) { } + "#, + ) + .build(); + + p.cargo("bench --bench bin1 --bench bin2") + .with_stdout_contains("test run1 ... bench: [..]") + .with_stdout_contains("test run2 ... bench: [..]") + .with_stdout_does_not_contain("[..]run3[..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn cargo_bench_verbose() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + fn main() {} + #[bench] fn bench_hello(_b: &mut test::Bencher) {} + "#, + ) + .build(); + + p.cargo("bench -v hello") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc [..] src/main.rs [..]` +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] `[..]target/release/deps/foo-[..][EXE] hello --bench`", + ) + .with_stdout_contains("test bench_hello ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn many_similar_names() { + let p = project() + .file( + "src/lib.rs", + " + #![feature(test)] + #[cfg(test)] + extern crate test; + pub fn foo() {} + #[bench] fn lib_bench(_b: &mut test::Bencher) {} + ", + ) + .file( + "src/main.rs", + " + #![feature(test)] + #[cfg(test)] + extern crate foo; + #[cfg(test)] + extern crate test; + fn main() {} + #[bench] fn bin_bench(_b: &mut test::Bencher) { foo::foo() } + ", + ) + .file( + "benches/foo.rs", + r#" + #![feature(test)] + extern crate foo; + extern crate test; + #[bench] fn bench_bench(_b: &mut test::Bencher) { foo::foo() } + "#, + ) + .build(); + + p.cargo("bench") + .with_stdout_contains("test bin_bench ... bench: 0 ns/iter (+/- 0)") + .with_stdout_contains("test lib_bench ... bench: 0 ns/iter (+/- 0)") + .with_stdout_contains("test bench_bench ... bench: 0 ns/iter (+/- 0)") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn cargo_bench_failing_test() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + + #[bench] + fn bench_hello(_b: &mut test::Bencher) { + assert_eq!(hello(), "nope") + } + "#, + ) + .build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("hello\n").run(); + + // Force libtest into serial execution so that the test header will be printed. + p.cargo("bench -- --test-threads=1") + .with_stdout_contains("test bench_hello ...[..]") + .with_stderr_contains( + "\ +[COMPILING] foo v0.5.0 ([CWD])[..] +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains( + "[..]thread '[..]' panicked at 'assertion failed: `(left == right)`[..]", + ) + .with_stdout_contains("[..]left: `\"hello\"`[..]") + .with_stdout_contains("[..]right: `\"nope\"`[..]") + .with_stdout_contains("[..]src/main.rs:15[..]") + .with_status(101) + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_with_lib_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name = "baz" + path = "src/main.rs" + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + /// + /// ```rust + /// extern crate foo; + /// fn main() { + /// println!("{}", foo::foo()); + /// } + /// ``` + /// + pub fn foo(){} + #[bench] fn lib_bench(_b: &mut test::Bencher) {} + "#, + ) + .file( + "src/main.rs", + " + #![feature(test)] + #[allow(unused_extern_crates)] + extern crate foo; + #[cfg(test)] + extern crate test; + + fn main() {} + + #[bench] + fn bin_bench(_b: &mut test::Bencher) {} + ", + ) + .build(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE]) +[RUNNING] [..] (target/release/deps/baz-[..][EXE])", + ) + .with_stdout_contains("test lib_bench ... bench: [..]") + .with_stdout_contains("test bin_bench ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_with_deep_lib_dep() { + let p = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies.foo] + path = "../foo" + "#, + ) + .file( + "src/lib.rs", + " + #![feature(test)] + #[cfg(test)] + extern crate foo; + #[cfg(test)] + extern crate test; + #[bench] + fn bar_bench(_b: &mut test::Bencher) { + foo::foo(); + } + ", + ) + .build(); + let _p2 = project() + .file( + "src/lib.rs", + " + #![feature(test)] + #[cfg(test)] + extern crate test; + + pub fn foo() {} + + #[bench] + fn foo_bench(_b: &mut test::Bencher) {} + ", + ) + .build(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[COMPILING] bar v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/bar-[..][EXE])", + ) + .with_stdout_contains("test bar_bench ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn external_bench_explicit() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bench]] + name = "bench" + path = "src/bench.rs" + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + pub fn get_hello() -> &'static str { "Hello" } + + #[bench] + fn internal_bench(_b: &mut test::Bencher) {} + "#, + ) + .file( + "src/bench.rs", + r#" + #![feature(test)] + #[allow(unused_extern_crates)] + extern crate foo; + extern crate test; + + #[bench] + fn external_bench(_b: &mut test::Bencher) {} + "#, + ) + .build(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE]) +[RUNNING] [..] (target/release/deps/bench-[..][EXE])", + ) + .with_stdout_contains("test internal_bench ... bench: [..]") + .with_stdout_contains("test external_bench ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn external_bench_implicit() { + let p = project() + .file( + "src/lib.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + + pub fn get_hello() -> &'static str { "Hello" } + + #[bench] + fn internal_bench(_b: &mut test::Bencher) {} + "#, + ) + .file( + "benches/external.rs", + r#" + #![feature(test)] + #[allow(unused_extern_crates)] + extern crate foo; + extern crate test; + + #[bench] + fn external_bench(_b: &mut test::Bencher) {} + "#, + ) + .build(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE]) +[RUNNING] [..] (target/release/deps/external-[..][EXE])", + ) + .with_stdout_contains("test internal_bench ... bench: [..]") + .with_stdout_contains("test external_bench ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_autodiscover_2015() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + edition = "2015" + + [features] + magic = [] + + [[bench]] + name = "bench_magic" + required-features = ["magic"] + "#, + ) + .file("src/lib.rs", "") + .file( + "benches/bench_basic.rs", + r#" + #![feature(test)] + #[allow(unused_extern_crates)] + extern crate foo; + extern crate test; + + #[bench] + fn bench_basic(_b: &mut test::Bencher) {} + "#, + ) + .file( + "benches/bench_magic.rs", + r#" + #![feature(test)] + #[allow(unused_extern_crates)] + extern crate foo; + extern crate test; + + #[bench] + fn bench_magic(_b: &mut test::Bencher) {} + "#, + ) + .build(); + + p.cargo("bench bench_basic") + .with_stderr( + "warning: \ +An explicit [[bench]] section is specified in Cargo.toml which currently +disables Cargo from automatically inferring other benchmark targets. +This inference behavior will change in the Rust 2018 edition and the following +files will be included as a benchmark target: + +* [..]bench_basic.rs + +This is likely to break cargo build or cargo test as these files may not be +ready to be compiled as a benchmark target today. You can future-proof yourself +and disable this warning by adding `autobenches = false` to your [package] +section. You may also move the files to a location where Cargo would not +automatically infer them to be a target, such as in subfolders. + +For more information on this warning you can consult +https://github.com/rust-lang/cargo/issues/5330 +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE]) +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn dont_run_examples() { + let p = project() + .file("src/lib.rs", "") + .file( + "examples/dont-run-me-i-will-fail.rs", + r#"fn main() { panic!("Examples should not be run by 'cargo test'"); }"#, + ) + .build(); + p.cargo("bench").run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn pass_through_command_line() { + let p = project() + .file( + "src/lib.rs", + " + #![feature(test)] + #[cfg(test)] + extern crate test; + + #[bench] fn foo(_b: &mut test::Bencher) {} + #[bench] fn bar(_b: &mut test::Bencher) {} + ", + ) + .build(); + + p.cargo("bench bar") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test bar ... bench: [..]") + .run(); + + p.cargo("bench foo") + .with_stderr( + "[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test foo ... bench: [..]") + .run(); +} + +// Regression test for running cargo-bench twice with +// tests in an rlib +#[cargo_test(nightly, reason = "bench")] +fn cargo_bench_twice() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/foo.rs", + r#" + #![crate_type = "rlib"] + #![feature(test)] + #[cfg(test)] + extern crate test; + + #[bench] + fn dummy_bench(b: &mut test::Bencher) { } + "#, + ) + .build(); + + for _ in 0..2 { + p.cargo("bench").run(); + } +} + +#[cargo_test(nightly, reason = "bench")] +fn lib_bin_same_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo" + [[bin]] + name = "foo" + "#, + ) + .file( + "src/lib.rs", + " + #![feature(test)] + #[cfg(test)] + extern crate test; + #[bench] fn lib_bench(_b: &mut test::Bencher) {} + ", + ) + .file( + "src/main.rs", + " + #![feature(test)] + #[allow(unused_extern_crates)] + extern crate foo; + #[cfg(test)] + extern crate test; + + #[bench] + fn bin_bench(_b: &mut test::Bencher) {} + ", + ) + .build(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE]) +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains_n("test [..] ... bench: [..]", 2) + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn lib_with_standard_name() { + let p = project() + .file("Cargo.toml", &basic_manifest("syntax", "0.0.1")) + .file( + "src/lib.rs", + " + #![feature(test)] + #[cfg(test)] + extern crate test; + + /// ``` + /// syntax::foo(); + /// ``` + pub fn foo() {} + + #[bench] + fn foo_bench(_b: &mut test::Bencher) {} + ", + ) + .file( + "benches/bench.rs", + " + #![feature(test)] + extern crate syntax; + extern crate test; + + #[bench] + fn bench(_b: &mut test::Bencher) { syntax::foo() } + ", + ) + .build(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] syntax v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/syntax-[..][EXE]) +[RUNNING] [..] (target/release/deps/bench-[..][EXE])", + ) + .with_stdout_contains("test foo_bench ... bench: [..]") + .with_stdout_contains("test bench ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn lib_with_standard_name2() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "syntax" + version = "0.0.1" + authors = [] + + [lib] + name = "syntax" + bench = false + doctest = false + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file( + "src/main.rs", + " + #![feature(test)] + #[cfg(test)] + extern crate syntax; + #[cfg(test)] + extern crate test; + + fn main() {} + + #[bench] + fn bench(_b: &mut test::Bencher) { syntax::foo() } + ", + ) + .build(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] syntax v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/syntax-[..][EXE])", + ) + .with_stdout_contains("test bench ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_dylib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo" + crate_type = ["dylib"] + + [dependencies.bar] + path = "bar" + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(test)] + extern crate bar as the_bar; + #[cfg(test)] + extern crate test; + + pub fn bar() { the_bar::baz(); } + + #[bench] + fn foo(_b: &mut test::Bencher) {} + "#, + ) + .file( + "benches/bench.rs", + r#" + #![feature(test)] + extern crate foo as the_foo; + extern crate test; + + #[bench] + fn foo(_b: &mut test::Bencher) { the_foo::bar(); } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + name = "bar" + crate_type = ["dylib"] + "#, + ) + .file("bar/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("bench -v") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[RUNNING] [..] -C opt-level=3 [..] +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] [..] -C opt-level=3 [..] +[RUNNING] [..] -C opt-level=3 [..] +[RUNNING] [..] -C opt-level=3 [..] +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] `[..]target/release/deps/foo-[..][EXE] --bench` +[RUNNING] `[..]target/release/deps/bench-[..][EXE] --bench`", + ) + .with_stdout_contains_n("test foo ... bench: [..]", 2) + .run(); + + p.root().move_into_the_past(); + p.cargo("bench -v") + .with_stderr( + "\ +[FRESH] bar v0.0.1 ([CWD]/bar) +[FRESH] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] `[..]target/release/deps/foo-[..][EXE] --bench` +[RUNNING] `[..]target/release/deps/bench-[..][EXE] --bench`", + ) + .with_stdout_contains_n("test foo ... bench: [..]", 2) + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_twice_with_build_cmd() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("build.rs", "fn main() {}") + .file( + "src/lib.rs", + " + #![feature(test)] + #[cfg(test)] + extern crate test; + #[bench] + fn foo(_b: &mut test::Bencher) {} + ", + ) + .build(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test foo ... bench: [..]") + .run(); + + p.cargo("bench") + .with_stderr( + "[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test foo ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_with_examples() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "6.6.6" + authors = [] + + [[example]] + name = "teste1" + + [[bench]] + name = "testb1" + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + #[cfg(test)] + use test::Bencher; + + pub fn f1() { + println!("f1"); + } + + pub fn f2() {} + + #[bench] + fn bench_bench1(_b: &mut Bencher) { + f2(); + } + "#, + ) + .file( + "benches/testb1.rs", + " + #![feature(test)] + extern crate foo; + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_bench2(_b: &mut Bencher) { + foo::f2(); + } + ", + ) + .file( + "examples/teste1.rs", + r#" + extern crate foo; + + fn main() { + println!("example1"); + foo::f1(); + } + "#, + ) + .build(); + + p.cargo("bench -v") + .with_stderr( + "\ +[COMPILING] foo v6.6.6 ([CWD]) +[RUNNING] `rustc [..]` +[RUNNING] `rustc [..]` +[RUNNING] `rustc [..]` +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] `[CWD]/target/release/deps/foo-[..][EXE] --bench` +[RUNNING] `[CWD]/target/release/deps/testb1-[..][EXE] --bench`", + ) + .with_stdout_contains("test bench_bench1 ... bench: [..]") + .with_stdout_contains("test bench_bench2 ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn test_a_bench() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.1.0" + + [lib] + name = "foo" + test = false + doctest = false + + [[bench]] + name = "b" + test = true + "#, + ) + .file("src/lib.rs", "") + .file("benches/b.rs", "#[test] fn foo() {}") + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.1.0 ([..]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/b-[..][EXE])", + ) + .with_stdout_contains("test foo ... ok") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn test_bench_no_run() { + let p = project() + .file("src/lib.rs", "") + .file( + "benches/bbaz.rs", + r#" + #![feature(test)] + + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_baz(_: &mut Bencher) {} + "#, + ) + .build(); + + p.cargo("bench --no-run") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] bench [optimized] target(s) in [..] +[EXECUTABLE] benches src/lib.rs (target/release/deps/foo-[..][EXE]) +[EXECUTABLE] benches/bbaz.rs (target/release/deps/bbaz-[..][EXE]) +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn test_bench_no_run_emit_json() { + let p = project() + .file("src/lib.rs", "") + .file( + "benches/bbaz.rs", + r#" + #![feature(test)] + + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_baz(_: &mut Bencher) {} + "#, + ) + .build(); + + p.cargo("bench --no-run --message-format json") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] bench [optimized] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn test_bench_no_fail_fast() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + + #[bench] + fn bench_hello(_b: &mut test::Bencher) { + assert_eq!(hello(), "hello") + } + + #[bench] + fn bench_nope(_b: &mut test::Bencher) { + assert_eq!("nope", hello()) + } + "#, + ) + .file( + "benches/b1.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] + fn b1_fail(_b: &mut test::Bencher) { assert_eq!(1, 2); } + "#, + ) + .build(); + + p.cargo("bench --no-fail-fast -- --test-threads=1") + .with_status(101) + .with_stderr( + "\ +[COMPILING] foo v0.5.0 [..] +[FINISHED] bench [..] +[RUNNING] unittests src/main.rs (target/release/deps/foo[..]) +[ERROR] bench failed, to rerun pass `--bin foo` +[RUNNING] benches/b1.rs (target/release/deps/b1[..]) +[ERROR] bench failed, to rerun pass `--bench b1` +[ERROR] 2 targets failed: + `--bin foo` + `--bench b1` +", + ) + .with_stdout_contains("running 2 tests") + .with_stdout_contains("test bench_hello [..]") + .with_stdout_contains("test bench_nope [..]") + .with_stdout_contains("test b1_fail [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn test_bench_multiple_packages() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.1.0" + + [dependencies.bar] + path = "../bar" + + [dependencies.baz] + path = "../baz" + "#, + ) + .file("src/lib.rs", "") + .build(); + + let _bar = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + authors = [] + version = "0.1.0" + + [[bench]] + name = "bbar" + test = true + "#, + ) + .file("src/lib.rs", "") + .file( + "benches/bbar.rs", + r#" + #![feature(test)] + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_bar(_b: &mut Bencher) {} + "#, + ) + .build(); + + let _baz = project() + .at("baz") + .file( + "Cargo.toml", + r#" + [package] + name = "baz" + authors = [] + version = "0.1.0" + + [[bench]] + name = "bbaz" + test = true + "#, + ) + .file("src/lib.rs", "") + .file( + "benches/bbaz.rs", + r#" + #![feature(test)] + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_baz(_b: &mut Bencher) {} + "#, + ) + .build(); + + p.cargo("bench -p bar -p baz") + .with_stderr_contains("[RUNNING] [..] (target/release/deps/bbaz-[..][EXE])") + .with_stdout_contains("test bench_baz ... bench: [..]") + .with_stderr_contains("[RUNNING] [..] (target/release/deps/bbar-[..][EXE])") + .with_stdout_contains("test bench_bar ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_all_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "benches/foo.rs", + r#" + #![feature(test)] + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_foo(_: &mut Bencher) -> () { () } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file( + "bar/benches/bar.rs", + r#" + #![feature(test)] + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_bar(_: &mut Bencher) -> () { () } + "#, + ) + .build(); + + p.cargo("bench --workspace") + .with_stderr_contains("[RUNNING] [..] (target/release/deps/bar-[..][EXE])") + .with_stdout_contains("test bench_bar ... bench: [..]") + .with_stderr_contains("[RUNNING] [..] (target/release/deps/foo-[..][EXE])") + .with_stdout_contains("test bench_foo ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_all_exclude() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file( + "bar/src/lib.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + + #[bench] + pub fn bar(b: &mut test::Bencher) { + b.iter(|| {}); + } + "#, + ) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file( + "baz/src/lib.rs", + "#[test] pub fn baz() { break_the_build(); }", + ) + .build(); + + p.cargo("bench --workspace --exclude baz") + .with_stdout_contains( + "\ +running 1 test +test bar ... bench: [..] ns/iter (+/- [..])", + ) + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_all_exclude_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file( + "bar/src/lib.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + + #[bench] + pub fn bar(b: &mut test::Bencher) { + b.iter(|| {}); + } + "#, + ) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file( + "baz/src/lib.rs", + "#[test] pub fn baz() { break_the_build(); }", + ) + .build(); + + p.cargo("bench --workspace --exclude '*z'") + .with_stdout_contains( + "\ +running 1 test +test bar ... bench: [..] ns/iter (+/- [..])", + ) + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_all_virtual_manifest() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file( + "bar/benches/bar.rs", + r#" + #![feature(test)] + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_bar(_: &mut Bencher) -> () { () } + "#, + ) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .file( + "baz/benches/baz.rs", + r#" + #![feature(test)] + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_baz(_: &mut Bencher) -> () { () } + "#, + ) + .build(); + + // The order in which bar and baz are built is not guaranteed + p.cargo("bench --workspace") + .with_stderr_contains("[RUNNING] [..] (target/release/deps/baz-[..][EXE])") + .with_stdout_contains("test bench_baz ... bench: [..]") + .with_stderr_contains("[RUNNING] [..] (target/release/deps/bar-[..][EXE])") + .with_stdout_contains("test bench_bar ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_virtual_manifest_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") + .file( + "bar/benches/bar.rs", + r#" + #![feature(test)] + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_bar(_: &mut Bencher) -> () { break_the_build(); } + "#, + ) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .file( + "baz/benches/baz.rs", + r#" + #![feature(test)] + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_baz(_: &mut Bencher) -> () { () } + "#, + ) + .build(); + + // The order in which bar and baz are built is not guaranteed + p.cargo("bench -p '*z'") + .with_stderr_contains("[RUNNING] [..] (target/release/deps/baz-[..][EXE])") + .with_stdout_contains("test bench_baz ... bench: [..]") + .with_stderr_does_not_contain("[RUNNING] [..] (target/release/deps/bar-[..][EXE])") + .with_stdout_does_not_contain("test bench_bar ... bench: [..]") + .run(); +} + +// https://github.com/rust-lang/cargo/issues/4287 +#[cargo_test(nightly, reason = "bench")] +fn legacy_bench_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [[bench]] + name = "bench" + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file( + "src/bench.rs", + r#" + #![feature(test)] + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_foo(_: &mut Bencher) -> () { () } + "#, + ) + .build(); + + p.cargo("bench") + .with_stderr_contains( + "\ +[WARNING] path `[..]src/bench.rs` was erroneously implicitly accepted for benchmark `bench`, +please set bench.path in Cargo.toml", + ) + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_virtual_manifest_all_implied() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn foo() {}") + .file( + "bar/benches/bar.rs", + r#" + #![feature(test)] + extern crate test; + use test::Bencher; + #[bench] + fn bench_bar(_: &mut Bencher) -> () { () } + "#, + ) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .file( + "baz/benches/baz.rs", + r#" + #![feature(test)] + extern crate test; + use test::Bencher; + #[bench] + fn bench_baz(_: &mut Bencher) -> () { () } + "#, + ) + .build(); + + // The order in which bar and baz are built is not guaranteed + + p.cargo("bench") + .with_stderr_contains("[RUNNING] [..] (target/release/deps/baz-[..][EXE])") + .with_stdout_contains("test bench_baz ... bench: [..]") + .with_stderr_contains("[RUNNING] [..] (target/release/deps/bar-[..][EXE])") + .with_stdout_contains("test bench_bar ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn json_artifact_includes_executable_for_benchmark() { + let p = project() + .file( + "benches/benchmark.rs", + r#" + #![feature(test)] + extern crate test; + + use test::Bencher; + + #[bench] + fn bench_foo(_: &mut Bencher) -> () { () } + "#, + ) + .build(); + + p.cargo("bench --no-run --message-format=json") + .with_json( + r#" + { + "executable": "[..]/foo/target/release/deps/benchmark-[..][EXE]", + "features": [], + "filenames": "{...}", + "fresh": false, + "package_id": "foo 0.0.1 ([..])", + "manifest_path": "[..]", + "profile": "{...}", + "reason": "compiler-artifact", + "target": { + "crate_types": [ "bin" ], + "kind": [ "bench" ], + "doc": false, + "doctest": false, + "edition": "2015", + "name": "benchmark", + "src_path": "[..]/foo/benches/benchmark.rs", + "test": false + } + } + + {"reason": "build-finished", "success": true} + "#, + ) + .run(); +} diff --git a/tests/testsuite/binary_name.rs b/tests/testsuite/binary_name.rs new file mode 100644 index 0000000..7735d60 --- /dev/null +++ b/tests/testsuite/binary_name.rs @@ -0,0 +1,301 @@ +use cargo_test_support::install::{ + assert_has_installed_exe, assert_has_not_installed_exe, cargo_home, +}; +use cargo_test_support::project; + +#[cargo_test] +fn gated() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [[bin]] + name = "foo" + filename = "007bar" + path = "src/main.rs" + "#, + ) + .file("src/main.rs", "fn main() { assert!(true) }") + .build(); + + // Run cargo build. + p.cargo("build") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .with_status(101) + .with_stderr_contains("[..]feature `different-binary-name` is required") + .run(); +} + +#[cargo_test] +// This test checks if: +// 1. The correct binary is produced +// 2. The deps file has the correct content +// 3. Fingerprinting works +// 4. `cargo clean` command works +fn binary_name1() { + // Create the project. + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["different-binary-name"] + + [package] + name = "foo" + version = "0.0.1" + + [[bin]] + name = "foo" + filename = "007bar" + path = "src/main.rs" + "#, + ) + .file("src/main.rs", "fn main() { assert!(true) }") + .build(); + + // Run cargo build. + p.cargo("build") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .run(); + + // Check the name of the binary that cargo has generated. + // A binary with the name of the crate should NOT be created. + let foo_path = p.bin("foo"); + assert!(!foo_path.is_file()); + // A binary with the name provided in `filename` parameter should be created. + let bar_path = p.bin("007bar"); + assert!(bar_path.is_file()); + + // Check if deps file exists. + let deps_path = p.bin("007bar").with_extension("d"); + assert!(deps_path.is_file(), "{:?}", bar_path); + + let depinfo = p.read_file(deps_path.to_str().unwrap()); + + // Prepare what content we expect to be present in deps file. + let deps_exp = format!( + "{}: {}", + p.bin("007bar").to_str().unwrap(), + p.root().join("src").join("main.rs").to_str().unwrap() + ); + + // Compare actual deps content with expected deps content. + assert!( + depinfo.lines().any(|line| line == deps_exp), + "Content of `{}` is incorrect", + deps_path.to_string_lossy() + ); + + // Run cargo second time, to verify fingerprint. + p.cargo("build -p foo -v") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .with_stderr( + "\ +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); + + // Run cargo clean. + p.cargo("clean -p foo") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .run(); + + // Check if the appropriate file was removed. + assert!( + !bar_path.is_file(), + "`cargo clean` did not remove the correct files" + ); +} + +#[cargo_test] +// This test checks if: +// 1. Check `cargo run` +// 2. Check `cargo test` +// 3. Check `cargo install/uninstall` +fn binary_name2() { + // Create the project. + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["different-binary-name"] + + [package] + name = "foo" + version = "0.0.1" + + [[bin]] + name = "foo" + filename = "007bar" + "#, + ) + .file( + "src/main.rs", + r#" + fn hello(name: &str) -> String { + format!("Hello, {}!", name) + } + + fn main() { + println!("{}", hello("crabs")); + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn check_crabs() { + assert_eq!(hello("crabs"), "Hello, crabs!"); + } + } + "#, + ) + .build(); + + // Run cargo build. + p.cargo("build") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .run(); + + // Check the name of the binary that cargo has generated. + // A binary with the name of the crate should NOT be created. + let foo_path = p.bin("foo"); + assert!(!foo_path.is_file()); + // A binary with the name provided in `filename` parameter should be created. + let bar_path = p.bin("007bar"); + assert!(bar_path.is_file()); + + // Check if `cargo test` works + p.cargo("test") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test tests::check_crabs ... ok") + .run(); + + // Check if `cargo run` is able to execute the binary + p.cargo("run") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .with_stdout("Hello, crabs!") + .run(); + + p.cargo("install") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .run(); + + assert_has_installed_exe(cargo_home(), "007bar"); + + p.cargo("uninstall") + .with_stderr("[REMOVING] [ROOT]/home/.cargo/bin/007bar[EXE]") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .run(); + + assert_has_not_installed_exe(cargo_home(), "007bar"); +} + +#[cargo_test] +fn check_env_vars() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["different-binary-name"] + + [package] + name = "foo" + version = "0.0.1" + + [[bin]] + name = "foo" + filename = "007bar" + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + println!("{}", option_env!("CARGO_BIN_NAME").unwrap()); + } + "#, + ) + .file( + "tests/integration.rs", + r#" + #[test] + fn check_env_vars2() { + let value = option_env!("CARGO_BIN_EXE_007bar").expect("Could not find environment variable."); + assert!(value.contains("007bar")); + } + "# + ) + .build(); + + // Run cargo build. + p.cargo("build") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .run(); + p.cargo("run") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .with_stdout("007bar") + .run(); + p.cargo("test") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .with_status(0) + .run(); +} + +#[cargo_test] +fn check_msg_format_json() { + // Create the project. + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["different-binary-name"] + + [package] + name = "foo" + version = "0.0.1" + + [[bin]] + name = "foo" + filename = "007bar" + path = "src/main.rs" + "#, + ) + .file("src/main.rs", "fn main() { assert!(true) }") + .build(); + + let output = r#" +{ + "reason": "compiler-artifact", + "package_id": "foo 0.0.1 [..]", + "manifest_path": "[CWD]/Cargo.toml", + "target": "{...}", + "profile": "{...}", + "features": [], + "filenames": "{...}", + "executable": "[ROOT]/foo/target/debug/007bar[EXE]", + "fresh": false +} + +{"reason":"build-finished", "success":true} +"#; + + // Run cargo build. + p.cargo("build --message-format=json") + .masquerade_as_nightly_cargo(&["different-binary-name"]) + .with_json(output) + .run(); +} diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs new file mode 100644 index 0000000..56130c6 --- /dev/null +++ b/tests/testsuite/build.rs @@ -0,0 +1,6404 @@ +//! Tests for the `cargo build` command. + +use cargo::{ + core::compiler::CompileMode, + core::{Shell, Workspace}, + ops::CompileOptions, + Config, +}; +use cargo_test_support::compare; +use cargo_test_support::paths::{root, CargoPathExt}; +use cargo_test_support::registry::Package; +use cargo_test_support::tools; +use cargo_test_support::{ + basic_bin_manifest, basic_lib_manifest, basic_manifest, cargo_exe, git, is_nightly, main_file, + paths, process, project, rustc_host, sleep_ms, symlink_supported, t, Execs, ProjectBuilder, +}; +use cargo_util::paths::dylib_path_envvar; +use std::env; +use std::fs; +use std::io::Read; +use std::process::Stdio; + +#[cargo_test] +fn cargo_compile_simple() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("i am foo\n").run(); +} + +#[cargo_test] +fn cargo_fail_with_no_stderr() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &String::from("refusal")) + .build(); + p.cargo("build --message-format=json") + .with_status(101) + .with_stderr_does_not_contain("--- stderr") + .run(); +} + +/// Checks that the `CARGO_INCREMENTAL` environment variable results in +/// `rustc` getting `-C incremental` passed to it. +#[cargo_test] +fn cargo_compile_incremental() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build -v") + .env("CARGO_INCREMENTAL", "1") + .with_stderr_contains( + "[RUNNING] `rustc [..] -C incremental=[..]/target/debug/incremental[..]`\n", + ) + .run(); + + p.cargo("test -v") + .env("CARGO_INCREMENTAL", "1") + .with_stderr_contains( + "[RUNNING] `rustc [..] -C incremental=[..]/target/debug/incremental[..]`\n", + ) + .run(); +} + +#[cargo_test] +fn incremental_profile() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [profile.dev] + incremental = false + + [profile.release] + incremental = true + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .env_remove("CARGO_INCREMENTAL") + .with_stderr_does_not_contain("[..]C incremental=[..]") + .run(); + + p.cargo("build -v") + .env("CARGO_INCREMENTAL", "1") + .with_stderr_contains("[..]C incremental=[..]") + .run(); + + p.cargo("build --release -v") + .env_remove("CARGO_INCREMENTAL") + .with_stderr_contains("[..]C incremental=[..]") + .run(); + + p.cargo("build --release -v") + .env("CARGO_INCREMENTAL", "0") + .with_stderr_does_not_contain("[..]C incremental=[..]") + .run(); +} + +#[cargo_test] +fn incremental_config() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [build] + incremental = false + "#, + ) + .build(); + + p.cargo("build -v") + .env_remove("CARGO_INCREMENTAL") + .with_stderr_does_not_contain("[..]C incremental=[..]") + .run(); + + p.cargo("build -v") + .env("CARGO_INCREMENTAL", "1") + .with_stderr_contains("[..]C incremental=[..]") + .run(); +} + +#[cargo_test] +fn cargo_compile_with_workspace_excluded() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + p.cargo("build --workspace --exclude foo") + .with_stderr_does_not_contain("[..]virtual[..]") + .with_stderr_contains("[..]no packages to compile") + .with_status(101) + .run(); +} + +#[cargo_test] +fn cargo_compile_manifest_path() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build --manifest-path foo/Cargo.toml") + .cwd(p.root().parent().unwrap()) + .run(); + assert!(p.bin("foo").is_file()); +} + +#[cargo_test] +fn chdir_gated() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .build(); + p.cargo("-C foo build") + .cwd(p.root().parent().unwrap()) + .with_stderr( + "error: the `-C` flag is unstable, \ + pass `-Z unstable-options` on the nightly channel to enable it", + ) + .with_status(101) + .run(); + // No masquerade should also fail. + p.cargo("-C foo -Z unstable-options build") + .cwd(p.root().parent().unwrap()) + .with_stderr( + "error: the `-C` flag is unstable, \ + pass `-Z unstable-options` on the nightly channel to enable it", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn cargo_compile_directory_not_cwd() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .file(".cargo/config.toml", &"") + .build(); + + p.cargo("-Zunstable-options -C foo build") + .masquerade_as_nightly_cargo(&["chdir"]) + .cwd(p.root().parent().unwrap()) + .run(); + assert!(p.bin("foo").is_file()); +} + +#[cargo_test] +fn cargo_compile_directory_not_cwd_with_invalid_config() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .file(".cargo/config.toml", &"!") + .build(); + + p.cargo("-Zunstable-options -C foo build") + .masquerade_as_nightly_cargo(&["chdir"]) + .cwd(p.root().parent().unwrap()) + .with_status(101) + .with_stderr_contains( + "\ +Caused by: + TOML parse error at line 1, column 1 + | + 1 | ! + | ^ + invalid key +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_invalid_manifest() { + let p = project().file("Cargo.toml", "").build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + virtual manifests must be configured with [workspace] +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_invalid_manifest2() { + let p = project() + .file( + "Cargo.toml", + " + [package] + foo = bar + ", + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 3, column 23 + | + 3 | foo = bar + | ^ + invalid string + expected `\"`, `'` +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_invalid_manifest3() { + let p = project().file("src/Cargo.toml", "a = bar").build(); + + p.cargo("build --manifest-path src/Cargo.toml") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 1, column 5 + | + 1 | a = bar + | ^ + invalid string + expected `\"`, `'` +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_duplicate_build_targets() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "main" + path = "src/main.rs" + crate-type = ["dylib"] + + [dependencies] + "#, + ) + .file("src/main.rs", "#![allow(warnings)] fn main() {}") + .build(); + + p.cargo("build") + .with_stderr( + "\ +warning: file `[..]main.rs` found to be present in multiple build targets: + * `lib` target `main` + * `bin` target `foo` +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_invalid_version() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0")) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + unexpected end of input while parsing minor version number + in `package.version` +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_empty_package_name() { + let p = project() + .file("Cargo.toml", &basic_manifest("", "0.0.0")) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + package name cannot be an empty string +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_invalid_package_name() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo::bar", "0.0.0")) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + invalid character `:` in package name: `foo::bar`, [..] +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_invalid_bin_target_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [[bin]] + name = "" + "#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + binary target names cannot be empty +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_forbidden_bin_target_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [[bin]] + name = "build" + "#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + the binary target name `build` is forbidden, it conflicts with with cargo's build directory names +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_bin_and_crate_type() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [[bin]] + name = "the_foo_bin" + path = "src/foo.rs" + crate-type = ["cdylib", "rlib"] + "#, + ) + .file("src/foo.rs", "fn main() {}") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + the target `the_foo_bin` is a binary and can't have any crate-types set \ +(currently \"cdylib, rlib\")", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_api_exposes_artifact_paths() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [[bin]] + name = "the_foo_bin" + path = "src/bin.rs" + + [lib] + name = "the_foo_lib" + path = "src/foo.rs" + crate-type = ["cdylib", "rlib"] + "#, + ) + .file("src/foo.rs", "pub fn bar() {}") + .file("src/bin.rs", "pub fn main() {}") + .build(); + + let shell = Shell::from_write(Box::new(Vec::new())); + let config = Config::new(shell, env::current_dir().unwrap(), paths::home()); + let ws = Workspace::new(&p.root().join("Cargo.toml"), &config).unwrap(); + let compile_options = CompileOptions::new(ws.config(), CompileMode::Build).unwrap(); + + let result = cargo::ops::compile(&ws, &compile_options).unwrap(); + + assert_eq!(1, result.binaries.len()); + assert!(result.binaries[0].path.exists()); + assert!(result.binaries[0] + .path + .to_str() + .unwrap() + .contains("the_foo_bin")); + + assert_eq!(1, result.cdylibs.len()); + // The exact library path varies by platform, but should certainly exist at least + assert!(result.cdylibs[0].path.exists()); + assert!(result.cdylibs[0] + .path + .to_str() + .unwrap() + .contains("the_foo_lib")); +} + +#[cargo_test] +fn cargo_compile_with_bin_and_proc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [[bin]] + name = "the_foo_bin" + path = "src/foo.rs" + proc-macro = true + "#, + ) + .file("src/foo.rs", "fn main() {}") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + the target `the_foo_bin` is a binary and can't have `proc-macro` set `true`", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_invalid_lib_target_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [lib] + name = "" + "#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + library target names cannot be empty +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_invalid_non_numeric_dep_version() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + crossbeam = "y" + "#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + failed to parse the version requirement `y` for dependency `crossbeam` + +Caused by: + unexpected character 'y' while parsing major version number +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_without_manifest() { + let p = project().no_manifest().build(); + + p.cargo("build") + .with_status(101) + .with_stderr("[ERROR] could not find `Cargo.toml` in `[..]` or any parent directory") + .run(); +} + +#[cargo_test] +#[cfg(target_os = "linux")] +fn cargo_compile_with_lowercase_cargo_toml() { + let p = project() + .no_manifest() + .file("cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/lib.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "[ERROR] could not find `Cargo.toml` in `[..]` or any parent directory, \ + but found cargo.toml please try to rename it to Cargo.toml", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_invalid_code() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", "invalid rust code!") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr_contains("[ERROR] could not compile `foo` due to previous error\n") + .run(); + assert!(p.root().join("Cargo.lock").is_file()); +} + +#[cargo_test] +fn cargo_compile_with_invalid_code_in_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + [dependencies.baz] + path = "../baz" + "#, + ) + .file("src/main.rs", "invalid rust code!") + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "invalid rust code!") + .build(); + let _baz = project() + .at("baz") + .file("Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("src/lib.rs", "invalid rust code!") + .build(); + p.cargo("build") + .with_status(101) + .with_stderr_contains("[..]invalid rust code[..]") + .with_stderr_contains("[ERROR] could not compile [..]") + .run(); +} + +#[cargo_test] +fn cargo_compile_with_warnings_in_the_root_package() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", "fn main() {} fn dead() {}") + .build(); + + p.cargo("build") + .with_stderr_contains("[WARNING] [..]dead[..]") + .run(); +} + +#[cargo_test] +fn cargo_compile_with_warnings_in_a_dep_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + + [[bin]] + + name = "foo" + "#, + ) + .file("src/foo.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file( + "bar/src/bar.rs", + r#" + pub fn gimme() -> &'static str { + "test passed" + } + + fn dead() {} + "#, + ) + .build(); + + p.cargo("build") + .with_stderr_contains("[WARNING] [..]dead[..]") + .run(); + + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("test passed\n").run(); +} + +#[cargo_test] +fn cargo_compile_with_nested_deps_inferred() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = 'bar' + + [[bin]] + name = "foo" + "#, + ) + .file("src/foo.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.baz] + path = "../baz" + "#, + ) + .file( + "bar/src/lib.rs", + r#" + extern crate baz; + + pub fn gimme() -> String { + baz::gimme() + } + "#, + ) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.5.0")) + .file( + "baz/src/lib.rs", + r#" + pub fn gimme() -> String { + "test passed".to_string() + } + "#, + ) + .build(); + + p.cargo("build").run(); + + assert!(p.bin("foo").is_file()); + assert!(!p.bin("libbar.rlib").is_file()); + assert!(!p.bin("libbaz.rlib").is_file()); + + p.process(&p.bin("foo")).with_stdout("test passed\n").run(); +} + +#[cargo_test] +fn cargo_compile_with_nested_deps_correct_bin() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.baz] + path = "../baz" + "#, + ) + .file( + "bar/src/lib.rs", + r#" + extern crate baz; + + pub fn gimme() -> String { + baz::gimme() + } + "#, + ) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.5.0")) + .file( + "baz/src/lib.rs", + r#" + pub fn gimme() -> String { + "test passed".to_string() + } + "#, + ) + .build(); + + p.cargo("build").run(); + + assert!(p.bin("foo").is_file()); + assert!(!p.bin("libbar.rlib").is_file()); + assert!(!p.bin("libbaz.rlib").is_file()); + + p.process(&p.bin("foo")).with_stdout("test passed\n").run(); +} + +#[cargo_test] +fn cargo_compile_with_nested_deps_shorthand() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.baz] + path = "../baz" + + [lib] + + name = "bar" + "#, + ) + .file( + "bar/src/bar.rs", + r#" + extern crate baz; + + pub fn gimme() -> String { + baz::gimme() + } + "#, + ) + .file("baz/Cargo.toml", &basic_lib_manifest("baz")) + .file( + "baz/src/baz.rs", + r#" + pub fn gimme() -> String { + "test passed".to_string() + } + "#, + ) + .build(); + + p.cargo("build").run(); + + assert!(p.bin("foo").is_file()); + assert!(!p.bin("libbar.rlib").is_file()); + assert!(!p.bin("libbaz.rlib").is_file()); + + p.process(&p.bin("foo")).with_stdout("test passed\n").run(); +} + +#[cargo_test] +fn cargo_compile_with_nested_deps_longhand() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + version = "0.5.0" + + [[bin]] + + name = "foo" + "#, + ) + .file("src/foo.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.baz] + path = "../baz" + version = "0.5.0" + + [lib] + + name = "bar" + "#, + ) + .file( + "bar/src/bar.rs", + r#" + extern crate baz; + + pub fn gimme() -> String { + baz::gimme() + } + "#, + ) + .file("baz/Cargo.toml", &basic_lib_manifest("baz")) + .file( + "baz/src/baz.rs", + r#" + pub fn gimme() -> String { + "test passed".to_string() + } + "#, + ) + .build(); + + p.cargo("build").run(); + + assert!(p.bin("foo").is_file()); + assert!(!p.bin("libbar.rlib").is_file()); + assert!(!p.bin("libbaz.rlib").is_file()); + + p.process(&p.bin("foo")).with_stdout("test passed\n").run(); +} + +// Check that Cargo gives a sensible error if a dependency can't be found +// because of a name mismatch. +#[cargo_test] +fn cargo_compile_with_dep_name_mismatch() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.0.1" + authors = ["wycats@example.com"] + + [[bin]] + + name = "foo" + + [dependencies.notquitebar] + + path = "bar" + "#, + ) + .file("src/bin/foo.rs", &main_file(r#""i am foo""#, &["bar"])) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/bar.rs", &main_file(r#""i am bar""#, &[])) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +error: no matching package named `notquitebar` found +location searched: [CWD]/bar +required by package `foo v0.0.1 ([CWD])` +", + ) + .run(); +} + +// Ensure that renamed deps have a valid name +#[cargo_test] +fn cargo_compile_with_invalid_dep_rename() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "buggin" + version = "0.1.0" + + [dependencies] + "haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" } + "#, + ) + .file("src/main.rs", &main_file(r#""What's good?""#, &[])) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + invalid character ` ` in dependency name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) +", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_filename() { + let p = project() + .file("src/lib.rs", "") + .file( + "src/bin/a.rs", + r#" + extern crate foo; + fn main() { println!("hello a.rs"); } + "#, + ) + .file("examples/a.rs", r#"fn main() { println!("example"); }"#) + .build(); + + p.cargo("build --bin bin.rs") + .with_status(101) + .with_stderr( + "\ +[ERROR] no bin target named `bin.rs`. +Available bin targets: + a + +", + ) + .run(); + + p.cargo("build --bin a.rs") + .with_status(101) + .with_stderr( + "\ +[ERROR] no bin target named `a.rs` + +Did you mean `a`?", + ) + .run(); + + p.cargo("build --example example.rs") + .with_status(101) + .with_stderr( + "\ +[ERROR] no example target named `example.rs`. +Available example targets: + a + +", + ) + .run(); + + p.cargo("build --example a.rs") + .with_status(101) + .with_stderr( + "\ +[ERROR] no example target named `a.rs` + +Did you mean `a`?", + ) + .run(); +} + +#[cargo_test] +fn incompatible_dependencies() { + Package::new("bad", "0.1.0").publish(); + Package::new("bad", "1.0.0").publish(); + Package::new("bad", "1.0.1").publish(); + Package::new("bad", "1.0.2").publish(); + Package::new("bar", "0.1.0").dep("bad", "0.1.0").publish(); + Package::new("baz", "0.1.1").dep("bad", "=1.0.0").publish(); + Package::new("baz", "0.1.0").dep("bad", "=1.0.0").publish(); + Package::new("qux", "0.1.2").dep("bad", ">=1.0.1").publish(); + Package::new("qux", "0.1.1").dep("bad", ">=1.0.1").publish(); + Package::new("qux", "0.1.0").dep("bad", ">=1.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = "0.1.0" + baz = "0.1.0" + qux = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main(){}") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr_contains( + "\ +error: failed to select a version for `bad`. + ... required by package `qux v0.1.0` + ... which satisfies dependency `qux = \"^0.1.0\"` of package `foo v0.0.1 ([..])` +versions that meet the requirements `>=1.0.1` are: 1.0.2, 1.0.1 + +all possible versions conflict with previously selected packages. + + previously selected package `bad v1.0.0` + ... which satisfies dependency `bad = \"=1.0.0\"` of package `baz v0.1.0` + ... which satisfies dependency `baz = \"^0.1.0\"` of package `foo v0.0.1 ([..])` + +failed to select a version for `bad` which could resolve this conflict", + ) + .run(); +} + +#[cargo_test] +fn incompatible_dependencies_with_multi_semver() { + Package::new("bad", "1.0.0").publish(); + Package::new("bad", "1.0.1").publish(); + Package::new("bad", "2.0.0").publish(); + Package::new("bad", "2.0.1").publish(); + Package::new("bar", "0.1.0").dep("bad", "=1.0.0").publish(); + Package::new("baz", "0.1.0").dep("bad", ">=2.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = "0.1.0" + baz = "0.1.0" + bad = ">=1.0.1, <=2.0.0" + "#, + ) + .file("src/main.rs", "fn main(){}") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr_contains( + "\ +error: failed to select a version for `bad`. + ... required by package `foo v0.0.1 ([..])` +versions that meet the requirements `>=1.0.1, <=2.0.0` are: 2.0.0, 1.0.1 + +all possible versions conflict with previously selected packages. + + previously selected package `bad v2.0.1` + ... which satisfies dependency `bad = \">=2.0.1\"` of package `baz v0.1.0` + ... which satisfies dependency `baz = \"^0.1.0\"` of package `foo v0.0.1 ([..])` + + previously selected package `bad v1.0.0` + ... which satisfies dependency `bad = \"=1.0.0\"` of package `bar v0.1.0` + ... which satisfies dependency `bar = \"^0.1.0\"` of package `foo v0.0.1 ([..])` + +failed to select a version for `bad` which could resolve this conflict", + ) + .run(); +} + +#[cargo_test] +fn compile_path_dep_then_change_version() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + p.change_file("bar/Cargo.toml", &basic_manifest("bar", "0.0.2")); + + p.cargo("build").run(); +} + +#[cargo_test] +fn ignores_carriage_return_in_lockfile() { + let p = project() + .file("src/main.rs", "mod a; fn main() {}") + .file("src/a.rs", "") + .build(); + + p.cargo("build").run(); + + let lock = p.read_lockfile(); + p.change_file("Cargo.lock", &lock.replace("\n", "\r\n")); + p.cargo("build").run(); +} + +#[cargo_test] +fn cargo_default_env_metadata_env_var() { + // Ensure that path dep + dylib + env_var get metadata + // (even though path_dep + dylib should not) + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/lib.rs", "// hi") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + name = "bar" + crate_type = ["dylib"] + "#, + ) + .file("bar/src/lib.rs", "// hello") + .build(); + + // No metadata on libbar since it's a dylib path dependency + p.cargo("build -v") + .with_stderr(&format!( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--crate-type dylib \ + --emit=[..]link \ + -C prefer-dynamic[..]-C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]-C debuginfo=2 \ + -C metadata=[..] \ + -C extra-filename=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps \ + --extern bar=[CWD]/target/debug/deps/{prefix}bar{suffix}` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + prefix = env::consts::DLL_PREFIX, + suffix = env::consts::DLL_SUFFIX, + )) + .run(); + + p.cargo("clean").run(); + + // If you set the env-var, then we expect metadata on libbar + p.cargo("build -v") + .env("__CARGO_DEFAULT_LIB_METADATA", "stable") + .with_stderr(&format!( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--crate-type dylib \ + --emit=[..]link \ + -C prefer-dynamic[..]-C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]-C debuginfo=2 \ + -C metadata=[..] \ + -C extra-filename=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps \ + --extern bar=[CWD]/target/debug/deps/{prefix}bar-[..]{suffix}` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + prefix = env::consts::DLL_PREFIX, + suffix = env::consts::DLL_SUFFIX, + )) + .run(); +} + +#[cargo_test] +fn crate_env_vars() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.1-alpha.1" + description = "This is foo" + homepage = "https://example.com" + repository = "https://example.com/repo.git" + authors = ["wycats@example.com"] + license = "MIT OR Apache-2.0" + license-file = "license.txt" + rust-version = "1.61.0" + + [[bin]] + name = "foo-bar" + path = "src/main.rs" + "#, + ) + .file( + "src/main.rs", + r#" + extern crate foo; + + + static VERSION_MAJOR: &'static str = env!("CARGO_PKG_VERSION_MAJOR"); + static VERSION_MINOR: &'static str = env!("CARGO_PKG_VERSION_MINOR"); + static VERSION_PATCH: &'static str = env!("CARGO_PKG_VERSION_PATCH"); + static VERSION_PRE: &'static str = env!("CARGO_PKG_VERSION_PRE"); + static VERSION: &'static str = env!("CARGO_PKG_VERSION"); + static CARGO_MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR"); + static PKG_NAME: &'static str = env!("CARGO_PKG_NAME"); + static HOMEPAGE: &'static str = env!("CARGO_PKG_HOMEPAGE"); + static REPOSITORY: &'static str = env!("CARGO_PKG_REPOSITORY"); + static LICENSE: &'static str = env!("CARGO_PKG_LICENSE"); + static LICENSE_FILE: &'static str = env!("CARGO_PKG_LICENSE_FILE"); + static DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION"); + static RUST_VERSION: &'static str = env!("CARGO_PKG_RUST_VERSION"); + static BIN_NAME: &'static str = env!("CARGO_BIN_NAME"); + static CRATE_NAME: &'static str = env!("CARGO_CRATE_NAME"); + + + fn main() { + let s = format!("{}-{}-{} @ {} in {}", VERSION_MAJOR, + VERSION_MINOR, VERSION_PATCH, VERSION_PRE, + CARGO_MANIFEST_DIR); + assert_eq!(s, foo::version()); + println!("{}", s); + assert_eq!("foo", PKG_NAME); + assert_eq!("foo-bar", BIN_NAME); + assert_eq!("foo_bar", CRATE_NAME); + assert_eq!("https://example.com", HOMEPAGE); + assert_eq!("https://example.com/repo.git", REPOSITORY); + assert_eq!("MIT OR Apache-2.0", LICENSE); + assert_eq!("license.txt", LICENSE_FILE); + assert_eq!("This is foo", DESCRIPTION); + assert_eq!("1.61.0", RUST_VERSION); + let s = format!("{}.{}.{}-{}", VERSION_MAJOR, + VERSION_MINOR, VERSION_PATCH, VERSION_PRE); + assert_eq!(s, VERSION); + + // Verify CARGO_TARGET_TMPDIR isn't set for bins + assert!(option_env!("CARGO_TARGET_TMPDIR").is_none()); + } + "#, + ) + .file( + "src/lib.rs", + r#" + use std::env; + use std::path::PathBuf; + + pub fn version() -> String { + format!("{}-{}-{} @ {} in {}", + env!("CARGO_PKG_VERSION_MAJOR"), + env!("CARGO_PKG_VERSION_MINOR"), + env!("CARGO_PKG_VERSION_PATCH"), + env!("CARGO_PKG_VERSION_PRE"), + env!("CARGO_MANIFEST_DIR")) + } + + pub fn check_no_int_test_env() { + env::var("CARGO_TARGET_DIR").unwrap_err(); + } + + pub fn check_tmpdir(tmp: Option<&'static str>) { + let tmpdir: PathBuf = tmp.unwrap().into(); + + let exe: PathBuf = env::current_exe().unwrap().into(); + let mut expected: PathBuf = exe.parent().unwrap() + .parent().unwrap() + .parent().unwrap() + .into(); + expected.push("tmp"); + assert_eq!(tmpdir, expected); + + // Check that CARGO_TARGET_TMPDIR isn't set for lib code + assert!(option_env!("CARGO_TARGET_TMPDIR").is_none()); + env::var("CARGO_TARGET_TMPDIR").unwrap_err(); + } + + #[test] + fn env() { + // Check that CARGO_TARGET_TMPDIR isn't set for unit tests + assert!(option_env!("CARGO_TARGET_TMPDIR").is_none()); + env::var("CARGO_TARGET_TMPDIR").unwrap_err(); + } + "#, + ) + .file( + "examples/ex-env-vars.rs", + r#" + static PKG_NAME: &'static str = env!("CARGO_PKG_NAME"); + static BIN_NAME: &'static str = env!("CARGO_BIN_NAME"); + static CRATE_NAME: &'static str = env!("CARGO_CRATE_NAME"); + + fn main() { + assert_eq!("foo", PKG_NAME); + assert_eq!("ex-env-vars", BIN_NAME); + assert_eq!("ex_env_vars", CRATE_NAME); + + // Verify CARGO_TARGET_TMPDIR isn't set for examples + assert!(option_env!("CARGO_TARGET_TMPDIR").is_none()); + } + "#, + ) + .file( + "tests/env.rs", + r#" + #[test] + fn env() { + foo::check_tmpdir(option_env!("CARGO_TARGET_TMPDIR")); + } + "#, + ); + + let p = if is_nightly() { + p.file( + "benches/env.rs", + r#" + #![feature(test)] + extern crate test; + use test::Bencher; + + #[bench] + fn env(_: &mut Bencher) { + foo::check_tmpdir(option_env!("CARGO_TARGET_TMPDIR")); + } + "#, + ) + .build() + } else { + p.build() + }; + + println!("build"); + p.cargo("build -v").run(); + + println!("bin"); + p.process(&p.bin("foo-bar")) + .with_stdout("0-5-1 @ alpha.1 in [CWD]") + .run(); + + println!("example"); + p.cargo("run --example ex-env-vars -v").run(); + + println!("test"); + p.cargo("test -v").run(); + + if is_nightly() { + println!("bench"); + p.cargo("bench -v").run(); + } +} + +#[cargo_test] +fn crate_authors_env_vars() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.1-alpha.1" + authors = ["wycats@example.com", "neikos@example.com"] + "#, + ) + .file( + "src/main.rs", + r#" + extern crate foo; + + static AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS"); + + fn main() { + let s = "wycats@example.com:neikos@example.com"; + assert_eq!(AUTHORS, foo::authors()); + println!("{}", AUTHORS); + assert_eq!(s, AUTHORS); + } + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn authors() -> String { + format!("{}", env!("CARGO_PKG_AUTHORS")) + } + "#, + ) + .build(); + + println!("build"); + p.cargo("build -v").run(); + + println!("bin"); + p.process(&p.bin("foo")) + .with_stdout("wycats@example.com:neikos@example.com") + .run(); + + println!("test"); + p.cargo("test -v").run(); +} + +#[cargo_test] +fn vv_prints_rustc_env_vars() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = ["escape='\"@example.com"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + let mut b = p.cargo("build -vv"); + + if cfg!(windows) { + b.with_stderr_contains( + "[RUNNING] `[..]set CARGO_PKG_NAME=foo&& [..]rustc [..]`" + ).with_stderr_contains( + r#"[RUNNING] `[..]set CARGO_PKG_AUTHORS="escape='\"@example.com"&& [..]rustc [..]`"# + ) + } else { + b.with_stderr_contains("[RUNNING] `[..]CARGO_PKG_NAME=foo [..]rustc [..]`") + .with_stderr_contains( + r#"[RUNNING] `[..]CARGO_PKG_AUTHORS='escape='\''"@example.com' [..]rustc [..]`"#, + ) + }; + + b.run(); +} + +// The tester may already have LD_LIBRARY_PATH=::/foo/bar which leads to a false positive error +fn setenv_for_removing_empty_component(mut execs: Execs) -> Execs { + let v = dylib_path_envvar(); + if let Ok(search_path) = env::var(v) { + let new_search_path = + env::join_paths(env::split_paths(&search_path).filter(|e| !e.as_os_str().is_empty())) + .expect("join_paths"); + execs.env(v, new_search_path); // build_command() will override LD_LIBRARY_PATH accordingly + } + execs +} + +// Regression test for #4277 +#[cargo_test] +fn crate_library_path_env_var() { + let p = project() + .file( + "src/main.rs", + &format!( + r#" + fn main() {{ + let search_path = env!("{}"); + let paths = std::env::split_paths(&search_path).collect::>(); + assert!(!paths.contains(&"".into())); + }} + "#, + dylib_path_envvar() + ), + ) + .build(); + + setenv_for_removing_empty_component(p.cargo("run")).run(); +} + +// Regression test for #4277 +#[cargo_test] +fn build_with_fake_libc_not_loading() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", r#" "#) + .file("libc.so.6", r#""#) + .build(); + + setenv_for_removing_empty_component(p.cargo("build")).run(); +} + +// this is testing that src/.rs still works (for now) +#[cargo_test] +fn many_crate_types_old_style_lib_location() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + + name = "foo" + crate_type = ["rlib", "dylib"] + "#, + ) + .file("src/foo.rs", "pub fn foo() {}") + .build(); + p.cargo("build") + .with_stderr_contains( + "\ +[WARNING] path `[..]src/foo.rs` was erroneously implicitly accepted for library `foo`, +please rename the file to `src/lib.rs` or set lib.path in Cargo.toml", + ) + .run(); + + assert!(p.root().join("target/debug/libfoo.rlib").is_file()); + let fname = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); + assert!(p.root().join("target/debug").join(&fname).is_file()); +} + +#[cargo_test] +fn many_crate_types_correct() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + + name = "foo" + crate_type = ["rlib", "dylib"] + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .build(); + p.cargo("build").run(); + + assert!(p.root().join("target/debug/libfoo.rlib").is_file()); + let fname = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); + assert!(p.root().join("target/debug").join(&fname).is_file()); +} + +#[cargo_test] +fn set_both_dylib_and_cdylib_crate_types() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + + name = "foo" + crate_type = ["cdylib", "dylib"] + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .build(); + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + library `foo` cannot set the crate type of both `dylib` and `cdylib` +", + ) + .run(); +} + +#[cargo_test] +fn dev_dependencies_conflicting_warning() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dev-dependencies] + a = {path = "a"} + [dev_dependencies] + a = {path = "a"} + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + "#, + ) + .file("a/src/lib.rs", "") + .build(); + p.cargo("build") + .with_stderr_contains( +"[WARNING] conflicting between `dev-dependencies` and `dev_dependencies` in the `foo` package.\n + `dev_dependencies` is ignored and not recommended for use in the future" + ) + .run(); +} + +#[cargo_test] +fn build_dependencies_conflicting_warning() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [build-dependencies] + a = {path = "a"} + [build_dependencies] + a = {path = "a"} + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + "#, + ) + .file("a/src/lib.rs", "") + .build(); + p.cargo("build") + .with_stderr_contains( +"[WARNING] conflicting between `build-dependencies` and `build_dependencies` in the `foo` package.\n + `build_dependencies` is ignored and not recommended for use in the future" + ) + .run(); +} + +#[cargo_test] +fn lib_crate_types_conflicting_warning() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + name = "foo" + crate-type = ["rlib", "dylib"] + crate_type = ["staticlib", "dylib"] + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .build(); + p.cargo("build") + .with_stderr_contains( +"[WARNING] conflicting between `crate-type` and `crate_type` in the `foo` library target.\n + `crate_type` is ignored and not recommended for use in the future", + ) + .run(); +} + +#[cargo_test] +fn examples_crate_types_conflicting_warning() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [[example]] + name = "ex" + path = "examples/ex.rs" + crate-type = ["rlib", "dylib"] + crate_type = ["proc_macro"] + [[example]] + name = "goodbye" + path = "examples/ex-goodbye.rs" + crate-type = ["rlib", "dylib"] + crate_type = ["rlib", "staticlib"] + "#, + ) + .file("src/lib.rs", "") + .file( + "examples/ex.rs", + r#" + fn main() { println!("ex"); } + "#, + ) + .file( + "examples/ex-goodbye.rs", + r#" + fn main() { println!("goodbye"); } + "#, + ) + .build(); + p.cargo("build") + .with_stderr_contains( + "\ +[WARNING] conflicting between `crate-type` and `crate_type` in the `ex` example target.\n + `crate_type` is ignored and not recommended for use in the future +[WARNING] conflicting between `crate-type` and `crate_type` in the `goodbye` example target.\n + `crate_type` is ignored and not recommended for use in the future", + ) + .run(); +} + +#[cargo_test] +fn self_dependency() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [dependencies.test] + + path = "." + + [lib] + name = "test" + path = "src/test.rs" + "#, + ) + .file("src/test.rs", "fn main() {}") + .build(); + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] cyclic package dependency: package `test v0.0.0 ([CWD])` depends on itself. Cycle: +package `test v0.0.0 ([CWD])` + ... which satisfies path dependency `test` of package `test v0.0.0 ([..])`", + ) + .run(); +} + +#[cargo_test] +/// Make sure broken and loop symlinks don't break the build +/// +/// This test requires you to be able to make symlinks. +/// For windows, this may require you to enable developer mode. +fn ignore_broken_symlinks() { + if !symlink_supported() { + return; + } + + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .symlink("Notafile", "bar") + // To hit the symlink directory, we need a build script + // to trigger a full scan of package files. + .file("build.rs", &main_file(r#""build script""#, &[])) + .symlink_dir("a/b", "a/b/c/d/foo") + .build(); + + p.cargo("build") + .with_stderr_contains( + "[WARNING] File system loop found: [..]/a/b/c/d/foo points to an ancestor [..]/a/b", + ) + .run(); + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("i am foo\n").run(); +} + +#[cargo_test] +fn missing_lib_and_bin() { + let p = project().build(); + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]Cargo.toml` + +Caused by: + no targets specified in the manifest + either src/lib.rs, src/main.rs, a [lib] section, or [[bin]] section must be present\n", + ) + .run(); +} + +#[cargo_test] +fn lto_build() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [profile.release] + lto = true + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("build -v --release") + .with_stderr( + "\ +[COMPILING] test v0.0.0 ([CWD]) +[RUNNING] `rustc --crate-name test src/main.rs [..]--crate-type bin \ + --emit=[..]link \ + -C opt-level=3 \ + -C lto \ + [..] +[FINISHED] release [optimized] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn verbose_build() { + let p = project().file("src/lib.rs", "").build(); + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]-C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn verbose_release_build() { + let p = project().file("src/lib.rs", "").build(); + p.cargo("build -v --release") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]\ + -C opt-level=3[..]\ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/release/deps` +[FINISHED] release [optimized] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn verbose_release_build_short() { + let p = project().file("src/lib.rs", "").build(); + p.cargo("build -v -r") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]\ + -C opt-level=3[..]\ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/release/deps` +[FINISHED] release [optimized] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn verbose_release_build_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [dependencies.foo] + path = "foo" + "#, + ) + .file("src/lib.rs", "") + .file( + "foo/Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.0.0" + authors = [] + + [lib] + name = "foo" + crate_type = ["dylib", "rlib"] + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + p.cargo("build -v --release") + .with_stderr(&format!( + "\ +[COMPILING] foo v0.0.0 ([CWD]/foo) +[RUNNING] `rustc --crate-name foo foo/src/lib.rs [..]\ + --crate-type dylib --crate-type rlib \ + --emit=[..]link \ + -C prefer-dynamic[..]\ + -C opt-level=3[..]\ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/release/deps` +[COMPILING] test v0.0.0 ([CWD]) +[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]\ + -C opt-level=3[..]\ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/release/deps \ + --extern foo=[CWD]/target/release/deps/{prefix}foo{suffix} \ + --extern foo=[CWD]/target/release/deps/libfoo.rlib` +[FINISHED] release [optimized] target(s) in [..] +", + prefix = env::consts::DLL_PREFIX, + suffix = env::consts::DLL_SUFFIX + )) + .run(); +} + +#[cargo_test] +fn explicit_examples() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + authors = [] + + [lib] + name = "foo" + path = "src/lib.rs" + + [[example]] + name = "hello" + path = "examples/ex-hello.rs" + + [[example]] + name = "goodbye" + path = "examples/ex-goodbye.rs" + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn get_hello() -> &'static str { "Hello" } + pub fn get_goodbye() -> &'static str { "Goodbye" } + pub fn get_world() -> &'static str { "World" } + "#, + ) + .file( + "examples/ex-hello.rs", + r#" + extern crate foo; + fn main() { println!("{}, {}!", foo::get_hello(), foo::get_world()); } + "#, + ) + .file( + "examples/ex-goodbye.rs", + r#" + extern crate foo; + fn main() { println!("{}, {}!", foo::get_goodbye(), foo::get_world()); } + "#, + ) + .build(); + + p.cargo("build --examples").run(); + p.process(&p.bin("examples/hello")) + .with_stdout("Hello, World!\n") + .run(); + p.process(&p.bin("examples/goodbye")) + .with_stdout("Goodbye, World!\n") + .run(); +} + +#[cargo_test] +fn non_existing_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [lib] + name = "foo" + path = "src/lib.rs" + + [[test]] + name = "hello" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build --tests -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + can't find `hello` test at `tests/hello.rs` or `tests/hello/main.rs`. \ + Please specify test.path if you want to use a non-default path.", + ) + .run(); +} + +#[cargo_test] +fn non_existing_example() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [lib] + name = "foo" + path = "src/lib.rs" + + [[example]] + name = "hello" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build --examples -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + can't find `hello` example at `examples/hello.rs` or `examples/hello/main.rs`. \ + Please specify example.path if you want to use a non-default path.", + ) + .run(); +} + +#[cargo_test] +fn non_existing_benchmark() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [lib] + name = "foo" + path = "src/lib.rs" + + [[bench]] + name = "hello" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build --benches -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + can't find `hello` bench at `benches/hello.rs` or `benches/hello/main.rs`. \ + Please specify bench.path if you want to use a non-default path.", + ) + .run(); +} + +#[cargo_test] +fn non_existing_binary() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/lib.rs", "") + .file("src/bin/ehlo.rs", "") + .build(); + + p.cargo("build -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + can't find `foo` bin at `src/bin/foo.rs` or `src/bin/foo/main.rs`. \ + Please specify bin.path if you want to use a non-default path.", + ) + .run(); +} + +#[cargo_test] +fn commonly_wrong_path_of_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [lib] + name = "foo" + path = "src/lib.rs" + + [[test]] + name = "foo" + "#, + ) + .file("src/lib.rs", "") + .file("test/foo.rs", "") + .build(); + + p.cargo("build --tests -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + can't find `foo` test at default paths, but found a file at `test/foo.rs`. + Perhaps rename the file to `tests/foo.rs` for target auto-discovery, \ + or specify test.path if you want to use a non-default path.", + ) + .run(); +} + +#[cargo_test] +fn commonly_wrong_path_of_example() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [lib] + name = "foo" + path = "src/lib.rs" + + [[example]] + name = "foo" + "#, + ) + .file("src/lib.rs", "") + .file("example/foo.rs", "") + .build(); + + p.cargo("build --examples -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + can't find `foo` example at default paths, but found a file at `example/foo.rs`. + Perhaps rename the file to `examples/foo.rs` for target auto-discovery, \ + or specify example.path if you want to use a non-default path.", + ) + .run(); +} + +#[cargo_test] +fn commonly_wrong_path_of_benchmark() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [lib] + name = "foo" + path = "src/lib.rs" + + [[bench]] + name = "foo" + "#, + ) + .file("src/lib.rs", "") + .file("bench/foo.rs", "") + .build(); + + p.cargo("build --benches -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + can't find `foo` bench at default paths, but found a file at `bench/foo.rs`. + Perhaps rename the file to `benches/foo.rs` for target auto-discovery, \ + or specify bench.path if you want to use a non-default path.", + ) + .run(); +} + +#[cargo_test] +fn commonly_wrong_path_binary() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/lib.rs", "") + .file("src/bins/foo.rs", "") + .build(); + + p.cargo("build -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + can't find `foo` bin at default paths, but found a file at `src/bins/foo.rs`. + Perhaps rename the file to `src/bin/foo.rs` for target auto-discovery, \ + or specify bin.path if you want to use a non-default path.", + ) + .run(); +} + +#[cargo_test] +fn commonly_wrong_path_subdir_binary() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/lib.rs", "") + .file("src/bins/foo/main.rs", "") + .build(); + + p.cargo("build -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + can't find `foo` bin at default paths, but found a file at `src/bins/foo/main.rs`. + Perhaps rename the file to `src/bin/foo/main.rs` for target auto-discovery, \ + or specify bin.path if you want to use a non-default path.", + ) + .run(); +} + +#[cargo_test] +fn found_multiple_target_files() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/lib.rs", "") + .file("src/bin/foo.rs", "") + .file("src/bin/foo/main.rs", "") + .build(); + + p.cargo("build -v") + .with_status(101) + // Don't assert the inferred paths since the order is non-deterministic. + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + cannot infer path for `foo` bin + Cargo doesn't know which to use because multiple target files found \ + at `src/bin/foo[..].rs` and `src/bin/foo[..].rs`.", + ) + .run(); +} + +#[cargo_test] +fn legacy_binary_paths_warnings() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + authors = [] + + [[bin]] + name = "bar" + "#, + ) + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .with_stderr_contains( + "\ +[WARNING] path `[..]src/main.rs` was erroneously implicitly accepted for binary `bar`, +please set bin.path in Cargo.toml", + ) + .run(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + authors = [] + + [[bin]] + name = "bar" + "#, + ) + .file("src/lib.rs", "") + .file("src/bin/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .with_stderr_contains( + "\ +[WARNING] path `[..]src/bin/main.rs` was erroneously implicitly accepted for binary `bar`, +please set bin.path in Cargo.toml", + ) + .run(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + authors = [] + + [[bin]] + name = "bar" + "#, + ) + .file("src/bar.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .with_stderr_contains( + "\ +[WARNING] path `[..]src/bar.rs` was erroneously implicitly accepted for binary `bar`, +please set bin.path in Cargo.toml", + ) + .run(); +} + +#[cargo_test] +fn implicit_examples() { + let p = project() + .file( + "src/lib.rs", + r#" + pub fn get_hello() -> &'static str { "Hello" } + pub fn get_goodbye() -> &'static str { "Goodbye" } + pub fn get_world() -> &'static str { "World" } + "#, + ) + .file( + "examples/hello.rs", + r#" + extern crate foo; + fn main() { + println!("{}, {}!", foo::get_hello(), foo::get_world()); + } + "#, + ) + .file( + "examples/goodbye.rs", + r#" + extern crate foo; + fn main() { + println!("{}, {}!", foo::get_goodbye(), foo::get_world()); + } + "#, + ) + .build(); + + p.cargo("build --examples").run(); + p.process(&p.bin("examples/hello")) + .with_stdout("Hello, World!\n") + .run(); + p.process(&p.bin("examples/goodbye")) + .with_stdout("Goodbye, World!\n") + .run(); +} + +#[cargo_test] +fn standard_build_no_ndebug() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/foo.rs", + r#" + fn main() { + if cfg!(debug_assertions) { + println!("slow") + } else { + println!("fast") + } + } + "#, + ) + .build(); + + p.cargo("build").run(); + p.process(&p.bin("foo")).with_stdout("slow\n").run(); +} + +#[cargo_test] +fn release_build_ndebug() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/foo.rs", + r#" + fn main() { + if cfg!(debug_assertions) { + println!("slow") + } else { + println!("fast") + } + } + "#, + ) + .build(); + + p.cargo("build --release").run(); + p.process(&p.release_bin("foo")).with_stdout("fast\n").run(); +} + +#[cargo_test] +fn inferred_main_bin() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + p.cargo("build").run(); + p.process(&p.bin("foo")).run(); +} + +#[cargo_test] +fn deletion_causes_failure() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.change_file("Cargo.toml", &basic_manifest("foo", "0.0.1")); + p.cargo("build") + .with_status(101) + .with_stderr_contains("[..]can't find crate for `bar`") + .run(); +} + +#[cargo_test] +fn bad_cargo_toml_in_target_dir() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("target/Cargo.toml", "bad-toml") + .build(); + + p.cargo("build").run(); + p.process(&p.bin("foo")).run(); +} + +#[cargo_test] +fn lib_with_standard_name() { + let p = project() + .file("Cargo.toml", &basic_manifest("syntax", "0.0.1")) + .file("src/lib.rs", "pub fn foo() {}") + .file( + "src/main.rs", + "extern crate syntax; fn main() { syntax::foo() }", + ) + .build(); + + p.cargo("build") + .with_stderr( + "\ +[COMPILING] syntax v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn simple_staticlib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [lib] + name = "foo" + crate-type = ["staticlib"] + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .build(); + + // env var is a test for #1381 + p.cargo("build").env("CARGO_LOG", "nekoneko=trace").run(); +} + +#[cargo_test] +fn staticlib_rlib_and_bin() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [lib] + name = "foo" + crate-type = ["staticlib", "rlib"] + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file("src/main.rs", "extern crate foo; fn main() { foo::foo(); }") + .build(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn opt_out_of_bin() { + let p = project() + .file( + "Cargo.toml", + r#" + bin = [] + + [package] + name = "foo" + authors = [] + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "") + .file("src/main.rs", "bad syntax") + .build(); + p.cargo("build").run(); +} + +#[cargo_test] +fn single_lib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [lib] + name = "foo" + path = "src/bar.rs" + "#, + ) + .file("src/bar.rs", "") + .build(); + p.cargo("build").run(); +} + +#[cargo_test] +fn freshness_ignores_excluded() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = "build.rs" + exclude = ["src/b*.rs"] + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + .build(); + foo.root().move_into_the_past(); + + foo.cargo("build") + .with_stderr( + "\ +[COMPILING] foo v0.0.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + // Smoke test to make sure it doesn't compile again + println!("first pass"); + foo.cargo("build").with_stdout("").run(); + + // Modify an ignored file and make sure we don't rebuild + println!("second pass"); + foo.change_file("src/bar.rs", ""); + foo.cargo("build").with_stdout("").run(); +} + +#[cargo_test] +fn rebuild_preserves_out_dir() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = 'build.rs' + "#, + ) + .file( + "build.rs", + r#" + use std::env; + use std::fs::File; + use std::path::Path; + + fn main() { + let path = Path::new(&env::var("OUT_DIR").unwrap()).join("foo"); + if env::var_os("FIRST").is_some() { + File::create(&path).unwrap(); + } else { + File::create(&path).unwrap(); + } + } + "#, + ) + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + .build(); + foo.root().move_into_the_past(); + + foo.cargo("build") + .env("FIRST", "1") + .with_stderr( + "\ +[COMPILING] foo v0.0.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + foo.change_file("src/bar.rs", ""); + foo.cargo("build") + .with_stderr( + "\ +[COMPILING] foo v0.0.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn dep_no_libs() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.0")) + .file("bar/src/main.rs", "") + .build(); + foo.cargo("build").run(); +} + +#[cargo_test] +fn recompile_space_in_name() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [lib] + name = "foo" + path = "src/my lib.rs" + "#, + ) + .file("src/my lib.rs", "") + .build(); + foo.cargo("build").run(); + foo.root().move_into_the_past(); + foo.cargo("build").with_stdout("").run(); +} + +#[cfg(unix)] +#[cargo_test] +fn credentials_is_unreadable() { + use cargo_test_support::paths::home; + use std::os::unix::prelude::*; + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/lib.rs", "") + .build(); + + let credentials = home().join(".cargo/credentials.toml"); + t!(fs::create_dir_all(credentials.parent().unwrap())); + t!(fs::write( + &credentials, + r#" + [registry] + token = "api-token" + "# + )); + let stat = fs::metadata(credentials.as_path()).unwrap(); + let mut perms = stat.permissions(); + perms.set_mode(0o000); + fs::set_permissions(credentials, perms).unwrap(); + + p.cargo("build").run(); +} + +#[cfg(unix)] +#[cargo_test] +fn ignore_bad_directories() { + use std::os::unix::prelude::*; + let foo = project() + .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) + .file("src/lib.rs", "") + .build(); + let dir = foo.root().join("tmp"); + fs::create_dir(&dir).unwrap(); + let stat = fs::metadata(&dir).unwrap(); + let mut perms = stat.permissions(); + perms.set_mode(0o644); + fs::set_permissions(&dir, perms.clone()).unwrap(); + foo.cargo("build").run(); + perms.set_mode(0o755); + fs::set_permissions(&dir, perms).unwrap(); +} + +#[cargo_test] +fn bad_cargo_config() { + let foo = project() + .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) + .file("src/lib.rs", "") + .file(".cargo/config", "this is not valid toml") + .build(); + foo.cargo("build -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] could not load Cargo configuration + +Caused by: + could not parse TOML configuration in `[..]` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 1, column 6 + | + 1 | this is not valid toml + | ^ + expected `.`, `=` +", + ) + .run(); +} + +#[cargo_test] +fn cargo_platform_specific_dependency() { + let host = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + + [target.{host}.dependencies] + dep = {{ path = "dep" }} + [target.{host}.build-dependencies] + build = {{ path = "build" }} + [target.{host}.dev-dependencies] + dev = {{ path = "dev" }} + "#, + host = host + ), + ) + .file("src/main.rs", "extern crate dep; fn main() { dep::dep() }") + .file( + "tests/foo.rs", + "extern crate dev; #[test] fn foo() { dev::dev() }", + ) + .file( + "build.rs", + "extern crate build; fn main() { build::build(); }", + ) + .file("dep/Cargo.toml", &basic_manifest("dep", "0.5.0")) + .file("dep/src/lib.rs", "pub fn dep() {}") + .file("build/Cargo.toml", &basic_manifest("build", "0.5.0")) + .file("build/src/lib.rs", "pub fn build() {}") + .file("dev/Cargo.toml", &basic_manifest("dev", "0.5.0")) + .file("dev/src/lib.rs", "pub fn dev() {}") + .build(); + + p.cargo("build").run(); + + assert!(p.bin("foo").is_file()); + p.cargo("test").run(); +} + +#[cargo_test] +fn cargo_platform_specific_dependency_build_dependencies_conflicting_warning() { + let host = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + + [target.{host}.build-dependencies] + build = {{ path = "build" }} + [target.{host}.build_dependencies] + build = {{ path = "build" }} + "#, + host = host + ), + ) + .file("src/main.rs", "fn main() { }") + .file( + "build.rs", + "extern crate build; fn main() { build::build(); }", + ) + .file("build/Cargo.toml", &basic_manifest("build", "0.5.0")) + .file("build/src/lib.rs", "pub fn build() {}") + .build(); + + p.cargo("build") + .with_stderr_contains( + format!("[WARNING] conflicting between `build-dependencies` and `build_dependencies` in the `{}` platform target.\n + `build_dependencies` is ignored and not recommended for use in the future", host) + ) + .run(); + + assert!(p.bin("foo").is_file()); +} + +#[cargo_test] +fn cargo_platform_specific_dependency_dev_dependencies_conflicting_warning() { + let host = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [target.{host}.dev-dependencies] + dev = {{ path = "dev" }} + [target.{host}.dev_dependencies] + dev = {{ path = "dev" }} + "#, + host = host + ), + ) + .file("src/main.rs", "fn main() { }") + .file( + "tests/foo.rs", + "extern crate dev; #[test] fn foo() { dev::dev() }", + ) + .file("dev/Cargo.toml", &basic_manifest("dev", "0.5.0")) + .file("dev/src/lib.rs", "pub fn dev() {}") + .build(); + + p.cargo("build") + .with_stderr_contains( + format!("[WARNING] conflicting between `dev-dependencies` and `dev_dependencies` in the `{}` platform target.\n + `dev_dependencies` is ignored and not recommended for use in the future", host) + ) + .run(); + + assert!(p.bin("foo").is_file()); + p.cargo("test").run(); +} + +#[cargo_test] +fn bad_platform_specific_dependency() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [target.wrong-target.dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file( + "bar/src/lib.rs", + r#"pub fn gimme() -> String { format!("") }"#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr_contains("[..]can't find crate for `bar`") + .run(); +} + +#[cargo_test] +fn cargo_platform_specific_dependency_wrong_platform() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [target.non-existing-triplet.dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file( + "bar/src/lib.rs", + "invalid rust file, should not be compiled", + ) + .build(); + + p.cargo("build").run(); + + assert!(p.bin("foo").is_file()); + p.process(&p.bin("foo")).run(); + + let lockfile = p.read_lockfile(); + assert!(lockfile.contains("bar")); +} + +#[cargo_test] +fn example_as_lib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["lib"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + p.cargo("build --example=ex").run(); + assert!(p.example_lib("ex", "lib").is_file()); +} + +#[cargo_test] +fn example_as_rlib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["rlib"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + p.cargo("build --example=ex").run(); + assert!(p.example_lib("ex", "rlib").is_file()); +} + +#[cargo_test] +fn example_as_dylib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["dylib"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + p.cargo("build --example=ex").run(); + assert!(p.example_lib("ex", "dylib").is_file()); +} + +#[cargo_test] +fn example_as_proc_macro() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["proc-macro"] + "#, + ) + .file("src/lib.rs", "") + .file( + "examples/ex.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro] + pub fn eat(_item: TokenStream) -> TokenStream { + "".parse().unwrap() + } + "#, + ) + .build(); + + p.cargo("build --example=ex").run(); + assert!(p.example_lib("ex", "proc-macro").is_file()); +} + +#[cargo_test] +fn example_bin_same_name() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("examples/foo.rs", "fn main() {}") + .build(); + + p.cargo("build --examples").run(); + + assert!(!p.bin("foo").is_file()); + // We expect a file of the form bin/foo-{metadata_hash} + assert!(p.bin("examples/foo").is_file()); + + p.cargo("build --examples").run(); + + assert!(!p.bin("foo").is_file()); + // We expect a file of the form bin/foo-{metadata_hash} + assert!(p.bin("examples/foo").is_file()); +} + +#[cargo_test] +fn compile_then_delete() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + p.cargo("run -v").run(); + assert!(p.bin("foo").is_file()); + if cfg!(windows) { + // On windows unlinking immediately after running often fails, so sleep + sleep_ms(100); + } + fs::remove_file(&p.bin("foo")).unwrap(); + p.cargo("run -v").run(); +} + +#[cargo_test] +fn transitive_dependencies_not_available() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.aaaaa] + path = "a" + "#, + ) + .file( + "src/main.rs", + "extern crate bbbbb; extern crate aaaaa; fn main() {}", + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "aaaaa" + version = "0.0.1" + authors = [] + + [dependencies.bbbbb] + path = "../b" + "#, + ) + .file("a/src/lib.rs", "extern crate bbbbb;") + .file("b/Cargo.toml", &basic_manifest("bbbbb", "0.0.1")) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_status(101) + .with_stderr_contains("[..] can't find crate for `bbbbb`[..]") + .run(); +} + +#[cargo_test] +fn cyclic_deps_rejected() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [dependencies.foo] + path = ".." + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_status(101) + .with_stderr( +"[ERROR] cyclic package dependency: package `a v0.0.1 ([CWD]/a)` depends on itself. Cycle: +package `a v0.0.1 ([CWD]/a)` + ... which satisfies path dependency `a` of package `foo v0.0.1 ([CWD])` + ... which satisfies path dependency `foo` of package `a v0.0.1 ([..])`", + ).run(); +} + +#[cargo_test] +fn predictable_filenames() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo" + crate-type = ["dylib", "rlib"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v").run(); + assert!(p.root().join("target/debug/libfoo.rlib").is_file()); + let dylib_name = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); + assert!(p.root().join("target/debug").join(dylib_name).is_file()); +} + +#[cargo_test] +fn dashes_to_underscores() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo-bar", "0.0.1")) + .file("src/lib.rs", "") + .file("src/main.rs", "extern crate foo_bar; fn main() {}") + .build(); + + p.cargo("build -v").run(); + assert!(p.bin("foo-bar").is_file()); +} + +#[cargo_test] +fn dashes_in_crate_name_bad() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo-bar" + "#, + ) + .file("src/lib.rs", "") + .file("src/main.rs", "extern crate foo_bar; fn main() {}") + .build(); + + p.cargo("build -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + library target names cannot contain hyphens: foo-bar +", + ) + .run(); +} + +#[cargo_test] +fn rustc_env_var() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("build -v") + .env("RUSTC", "rustc-that-does-not-exist") + .with_status(101) + .with_stderr( + "\ +[ERROR] could not execute process `rustc-that-does-not-exist -vV` ([..]) + +Caused by: +[..] +", + ) + .run(); + assert!(!p.bin("a").is_file()); +} + +#[cargo_test] +fn filtering() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("src/bin/b.rs", "fn main() {}") + .file("examples/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .build(); + + p.cargo("build --lib").run(); + assert!(!p.bin("a").is_file()); + + p.cargo("build --bin=a --example=a").run(); + assert!(p.bin("a").is_file()); + assert!(!p.bin("b").is_file()); + assert!(p.bin("examples/a").is_file()); + assert!(!p.bin("examples/b").is_file()); +} + +#[cargo_test] +fn filtering_implicit_bins() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("src/bin/b.rs", "fn main() {}") + .file("examples/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .build(); + + p.cargo("build --bins").run(); + assert!(p.bin("a").is_file()); + assert!(p.bin("b").is_file()); + assert!(!p.bin("examples/a").is_file()); + assert!(!p.bin("examples/b").is_file()); +} + +#[cargo_test] +fn filtering_implicit_examples() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("src/bin/b.rs", "fn main() {}") + .file("examples/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .build(); + + p.cargo("build --examples").run(); + assert!(!p.bin("a").is_file()); + assert!(!p.bin("b").is_file()); + assert!(p.bin("examples/a").is_file()); + assert!(p.bin("examples/b").is_file()); +} + +#[cargo_test] +fn ignore_dotfile() { + let p = project() + .file("src/bin/.a.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .build(); + + p.cargo("build").run(); +} + +#[cargo_test] +fn ignore_dotdirs() { + let p = project() + .file("src/bin/a.rs", "fn main() {}") + .file(".git/Cargo.toml", "") + .file(".pc/dummy-fix.patch/Cargo.toml", "") + .build(); + + p.cargo("build").run(); +} + +#[cargo_test] +fn dotdir_root() { + let p = ProjectBuilder::new(root().join(".foo")) + .file("src/bin/a.rs", "fn main() {}") + .build(); + p.cargo("build").run(); +} + +#[cargo_test] +fn custom_target_dir_env() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + let exe_name = format!("foo{}", env::consts::EXE_SUFFIX); + + p.cargo("build").env("CARGO_TARGET_DIR", "foo/target").run(); + assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); + assert!(!p.root().join("target/debug").join(&exe_name).is_file()); + + p.cargo("build").run(); + assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); + assert!(p.root().join("target/debug").join(&exe_name).is_file()); + + p.cargo("build") + .env("CARGO_BUILD_TARGET_DIR", "foo2/target") + .run(); + assert!(p.root().join("foo2/target/debug").join(&exe_name).is_file()); + + p.change_file( + ".cargo/config", + r#" + [build] + target-dir = "foo/target" + "#, + ); + p.cargo("build").env("CARGO_TARGET_DIR", "bar/target").run(); + assert!(p.root().join("bar/target/debug").join(&exe_name).is_file()); + assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); + assert!(p.root().join("target/debug").join(&exe_name).is_file()); +} + +#[cargo_test] +fn custom_target_dir_line_parameter() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + let exe_name = format!("foo{}", env::consts::EXE_SUFFIX); + + p.cargo("build --target-dir foo/target").run(); + assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); + assert!(!p.root().join("target/debug").join(&exe_name).is_file()); + + p.cargo("build").run(); + assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); + assert!(p.root().join("target/debug").join(&exe_name).is_file()); + + p.change_file( + ".cargo/config", + r#" + [build] + target-dir = "foo/target" + "#, + ); + p.cargo("build --target-dir bar/target").run(); + assert!(p.root().join("bar/target/debug").join(&exe_name).is_file()); + assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); + assert!(p.root().join("target/debug").join(&exe_name).is_file()); + + p.cargo("build --target-dir foobar/target") + .env("CARGO_TARGET_DIR", "bar/target") + .run(); + assert!(p + .root() + .join("foobar/target/debug") + .join(&exe_name) + .is_file()); + assert!(p.root().join("bar/target/debug").join(&exe_name).is_file()); + assert!(p.root().join("foo/target/debug").join(&exe_name).is_file()); + assert!(p.root().join("target/debug").join(&exe_name).is_file()); +} + +#[cargo_test] +fn build_multiple_packages() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.d1] + path = "d1" + [dependencies.d2] + path = "d2" + + [[bin]] + name = "foo" + "#, + ) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .file("d1/Cargo.toml", &basic_bin_manifest("d1")) + .file("d1/src/lib.rs", "") + .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }") + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.1" + authors = [] + + [[bin]] + name = "d2" + doctest = false + "#, + ) + .file("d2/src/main.rs", "fn main() { println!(\"d2\"); }") + .build(); + + p.cargo("build -p d1 -p d2 -p foo").run(); + + assert!(p.bin("foo").is_file()); + p.process(&p.bin("foo")).with_stdout("i am foo\n").run(); + + let d1_path = &p + .build_dir() + .join("debug") + .join(format!("d1{}", env::consts::EXE_SUFFIX)); + let d2_path = &p + .build_dir() + .join("debug") + .join(format!("d2{}", env::consts::EXE_SUFFIX)); + + assert!(d1_path.is_file()); + p.process(d1_path).with_stdout("d1").run(); + + assert!(d2_path.is_file()); + p.process(d2_path).with_stdout("d2").run(); +} + +#[cargo_test] +fn invalid_spec() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.d1] + path = "d1" + + [[bin]] + name = "foo" + "#, + ) + .file("src/bin/foo.rs", &main_file(r#""i am foo""#, &[])) + .file("d1/Cargo.toml", &basic_bin_manifest("d1")) + .file("d1/src/lib.rs", "") + .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }") + .build(); + + p.cargo("build -p notAValidDep") + .with_status(101) + .with_stderr("[ERROR] package ID specification `notAValidDep` did not match any packages") + .run(); + + p.cargo("build -p d1 -p notAValidDep") + .with_status(101) + .with_stderr("[ERROR] package ID specification `notAValidDep` did not match any packages") + .run(); +} + +#[cargo_test] +fn manifest_with_bom_is_ok() { + let p = project() + .file( + "Cargo.toml", + "\u{FEFF} + [package] + name = \"foo\" + version = \"0.0.1\" + authors = [] + ", + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build -v").run(); +} + +#[cargo_test] +fn panic_abort_compiles_with_panic_abort() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.dev] + panic = 'abort' + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build -v") + .with_stderr_contains("[..] -C panic=abort [..]") + .run(); +} + +#[cargo_test] +fn compiler_json_error_format() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + "#, + ) + .file( + "build.rs", + "fn main() { println!(\"cargo:rustc-cfg=xyz\") }", + ) + .file("src/main.rs", "fn main() { let unused = 92; }") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("bar/src/lib.rs", r#"fn dead() {}"#) + .build(); + + let output = |fresh| { + r#" + { + "reason":"compiler-artifact", + "package_id":"foo 0.5.0 ([..])", + "manifest_path": "[..]", + "target":{ + "kind":["custom-build"], + "crate_types":["bin"], + "doc": false, + "doctest": false, + "edition": "2015", + "name":"build-script-build", + "src_path":"[..]build.rs", + "test": false + }, + "profile": { + "debug_assertions": true, + "debuginfo": null, + "opt_level": "0", + "overflow_checks": true, + "test": false + }, + "executable": null, + "features": [], + "filenames": "{...}", + "fresh": $FRESH + } + + { + "reason":"compiler-message", + "package_id":"bar 0.5.0 ([..])", + "manifest_path": "[..]", + "target":{ + "kind":["lib"], + "crate_types":["lib"], + "doc": true, + "doctest": true, + "edition": "2015", + "name":"bar", + "src_path":"[..]lib.rs", + "test": true + }, + "message":"{...}" + } + + { + "reason":"compiler-artifact", + "profile": { + "debug_assertions": true, + "debuginfo": 2, + "opt_level": "0", + "overflow_checks": true, + "test": false + }, + "executable": null, + "features": [], + "package_id":"bar 0.5.0 ([..])", + "manifest_path": "[..]", + "target":{ + "kind":["lib"], + "crate_types":["lib"], + "doc": true, + "doctest": true, + "edition": "2015", + "name":"bar", + "src_path":"[..]lib.rs", + "test": true + }, + "filenames":[ + "[..].rlib", + "[..].rmeta" + ], + "fresh": $FRESH + } + + { + "reason":"build-script-executed", + "package_id":"foo 0.5.0 ([..])", + "linked_libs":[], + "linked_paths":[], + "env":[], + "cfgs":["xyz"], + "out_dir": "[..]target/debug/build/foo-[..]/out" + } + + { + "reason":"compiler-message", + "package_id":"foo 0.5.0 ([..])", + "manifest_path": "[..]", + "target":{ + "kind":["bin"], + "crate_types":["bin"], + "doc": true, + "doctest": false, + "edition": "2015", + "name":"foo", + "src_path":"[..]main.rs", + "test": true + }, + "message":"{...}" + } + + { + "reason":"compiler-artifact", + "package_id":"foo 0.5.0 ([..])", + "manifest_path": "[..]", + "target":{ + "kind":["bin"], + "crate_types":["bin"], + "doc": true, + "doctest": false, + "edition": "2015", + "name":"foo", + "src_path":"[..]main.rs", + "test": true + }, + "profile": { + "debug_assertions": true, + "debuginfo": 2, + "opt_level": "0", + "overflow_checks": true, + "test": false + }, + "executable": "[..]/foo/target/debug/foo[EXE]", + "features": [], + "filenames": "{...}", + "fresh": $FRESH + } + + {"reason": "build-finished", "success": true} + "# + .replace("$FRESH", fresh) + }; + + // Use `jobs=1` to ensure that the order of messages is consistent. + p.cargo("build -v --message-format=json --jobs=1") + .with_json_contains_unordered(&output("false")) + .run(); + + // With fresh build, we should repeat the artifacts, + // and replay the cached compiler warnings. + p.cargo("build -v --message-format=json --jobs=1") + .with_json_contains_unordered(&output("true")) + .run(); +} + +#[cargo_test] +fn wrong_message_format_option() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --message-format XML") + .with_status(101) + .with_stderr_contains( + "\ +error: invalid message format specifier: `xml` +", + ) + .run(); +} + +#[cargo_test] +fn message_format_json_forward_stderr() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() { let unused = 0; }") + .build(); + + p.cargo("rustc --release --bin foo --message-format JSON") + .with_json_contains_unordered( + r#" + { + "reason":"compiler-message", + "package_id":"foo 0.5.0 ([..])", + "manifest_path": "[..]", + "target":{ + "kind":["bin"], + "crate_types":["bin"], + "doc": true, + "doctest": false, + "edition": "2015", + "name":"foo", + "src_path":"[..]", + "test": true + }, + "message":"{...}" + } + + { + "reason":"compiler-artifact", + "package_id":"foo 0.5.0 ([..])", + "manifest_path": "[..]", + "target":{ + "kind":["bin"], + "crate_types":["bin"], + "doc": true, + "doctest": false, + "edition": "2015", + "name":"foo", + "src_path":"[..]", + "test": true + }, + "profile":{ + "debug_assertions":false, + "debuginfo":null, + "opt_level":"3", + "overflow_checks": false, + "test":false + }, + "executable": "{...}", + "features":[], + "filenames": "{...}", + "fresh": false + } + + {"reason": "build-finished", "success": true} + "#, + ) + .run(); +} + +#[cargo_test] +fn no_warn_about_package_metadata() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [package.metadata] + foo = "bar" + a = true + b = 3 + + [package.metadata.another] + bar = 3 + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build") + .with_stderr( + "[..] foo v0.0.1 ([..])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn no_warn_about_workspace_metadata() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + + [workspace.metadata] + something = "something_else" + x = 1 + y = 2 + + [workspace.metadata.another] + bar = 12 + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("build") + .with_stderr( + "[..] foo v0.0.1 ([..])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn cargo_build_empty_target() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --target") + .arg("") + .with_status(101) + .with_stderr_contains("[..] target was empty") + .run(); +} + +#[cargo_test] +fn build_all_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("build --workspace") + .with_stderr( + "\ +[COMPILING] bar v0.1.0 ([..]) +[COMPILING] foo v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_all_exclude() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") + .build(); + + p.cargo("build --workspace --exclude baz") + .with_stderr_does_not_contain("[COMPILING] baz v0.1.0 [..]") + .with_stderr_unordered( + "\ +[COMPILING] foo v0.1.0 ([..]) +[COMPILING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_all_exclude_not_found() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("build --workspace --exclude baz") + .with_stderr_does_not_contain("[COMPILING] baz v0.1.0 [..]") + .with_stderr_unordered( + "\ +[WARNING] excluded package(s) `baz` not found in workspace [..] +[COMPILING] foo v0.1.0 ([..]) +[COMPILING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_all_exclude_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") + .build(); + + p.cargo("build --workspace --exclude '*z'") + .with_stderr_does_not_contain("[COMPILING] baz v0.1.0 [..]") + .with_stderr_unordered( + "\ +[COMPILING] foo v0.1.0 ([..]) +[COMPILING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_all_exclude_glob_not_found() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("build --workspace --exclude '*z'") + .with_stderr_does_not_contain("[COMPILING] baz v0.1.0 [..]") + .with_stderr( + "\ +[WARNING] excluded package pattern(s) `*z` not found in workspace [..] +[COMPILING] [..] v0.1.0 ([..]) +[COMPILING] [..] v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_all_exclude_broken_glob() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + p.cargo("build --workspace --exclude '[*z'") + .with_status(101) + .with_stderr_contains("[ERROR] cannot build glob pattern from `[*z`") + .run(); +} + +#[cargo_test] +fn build_all_workspace_implicit_examples() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#, + ) + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("src/bin/b.rs", "fn main() {}") + .file("examples/c.rs", "fn main() {}") + .file("examples/d.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .file("bar/src/bin/e.rs", "fn main() {}") + .file("bar/src/bin/f.rs", "fn main() {}") + .file("bar/examples/g.rs", "fn main() {}") + .file("bar/examples/h.rs", "fn main() {}") + .build(); + + p.cargo("build --workspace --examples") + .with_stderr( + "[..] Compiling bar v0.1.0 ([..])\n\ + [..] Compiling foo v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); + assert!(!p.bin("a").is_file()); + assert!(!p.bin("b").is_file()); + assert!(p.bin("examples/c").is_file()); + assert!(p.bin("examples/d").is_file()); + assert!(!p.bin("e").is_file()); + assert!(!p.bin("f").is_file()); + assert!(p.bin("examples/g").is_file()); + assert!(p.bin("examples/h").is_file()); +} + +#[cargo_test] +fn build_all_virtual_manifest() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + // The order in which bar and baz are built is not guaranteed + p.cargo("build --workspace") + .with_stderr_unordered( + "\ +[COMPILING] baz v0.1.0 ([..]) +[COMPILING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_virtual_manifest_all_implied() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + // The order in which `bar` and `baz` are built is not guaranteed. + p.cargo("build") + .with_stderr_unordered( + "\ +[COMPILING] baz v0.1.0 ([..]) +[COMPILING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_virtual_manifest_one_project() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") + .build(); + + p.cargo("build -p bar") + .with_stderr_does_not_contain("[..]baz[..]") + .with_stderr( + "\ +[COMPILING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_virtual_manifest_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("build -p '*z'") + .with_stderr_does_not_contain("[..]bar[..]") + .with_stderr( + "\ +[COMPILING] baz v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_virtual_manifest_glob_not_found() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("build -p bar -p '*z'") + .with_status(101) + .with_stderr("[ERROR] package pattern(s) `*z` not found in workspace [..]") + .run(); +} + +#[cargo_test] +fn build_virtual_manifest_broken_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("build -p '[*z'") + .with_status(101) + .with_stderr_contains("[ERROR] cannot build glob pattern from `[*z`") + .run(); +} + +#[cargo_test] +fn build_all_virtual_manifest_implicit_examples() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .file("bar/src/bin/a.rs", "fn main() {}") + .file("bar/src/bin/b.rs", "fn main() {}") + .file("bar/examples/c.rs", "fn main() {}") + .file("bar/examples/d.rs", "fn main() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "") + .file("baz/src/bin/e.rs", "fn main() {}") + .file("baz/src/bin/f.rs", "fn main() {}") + .file("baz/examples/g.rs", "fn main() {}") + .file("baz/examples/h.rs", "fn main() {}") + .build(); + + // The order in which bar and baz are built is not guaranteed + p.cargo("build --workspace --examples") + .with_stderr_unordered( + "\ +[COMPILING] baz v0.1.0 ([..]) +[COMPILING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + assert!(!p.bin("a").is_file()); + assert!(!p.bin("b").is_file()); + assert!(p.bin("examples/c").is_file()); + assert!(p.bin("examples/d").is_file()); + assert!(!p.bin("e").is_file()); + assert!(!p.bin("f").is_file()); + assert!(p.bin("examples/g").is_file()); + assert!(p.bin("examples/h").is_file()); +} + +#[cargo_test] +fn build_all_member_dependency_same_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + a = "0.1.0" + "#, + ) + .file("a/src/lib.rs", "pub fn a() {}") + .build(); + + Package::new("a", "0.1.0").publish(); + + p.cargo("build --workspace") + .with_stderr( + "[UPDATING] `[..]` index\n\ + [DOWNLOADING] crates ...\n\ + [DOWNLOADED] a v0.1.0 ([..])\n\ + [COMPILING] a v0.1.0\n\ + [COMPILING] a v0.1.0 ([..])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn run_proper_binary() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + [[bin]] + name = "main" + [[bin]] + name = "other" + "#, + ) + .file("src/lib.rs", "") + .file( + "src/bin/main.rs", + r#"fn main() { panic!("This should never be run."); }"#, + ) + .file("src/bin/other.rs", "fn main() {}") + .build(); + + p.cargo("run --bin other").run(); +} + +#[cargo_test] +fn run_proper_binary_main_rs() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/lib.rs", "") + .file("src/bin/main.rs", "fn main() {}") + .build(); + + p.cargo("run --bin foo").run(); +} + +#[cargo_test] +fn run_proper_alias_binary_from_src() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + [[bin]] + name = "foo" + [[bin]] + name = "bar" + "#, + ) + .file("src/foo.rs", r#"fn main() { println!("foo"); }"#) + .file("src/bar.rs", r#"fn main() { println!("bar"); }"#) + .build(); + + p.cargo("build --workspace").run(); + p.process(&p.bin("foo")).with_stdout("foo\n").run(); + p.process(&p.bin("bar")).with_stdout("bar\n").run(); +} + +#[cargo_test] +fn run_proper_alias_binary_main_rs() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + [[bin]] + name = "foo" + [[bin]] + name = "bar" + "#, + ) + .file("src/main.rs", r#"fn main() { println!("main"); }"#) + .build(); + + p.cargo("build --workspace").run(); + p.process(&p.bin("foo")).with_stdout("main\n").run(); + p.process(&p.bin("bar")).with_stdout("main\n").run(); +} + +#[cargo_test] +fn run_proper_binary_main_rs_as_foo() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/foo.rs", + r#" fn main() { panic!("This should never be run."); }"#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("run --bin foo").run(); +} + +#[cargo_test] +fn rustc_wrapper() { + let p = project().file("src/lib.rs", "").build(); + let wrapper = tools::echo_wrapper(); + let running = format!( + "[RUNNING] `{} rustc --crate-name foo [..]", + wrapper.display() + ); + p.cargo("build -v") + .env("RUSTC_WRAPPER", &wrapper) + .with_stderr_contains(&running) + .run(); + p.build_dir().rm_rf(); + p.cargo("build -v") + .env("RUSTC_WORKSPACE_WRAPPER", &wrapper) + .with_stderr_contains(&running) + .run(); +} + +#[cargo_test] +fn rustc_wrapper_relative() { + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + let wrapper = tools::echo_wrapper(); + let exe_name = wrapper.file_name().unwrap().to_str().unwrap(); + let relative_path = format!("./{}", exe_name); + fs::hard_link(&wrapper, p.root().join(exe_name)).unwrap(); + let running = format!("[RUNNING] `[ROOT]/foo/./{} rustc[..]", exe_name); + p.cargo("build -v") + .env("RUSTC_WRAPPER", &relative_path) + .with_stderr_contains(&running) + .run(); + p.build_dir().rm_rf(); + p.cargo("build -v") + .env("RUSTC_WORKSPACE_WRAPPER", &relative_path) + .with_stderr_contains(&running) + .run(); + p.build_dir().rm_rf(); + p.change_file( + ".cargo/config.toml", + &format!( + r#" + build.rustc-wrapper = "./{}" + "#, + exe_name + ), + ); + p.cargo("build -v").with_stderr_contains(&running).run(); +} + +#[cargo_test] +fn rustc_wrapper_from_path() { + let p = project().file("src/lib.rs", "").build(); + p.cargo("build -v") + .env("RUSTC_WRAPPER", "wannabe_sccache") + .with_status(101) + .with_stderr_contains("[..]`wannabe_sccache rustc [..]") + .run(); + p.build_dir().rm_rf(); + p.cargo("build -v") + .env("RUSTC_WORKSPACE_WRAPPER", "wannabe_sccache") + .with_status(101) + .with_stderr_contains("[..]`wannabe_sccache rustc [..]") + .run(); +} + +#[cargo_test] +fn cdylib_not_lifted() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.1.0" + + [lib] + crate-type = ["cdylib"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + let files = if cfg!(windows) { + if cfg!(target_env = "msvc") { + vec!["foo.dll.lib", "foo.dll.exp", "foo.dll"] + } else { + vec!["libfoo.dll.a", "foo.dll"] + } + } else if cfg!(target_os = "macos") { + vec!["libfoo.dylib"] + } else { + vec!["libfoo.so"] + }; + + for file in files { + println!("checking: {}", file); + assert!(p.root().join("target/debug/deps").join(&file).is_file()); + } +} + +#[cargo_test] +fn cdylib_final_outputs() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo-bar" + authors = [] + version = "0.1.0" + + [lib] + crate-type = ["cdylib"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + let files = if cfg!(windows) { + if cfg!(target_env = "msvc") { + vec!["foo_bar.dll.lib", "foo_bar.dll"] + } else { + vec!["foo_bar.dll", "libfoo_bar.dll.a"] + } + } else if cfg!(target_os = "macos") { + vec!["libfoo_bar.dylib"] + } else { + vec!["libfoo_bar.so"] + }; + + for file in files { + println!("checking: {}", file); + assert!(p.root().join("target/debug").join(&file).is_file()); + } +} + +#[cargo_test] +// NOTE: Windows MSVC and wasm32-unknown-emscripten do not use metadata. Skip them. +// See +#[cfg(not(all(target_os = "windows", target_env = "msvc")))] +fn no_dep_info_collision_when_cdylib_and_bin_coexist() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [lib] + crate-type = ["cdylib"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr_unordered( + "\ +[COMPILING] foo v1.0.0 ([CWD]) +[RUNNING] `rustc [..] --crate-type bin [..] -C metadata=[..]` +[RUNNING] `rustc [..] --crate-type cdylib [..] -C metadata=[..]` +[FINISHED] [..] +", + ) + .run(); + + let deps_dir = p.target_debug_dir().join("deps"); + assert!(deps_dir.join("foo.d").exists()); + let dep_info_count = deps_dir + .read_dir() + .unwrap() + .filter(|e| { + let filename = e.as_ref().unwrap().file_name(); + let filename = filename.to_str().unwrap(); + filename.starts_with("foo") && filename.ends_with(".d") + }) + .count(); + // cdylib -> foo.d + // bin -> foo-.d + assert_eq!(dep_info_count, 2); +} + +#[cargo_test] +fn deterministic_cfg_flags() { + // This bug is non-deterministic. + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + build = "build.rs" + + [features] + default = ["f_a", "f_b", "f_c", "f_d"] + f_a = [] + f_b = [] + f_c = [] + f_d = [] + "#, + ) + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-cfg=cfg_a"); + println!("cargo:rustc-cfg=cfg_b"); + println!("cargo:rustc-cfg=cfg_c"); + println!("cargo:rustc-cfg=cfg_d"); + println!("cargo:rustc-cfg=cfg_e"); + } + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.1.0 [..] +[RUNNING] [..] +[RUNNING] [..] +[RUNNING] `rustc --crate-name foo [..] \ +--cfg[..]default[..]--cfg[..]f_a[..]--cfg[..]f_b[..]\ +--cfg[..]f_c[..]--cfg[..]f_d[..] \ +--cfg cfg_a --cfg cfg_b --cfg cfg_c --cfg cfg_d --cfg cfg_e` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn explicit_bins_without_paths() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [[bin]] + name = "foo" + + [[bin]] + name = "bar" + "#, + ) + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .file("src/bin/bar.rs", "fn main() {}") + .build(); + + p.cargo("build").run(); +} + +#[cargo_test] +fn no_bin_in_src_with_lib() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/lib.rs", "") + .file("src/foo.rs", "fn main() {}") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr_contains( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + can't find `foo` bin at `src/bin/foo.rs` or `src/bin/foo/main.rs`. [..]", + ) + .run(); +} + +#[cargo_test] +fn inferred_bins() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("src/bin/bar.rs", "fn main() {}") + .file("src/bin/baz/main.rs", "fn main() {}") + .build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + assert!(p.bin("bar").is_file()); + assert!(p.bin("baz").is_file()); +} + +#[cargo_test] +fn inferred_bins_duplicate_name() { + // this should fail, because we have two binaries with the same name + let p = project() + .file("src/main.rs", "fn main() {}") + .file("src/bin/bar.rs", "fn main() {}") + .file("src/bin/bar/main.rs", "fn main() {}") + .build(); + + p.cargo("build").with_status(101).with_stderr_contains( + "[..]found duplicate binary name bar, but all binary targets must have a unique name[..]", + ) + .run(); +} + +#[cargo_test] +fn inferred_bin_path() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [[bin]] + name = "bar" + # Note, no `path` key! + "#, + ) + .file("src/bin/bar/main.rs", "fn main() {}") + .build(); + + p.cargo("build").run(); + assert!(p.bin("bar").is_file()); +} + +#[cargo_test] +fn inferred_examples() { + let p = project() + .file("src/lib.rs", "fn main() {}") + .file("examples/bar.rs", "fn main() {}") + .file("examples/baz/main.rs", "fn main() {}") + .build(); + + p.cargo("build --examples").run(); + assert!(p.bin("examples/bar").is_file()); + assert!(p.bin("examples/baz").is_file()); +} + +#[cargo_test] +fn inferred_tests() { + let p = project() + .file("src/lib.rs", "fn main() {}") + .file("tests/bar.rs", "fn main() {}") + .file("tests/baz/main.rs", "fn main() {}") + .build(); + + p.cargo("test --test=bar --test=baz").run(); +} + +#[cargo_test] +fn inferred_benchmarks() { + let p = project() + .file("src/lib.rs", "fn main() {}") + .file("benches/bar.rs", "fn main() {}") + .file("benches/baz/main.rs", "fn main() {}") + .build(); + + p.cargo("bench --bench=bar --bench=baz").run(); +} + +#[cargo_test] +fn no_infer_dirs() { + let p = project() + .file("src/lib.rs", "fn main() {}") + .file("examples/dir.rs/dummy", "") + .file("benches/dir.rs/dummy", "") + .file("tests/dir.rs/dummy", "") + .build(); + + p.cargo("build --examples --benches --tests").run(); // should not fail with "is a directory" +} + +#[cargo_test] +fn target_edition() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [lib] + edition = "2018" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..]--edition=2018 [..] +", + ) + .run(); +} + +#[cargo_test] +fn target_edition_override() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + edition = "2018" + + [lib] + edition = "2015" + "#, + ) + .file( + "src/lib.rs", + " + pub fn async() {} + pub fn try() {} + pub fn await() {} + ", + ) + .build(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn same_metadata_different_directory() { + // A top-level crate built in two different workspaces should have the + // same metadata hash. + let p = project() + .at("foo1") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + let output = t!(String::from_utf8( + t!(p.cargo("build -v").exec_with_output()).stderr, + )); + let metadata = output + .split_whitespace() + .find(|arg| arg.starts_with("metadata=")) + .unwrap(); + + let p = project() + .at("foo2") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build -v") + .with_stderr_contains(format!("[..]{}[..]", metadata)) + .run(); +} + +#[cargo_test] +fn building_a_dependent_crate_without_bin_should_fail() { + Package::new("testless", "0.1.0") + .file( + "Cargo.toml", + r#" + [package] + name = "testless" + version = "0.1.0" + + [[bin]] + name = "a_bin" + "#, + ) + .file("src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + testless = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr_contains( + "[..]can't find `a_bin` bin at `src/bin/a_bin.rs` or `src/bin/a_bin/main.rs`[..]", + ) + .run(); +} + +#[cargo_test] +#[cfg(any(target_os = "macos", target_os = "ios"))] +fn uplift_dsym_of_bin_on_mac() { + let p = project() + .file("src/main.rs", "fn main() { panic!(); }") + .file("src/bin/b.rs", "fn main() { panic!(); }") + .file("examples/c.rs", "fn main() { panic!(); }") + .file("tests/d.rs", "fn main() { panic!(); }") + .build(); + + p.cargo("build --bins --examples --tests") + .enable_mac_dsym() + .run(); + assert!(p.target_debug_dir().join("foo.dSYM").is_dir()); + assert!(p.target_debug_dir().join("b.dSYM").is_dir()); + assert!(p.target_debug_dir().join("b.dSYM").is_symlink()); + assert!(p.target_debug_dir().join("examples/c.dSYM").is_dir()); + assert!(!p.target_debug_dir().join("c.dSYM").exists()); + assert!(!p.target_debug_dir().join("d.dSYM").exists()); +} + +#[cargo_test] +#[cfg(any(target_os = "macos", target_os = "ios"))] +fn uplift_dsym_of_bin_on_mac_when_broken_link_exists() { + let p = project() + .file("src/main.rs", "fn main() { panic!(); }") + .build(); + let dsym = p.target_debug_dir().join("foo.dSYM"); + + p.cargo("build").enable_mac_dsym().run(); + assert!(dsym.is_dir()); + + // Simulate the situation where the underlying dSYM bundle goes missing + // but the uplifted symlink to it remains. This would previously cause + // builds to permanently fail until the bad symlink was manually removed. + dsym.rm_rf(); + p.symlink( + p.target_debug_dir() + .join("deps") + .join("foo-baaaaaadbaaaaaad.dSYM"), + &dsym, + ); + assert!(dsym.is_symlink()); + assert!(!dsym.exists()); + + p.cargo("build").enable_mac_dsym().run(); + assert!(dsym.is_dir()); +} + +#[cargo_test] +#[cfg(all(target_os = "windows", target_env = "msvc"))] +fn uplift_pdb_of_bin_on_windows() { + let p = project() + .file("src/main.rs", "fn main() { panic!(); }") + .file("src/bin/b.rs", "fn main() { panic!(); }") + .file("src/bin/foo-bar.rs", "fn main() { panic!(); }") + .file("examples/c.rs", "fn main() { panic!(); }") + .file("tests/d.rs", "fn main() { panic!(); }") + .build(); + + p.cargo("build --bins --examples --tests").run(); + assert!(p.target_debug_dir().join("foo.pdb").is_file()); + assert!(p.target_debug_dir().join("b.pdb").is_file()); + assert!(p.target_debug_dir().join("examples/c.pdb").exists()); + assert!(p.target_debug_dir().join("foo-bar.exe").is_file()); + assert!(p.target_debug_dir().join("foo_bar.pdb").is_file()); + assert!(!p.target_debug_dir().join("c.pdb").exists()); + assert!(!p.target_debug_dir().join("d.pdb").exists()); +} + +#[cargo_test] +#[cfg(target_os = "linux")] +fn uplift_dwp_of_bin_on_linux() { + let p = project() + .file("src/main.rs", "fn main() { panic!(); }") + .file("src/bin/b.rs", "fn main() { panic!(); }") + .file("src/bin/foo-bar.rs", "fn main() { panic!(); }") + .file("examples/c.rs", "fn main() { panic!(); }") + .file("tests/d.rs", "fn main() { panic!(); }") + .build(); + + p.cargo("build --bins --examples --tests") + .enable_split_debuginfo_packed() + .run(); + assert!(p.target_debug_dir().join("foo.dwp").is_file()); + assert!(p.target_debug_dir().join("b.dwp").is_file()); + assert!(p.target_debug_dir().join("examples/c.dwp").exists()); + assert!(p.target_debug_dir().join("foo-bar").is_file()); + assert!(p.target_debug_dir().join("foo-bar.dwp").is_file()); + assert!(!p.target_debug_dir().join("c.dwp").exists()); + assert!(!p.target_debug_dir().join("d.dwp").exists()); +} + +// Ensure that `cargo build` chooses the correct profile for building +// targets based on filters (assuming `--profile` is not specified). +#[cargo_test] +fn build_filter_infer_profile() { + let p = project() + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .file("tests/t1.rs", "") + .file("benches/b1.rs", "") + .file("examples/ex1.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin \ + --emit=[..]link[..]", + ) + .run(); + + p.root().join("target").rm_rf(); + p.cargo("build -v --test=t1") + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]-C debuginfo=2 [..]", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name t1 tests/t1.rs [..]--emit=[..]link[..]\ + -C debuginfo=2 [..]", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin \ + --emit=[..]link[..]-C debuginfo=2 [..]", + ) + .run(); + + p.root().join("target").rm_rf(); + // Bench uses test profile without `--release`. + p.cargo("build -v --bench=b1") + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]-C debuginfo=2 [..]", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name b1 benches/b1.rs [..]--emit=[..]link[..]\ + -C debuginfo=2 [..]", + ) + .with_stderr_does_not_contain("opt-level") + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin \ + --emit=[..]link[..]-C debuginfo=2 [..]", + ) + .run(); +} + +#[cargo_test] +fn targets_selected_default() { + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("build -v") + // Binaries. + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin \ + --emit=[..]link[..]", + ) + // Benchmarks. + .with_stderr_does_not_contain( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link \ + -C opt-level=3 --test [..]", + ) + // Unit tests. + .with_stderr_does_not_contain( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]\ + -C debuginfo=2 --test [..]", + ) + .run(); +} + +#[cargo_test] +fn targets_selected_all() { + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("build -v --all-targets") + // Binaries. + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin \ + --emit=[..]link[..]", + ) + // Unit tests. + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]\ + -C debuginfo=2 --test [..]", + ) + .run(); +} + +#[cargo_test] +fn all_targets_no_lib() { + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("build -v --all-targets") + // Binaries. + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin \ + --emit=[..]link[..]", + ) + // Unit tests. + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]\ + -C debuginfo=2 --test [..]", + ) + .run(); +} + +#[cargo_test] +fn no_linkable_target() { + // Issue 3169: this is currently not an error as per discussion in PR #4797. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + [dependencies] + the_lib = { path = "the_lib" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "the_lib/Cargo.toml", + r#" + [package] + name = "the_lib" + version = "0.1.0" + [lib] + name = "the_lib" + crate-type = ["staticlib"] + "#, + ) + .file("the_lib/src/lib.rs", "pub fn foo() {}") + .build(); + p.cargo("build") + .with_stderr_contains( + "[WARNING] The package `the_lib` provides no linkable [..] \ + while compiling `foo`. [..] in `the_lib`'s Cargo.toml. [..]", + ) + .run(); +} + +#[cargo_test] +fn avoid_dev_deps() { + Package::new("foo", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dev-dependencies] + baz = "1.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] no matching package named `baz` found +location searched: registry `crates-io` +required by package `bar v0.1.0 ([..]/foo)` +", + ) + .run(); + p.cargo("build -Zavoid-dev-deps") + .masquerade_as_nightly_cargo(&["avoid-dev-deps"]) + .run(); +} + +#[cargo_test] +fn default_cargo_config_jobs() { + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [build] + jobs = 1 + "#, + ) + .build(); + p.cargo("build -v").run(); +} + +#[cargo_test] +fn good_cargo_config_jobs() { + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [build] + jobs = 4 + "#, + ) + .build(); + p.cargo("build -v").run(); +} + +#[cargo_test] +fn good_jobs() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build --jobs 1").run(); + + p.cargo("build --jobs -1").run(); +} + +#[cargo_test] +fn invalid_cargo_config_jobs() { + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [build] + jobs = 0 + "#, + ) + .build(); + p.cargo("build -v") + .with_status(101) + .with_stderr_contains("error: jobs may not be 0") + .run(); +} + +#[cargo_test] +fn invalid_jobs() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build --jobs 0") + .with_status(101) + .with_stderr_contains("error: jobs may not be 0") + .run(); + + p.cargo("build --jobs over9000") + .with_status(1) + .with_stderr("error: Invalid value: could not parse `over9000` as a number") + .run(); +} + +#[cargo_test] +fn target_filters_workspace() { + let ws = project() + .at("ws") + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file("a/Cargo.toml", &basic_lib_manifest("a")) + .file("a/src/lib.rs", "") + .file("a/examples/ex1.rs", "fn main() {}") + .file("b/Cargo.toml", &basic_bin_manifest("b")) + .file("b/src/lib.rs", "") + .file("b/src/main.rs", "fn main() {}") + .build(); + + ws.cargo("build -v --example ex") + .with_status(101) + .with_stderr( + "\ +[ERROR] no example target named `ex` + +Did you mean `ex1`?", + ) + .run(); + + ws.cargo("build -v --example 'ex??'") + .with_status(101) + .with_stderr( + "\ +[ERROR] no example target matches pattern `ex??` + +Did you mean `ex1`?", + ) + .run(); + + ws.cargo("build -v --lib") + .with_stderr_contains("[RUNNING] `rustc [..]a/src/lib.rs[..]") + .with_stderr_contains("[RUNNING] `rustc [..]b/src/lib.rs[..]") + .run(); + + ws.cargo("build -v --example ex1") + .with_stderr_contains("[RUNNING] `rustc [..]a/examples/ex1.rs[..]") + .run(); +} + +#[cargo_test] +fn target_filters_workspace_not_found() { + let ws = project() + .at("ws") + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file("a/Cargo.toml", &basic_bin_manifest("a")) + .file("a/src/main.rs", "fn main() {}") + .file("b/Cargo.toml", &basic_bin_manifest("b")) + .file("b/src/main.rs", "fn main() {}") + .build(); + + ws.cargo("build -v --lib") + .with_status(101) + .with_stderr("[ERROR] no library targets found in packages: a, b") + .run(); +} + +#[cfg(unix)] +#[cargo_test] +fn signal_display() { + // Cause the compiler to crash with a signal. + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + pm = { path = "pm" } + "#, + ) + .file( + "src/lib.rs", + r#" + #[macro_use] + extern crate pm; + + #[derive(Foo)] + pub struct S; + "#, + ) + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + [lib] + proc-macro = true + "#, + ) + .file( + "pm/src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro_derive(Foo)] + pub fn derive(_input: TokenStream) -> TokenStream { + std::process::abort() + } + "#, + ) + .build(); + + foo.cargo("build") + .with_stderr( + "\ +[COMPILING] pm [..] +[COMPILING] foo [..] +[ERROR] could not compile `foo` + +Caused by: + process didn't exit successfully: `rustc [..]` (signal: 6, SIGABRT: process abort signal) +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn tricky_pipelining() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + foo.cargo("build -p bar").run(); + foo.cargo("build -p foo").run(); +} + +#[cargo_test] +fn pipelining_works() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + foo.cargo("build") + .with_stdout("") + .with_stderr( + "\ +[COMPILING] [..] +[COMPILING] [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn pipelining_big_graph() { + // Create a crate graph of the form {a,b}{0..29}, where {a,b}(n) depend on {a,b}(n+1) + // Then have `foo`, a binary crate, depend on the whole thing. + let mut project = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + a1 = { path = "a1" } + b1 = { path = "b1" } + "#, + ) + .file("src/main.rs", "fn main(){}"); + + for n in 0..30 { + for x in &["a", "b"] { + project = project + .file( + &format!("{x}{n}/Cargo.toml", x = x, n = n), + &format!( + r#" + [package] + name = "{x}{n}" + version = "0.1.0" + [dependencies] + a{np1} = {{ path = "../a{np1}" }} + b{np1} = {{ path = "../b{np1}" }} + "#, + x = x, + n = n, + np1 = n + 1 + ), + ) + .file(&format!("{x}{n}/src/lib.rs", x = x, n = n), ""); + } + } + + let foo = project + .file("a30/Cargo.toml", &basic_lib_manifest("a30")) + .file( + "a30/src/lib.rs", + r#"compile_error!("don't actually build me");"#, + ) + .file("b30/Cargo.toml", &basic_lib_manifest("b30")) + .file("b30/src/lib.rs", "") + .build(); + foo.cargo("build -p foo") + .with_status(101) + .with_stderr_contains("[ERROR] could not compile `a30`[..]") + .run(); +} + +#[cargo_test] +fn forward_rustc_output() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = '2018' + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "bar::foo!();") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + [lib] + proc-macro = true + "#, + ) + .file( + "bar/src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::*; + + #[proc_macro] + pub fn foo(input: TokenStream) -> TokenStream { + println!("a"); + println!("b"); + println!("{{}}"); + eprintln!("c"); + eprintln!("d"); + eprintln!("{{a"); // "malformed json" + input + } + "#, + ) + .build(); + + foo.cargo("build") + .with_stdout("a\nb\n{}") + .with_stderr( + "\ +[COMPILING] [..] +[COMPILING] [..] +c +d +{a +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_lib_only() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("build --lib -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]-C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn build_with_no_lib() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --lib") + .with_status(101) + .with_stderr("[ERROR] no library targets found in package `foo`") + .run(); +} + +#[cargo_test] +fn build_with_relative_cargo_home_path() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.0.1" + authors = ["wycats@example.com"] + + [dependencies] + + "test-dependency" = { path = "src/test_dependency" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("src/test_dependency/src/lib.rs", r#" "#) + .file( + "src/test_dependency/Cargo.toml", + &basic_manifest("test-dependency", "0.0.1"), + ) + .build(); + + p.cargo("build").env("CARGO_HOME", "./cargo_home/").run(); +} + +#[cargo_test] +fn user_specific_cfgs_are_filtered_out() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", r#"fn main() {}"#) + .file( + "build.rs", + r#" + fn main() { + assert!(std::env::var_os("CARGO_CFG_PROC_MACRO").is_none()); + assert!(std::env::var_os("CARGO_CFG_DEBUG_ASSERTIONS").is_none()); + } + "#, + ) + .build(); + + p.cargo("rustc -- --cfg debug_assertions --cfg proc_macro") + .run(); + p.process(&p.bin("foo")).run(); +} + +#[cargo_test] +fn close_output() { + // What happens when stdout or stderr is closed during a build. + + // Server to know when rustc has spawned. + let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [lib] + proc-macro = true + + [[bin]] + name = "foobar" + "#, + ) + .file( + "src/lib.rs", + &r#" + use proc_macro::TokenStream; + use std::io::Read; + + #[proc_macro] + pub fn repro(_input: TokenStream) -> TokenStream { + println!("hello stdout!"); + eprintln!("hello stderr!"); + // Tell the test we have started. + let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap(); + // Wait for the test to tell us to start printing. + let mut buf = [0]; + drop(socket.read_exact(&mut buf)); + let use_stderr = std::env::var("__CARGO_REPRO_STDERR").is_ok(); + // Emit at least 1MB of data. + // Linux pipes can buffer up to 64KB. + // This test seems to be sensitive to having other threads + // calling fork. My hypothesis is that the stdout/stderr + // file descriptors are duplicated into the child process, + // and during the short window between fork and exec, the + // file descriptor is kept alive long enough for the + // build to finish. It's a half-baked theory, but this + // seems to prevent the spurious errors in CI. + // An alternative solution is to run this test in + // a single-threaded environment. + for i in 0..100000 { + if use_stderr { + eprintln!("0123456789{}", i); + } else { + println!("0123456789{}", i); + } + } + TokenStream::new() + } + "# + .replace("__ADDR__", &addr.to_string()), + ) + .file( + "src/bin/foobar.rs", + r#" + foo::repro!(); + + fn main() {} + "#, + ) + .build(); + + // The `stderr` flag here indicates if this should forcefully close stderr or stdout. + let spawn = |stderr: bool| { + let mut cmd = p.cargo("build").build_command(); + cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); + if stderr { + cmd.env("__CARGO_REPRO_STDERR", "1"); + } + let mut child = cmd.spawn().unwrap(); + // Wait for proc macro to start. + let pm_conn = listener.accept().unwrap().0; + // Close stderr or stdout. + if stderr { + drop(child.stderr.take()); + } else { + drop(child.stdout.take()); + } + // Tell the proc-macro to continue; + drop(pm_conn); + // Read the output from the other channel. + let out: &mut dyn Read = if stderr { + child.stdout.as_mut().unwrap() + } else { + child.stderr.as_mut().unwrap() + }; + let mut result = String::new(); + out.read_to_string(&mut result).unwrap(); + let status = child.wait().unwrap(); + assert!(!status.success()); + result + }; + + let stderr = spawn(false); + compare::match_unordered( + "\ +[COMPILING] foo [..] +hello stderr! +[ERROR] [..] +[WARNING] build failed, waiting for other jobs to finish... +", + &stderr, + None, + ) + .unwrap(); + + // Try again with stderr. + p.build_dir().rm_rf(); + let stdout = spawn(true); + assert_eq!(stdout, "hello stdout!\n"); +} + +#[cargo_test] +fn close_output_during_drain() { + // Test to close the output during the build phase (drain_the_queue). + // There was a bug where it would hang. + + // Server to know when rustc has spawned. + let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + // Create a wrapper so the test can know when compiling has started. + let rustc_wrapper = { + let p = project() + .at("compiler") + .file("Cargo.toml", &basic_manifest("compiler", "1.0.0")) + .file( + "src/main.rs", + &r#" + use std::process::Command; + use std::env; + use std::io::Read; + + fn main() { + // Only wait on the first dependency. + if matches!(env::var("CARGO_PKG_NAME").as_deref(), Ok("dep")) { + let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap(); + // Wait for the test to tell us to start printing. + let mut buf = [0]; + drop(socket.read_exact(&mut buf)); + } + let mut cmd = Command::new("rustc"); + for arg in env::args_os().skip(1) { + cmd.arg(arg); + } + std::process::exit(cmd.status().unwrap().code().unwrap()); + } + "# + .replace("__ADDR__", &addr.to_string()), + ) + .build(); + p.cargo("build").run(); + p.bin("compiler") + }; + + Package::new("dep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Spawn cargo, wait for the first rustc to start, and then close stderr. + let mut cmd = process(&cargo_exe()) + .arg("check") + .cwd(p.root()) + .env("RUSTC", rustc_wrapper) + .build_command(); + cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); + let mut child = cmd.spawn().expect("cargo should spawn"); + // Wait for the rustc wrapper to start. + let rustc_conn = listener.accept().unwrap().0; + // Close stderr to force an error. + drop(child.stderr.take()); + // Tell the wrapper to continue. + drop(rustc_conn); + match child.wait() { + Ok(status) => assert!(!status.success()), + Err(e) => panic!("child wait failed: {}", e), + } +} + +use cargo_test_support::registry::Dependency; + +#[cargo_test] +fn reduced_reproduction_8249() { + // https://github.com/rust-lang/cargo/issues/8249 + Package::new("a-src", "0.1.0").links("a").publish(); + Package::new("a-src", "0.2.0").links("a").publish(); + + Package::new("b", "0.1.0") + .add_dep(Dependency::new("a-src", "0.1").optional(true)) + .publish(); + Package::new("b", "0.2.0") + .add_dep(Dependency::new("a-src", "0.2").optional(true)) + .publish(); + + Package::new("c", "1.0.0") + .add_dep(&Dependency::new("b", "0.1.0")) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + b = { version = "*", features = ["a-src"] } + a-src = "*" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile").run(); + cargo_util::paths::append(&p.root().join("Cargo.toml"), b"c = \"*\"").unwrap(); + p.cargo("check").run(); + p.cargo("check").run(); +} + +#[cargo_test] +fn target_directory_backup_exclusion() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + // Newly created target/ should have CACHEDIR.TAG inside... + p.cargo("build").run(); + let cachedir_tag = p.build_dir().join("CACHEDIR.TAG"); + assert!(cachedir_tag.is_file()); + assert!(fs::read_to_string(&cachedir_tag) + .unwrap() + .starts_with("Signature: 8a477f597d28d172789f06886806bc55")); + // ...but if target/ already exists CACHEDIR.TAG should not be created in it. + fs::remove_file(&cachedir_tag).unwrap(); + p.cargo("build").run(); + assert!(!&cachedir_tag.is_file()); +} + +#[cargo_test] +fn simple_terminal_width() { + let p = project() + .file( + "src/lib.rs", + r#" + pub fn foo() { + let _: () = 42; + } + "#, + ) + .build(); + + p.cargo("build -v") + .env("__CARGO_TEST_TTY_WIDTH_DO_NOT_USE_THIS", "20") + .with_status(101) + .with_stderr_contains("[RUNNING] `rustc [..]--diagnostic-width=20[..]") + .run(); + + p.cargo("doc -v") + .env("__CARGO_TEST_TTY_WIDTH_DO_NOT_USE_THIS", "20") + .with_stderr_contains("[RUNNING] `rustdoc [..]--diagnostic-width=20[..]") + .run(); +} + +#[cargo_test] +fn build_script_o0_default() { + let p = project() + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + p.cargo("build -v --release") + .with_stderr_does_not_contain("[..]build_script_build[..]opt-level[..]") + .run(); +} + +#[cargo_test] +fn build_script_o0_default_even_with_release() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.release] + opt-level = 1 + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + p.cargo("build -v --release") + .with_stderr_does_not_contain("[..]build_script_build[..]opt-level[..]") + .run(); +} + +#[cargo_test] +fn primary_package_env_var() { + // Test that CARGO_PRIMARY_PACKAGE is enabled only for "foo" and not for any dependency. + + let is_primary_package = r#" + pub fn is_primary_package() -> bool {{ + option_env!("CARGO_PRIMARY_PACKAGE").is_some() + }} + "#; + + Package::new("qux", "0.1.0") + .file("src/lib.rs", is_primary_package) + .publish(); + + let baz = git::new("baz", |project| { + project + .file("Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("src/lib.rs", is_primary_package) + }); + + let foo = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {{ path = "bar" }} + baz = {{ git = '{}' }} + qux = "0.1" + "#, + baz.url() + ), + ) + .file( + "src/lib.rs", + &format!( + r#" + extern crate bar; + extern crate baz; + extern crate qux; + + {} + + #[test] + fn verify_primary_package() {{ + assert!(!bar::is_primary_package()); + assert!(!baz::is_primary_package()); + assert!(!qux::is_primary_package()); + assert!(is_primary_package()); + }} + "#, + is_primary_package + ), + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", is_primary_package) + .build(); + + foo.cargo("test").run(); +} + +#[cargo_test] +fn renamed_uplifted_artifact_remains_unmodified_after_rebuild() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build").run(); + + let bin = p.bin("foo"); + let renamed_bin = p.bin("foo-renamed"); + + fs::rename(&bin, &renamed_bin).unwrap(); + + p.change_file("src/main.rs", "fn main() { eprintln!(\"hello, world\"); }"); + p.cargo("build").run(); + + let not_the_same = !same_file::is_same_file(bin, renamed_bin).unwrap(); + assert!(not_the_same, "renamed uplifted artifact must be unmodified"); +} diff --git a/tests/testsuite/build_plan.rs b/tests/testsuite/build_plan.rs new file mode 100644 index 0000000..647bc12 --- /dev/null +++ b/tests/testsuite/build_plan.rs @@ -0,0 +1,222 @@ +//! Tests for --build-plan feature. + +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_bin_manifest, basic_manifest, main_file, project}; + +#[cargo_test] +fn cargo_build_plan_simple() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build --build-plan -Zunstable-options") + .masquerade_as_nightly_cargo(&["build-plan"]) + .with_json( + r#" + { + "inputs": [ + "[..]/foo/Cargo.toml" + ], + "invocations": [ + { + "args": "{...}", + "cwd": "[..]/cit/[..]/foo", + "deps": [], + "env": "{...}", + "kind": null, + "links": "{...}", + "outputs": "{...}", + "package_name": "foo", + "package_version": "0.5.0", + "program": "rustc", + "target_kind": ["bin"], + "compile_mode": "build" + } + ] + } + "#, + ) + .run(); + assert!(!p.bin("foo").is_file()); +} + +#[cargo_test] +fn cargo_build_plan_single_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.5.0" + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate bar; + pub fn foo() { bar::bar(); } + + #[test] + fn test() { foo(); } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + p.cargo("build --build-plan -Zunstable-options") + .masquerade_as_nightly_cargo(&["build-plan"]) + .with_json( + r#" + { + "inputs": [ + "[..]/foo/Cargo.toml", + "[..]/foo/bar/Cargo.toml" + ], + "invocations": [ + { + "args": "{...}", + "cwd": "[..]/cit/[..]/foo", + "deps": [], + "env": "{...}", + "kind": null, + "links": "{...}", + "outputs": [ + "[..]/foo/target/debug/deps/libbar-[..].rlib", + "[..]/foo/target/debug/deps/libbar-[..].rmeta" + ], + "package_name": "bar", + "package_version": "0.0.1", + "program": "rustc", + "target_kind": ["lib"], + "compile_mode": "build" + }, + { + "args": "{...}", + "cwd": "[..]/cit/[..]/foo", + "deps": [0], + "env": "{...}", + "kind": null, + "links": "{...}", + "outputs": [ + "[..]/foo/target/debug/deps/libfoo-[..].rlib", + "[..]/foo/target/debug/deps/libfoo-[..].rmeta" + ], + "package_name": "foo", + "package_version": "0.5.0", + "program": "rustc", + "target_kind": ["lib"], + "compile_mode": "build" + } + ] + } + "#, + ) + .run(); +} + +#[cargo_test] +fn cargo_build_plan_build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + "#, + ) + .file("src/main.rs", r#"fn main() {}"#) + .file("build.rs", r#"fn main() {}"#) + .build(); + + p.cargo("build --build-plan -Zunstable-options") + .masquerade_as_nightly_cargo(&["build-plan"]) + .with_json( + r#" + { + "inputs": [ + "[..]/foo/Cargo.toml" + ], + "invocations": [ + { + "args": "{...}", + "cwd": "[..]/cit/[..]/foo", + "deps": [], + "env": "{...}", + "kind": null, + "links": "{...}", + "outputs": "{...}", + "package_name": "foo", + "package_version": "0.5.0", + "program": "rustc", + "target_kind": ["custom-build"], + "compile_mode": "build" + }, + { + "args": "{...}", + "cwd": "[..]/cit/[..]/foo", + "deps": [0], + "env": "{...}", + "kind": null, + "links": "{...}", + "outputs": [], + "package_name": "foo", + "package_version": "0.5.0", + "program": "[..]/build-script-build", + "target_kind": ["custom-build"], + "compile_mode": "run-custom-build" + }, + { + "args": "{...}", + "cwd": "[..]/cit/[..]/foo", + "deps": [1], + "env": "{...}", + "kind": null, + "links": "{...}", + "outputs": "{...}", + "package_name": "foo", + "package_version": "0.5.0", + "program": "rustc", + "target_kind": ["bin"], + "compile_mode": "build" + } + ] + } + "#, + ) + .run(); +} + +#[cargo_test] +fn build_plan_with_dev_dep() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dev-dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build --build-plan -Zunstable-options") + .masquerade_as_nightly_cargo(&["build-plan"]) + .run(); +} diff --git a/tests/testsuite/build_script.rs b/tests/testsuite/build_script.rs new file mode 100644 index 0000000..ccccfca --- /dev/null +++ b/tests/testsuite/build_script.rs @@ -0,0 +1,5168 @@ +//! Tests for build.rs scripts. + +use cargo_test_support::compare::assert_match_exact; +use cargo_test_support::install::cargo_home; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::Package; +use cargo_test_support::tools; +use cargo_test_support::{ + basic_manifest, cargo_exe, cross_compile, is_coarse_mtime, project, project_in, +}; +use cargo_test_support::{rustc_host, sleep_ms, slow_cpu_multiplier, symlink_supported}; +use cargo_util::paths::{self, remove_dir_all}; +use std::env; +use std::fs; +use std::io; +use std::thread; + +#[cargo_test] +fn custom_build_script_failed() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("build.rs", "fn main() { std::process::exit(101); }") + .build(); + p.cargo("build -v") + .with_status(101) + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]` +[RUNNING] `[..]/build-script-build` +[ERROR] failed to run custom build command for `foo v0.5.0 ([CWD])` + +Caused by: + process didn't exit successfully: `[..]/build-script-build` (exit [..]: 101)", + ) + .run(); +} + +#[cargo_test] +fn custom_build_script_failed_backtraces_message() { + // In this situation (no dependency sharing), debuginfo is turned off in + // `dev.build-override`. However, if an error occurs running e.g. a build + // script, and backtraces are opted into: a message explaining how to + // improve backtraces is also displayed. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("build.rs", "fn main() { std::process::exit(101); }") + .build(); + p.cargo("build -v") + .env("RUST_BACKTRACE", "1") + .with_status(101) + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]` +[RUNNING] `[..]/build-script-build` +[ERROR] failed to run custom build command for `foo v0.5.0 ([CWD])` +note: To improve backtraces for build dependencies, set the \ +CARGO_PROFILE_DEV_BUILD_OVERRIDE_DEBUG=true environment variable [..] + +Caused by: + process didn't exit successfully: `[..]/build-script-build` (exit [..]: 101)", + ) + .run(); + + p.cargo("check -v") + .env("RUST_BACKTRACE", "1") + .with_status(101) + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `[..]/build-script-build` +[ERROR] failed to run custom build command for `foo v0.5.0 ([CWD])` +note: To improve backtraces for build dependencies, set the \ +CARGO_PROFILE_DEV_BUILD_OVERRIDE_DEBUG=true environment variable [..] + +Caused by: + process didn't exit successfully: `[..]/build-script-build` (exit [..]: 101)", + ) + .run(); +} + +#[cargo_test] +fn custom_build_script_failed_backtraces_message_with_debuginfo() { + // This is the same test as `custom_build_script_failed_backtraces_message` above, this time + // ensuring that the message dedicated to improving backtraces by requesting debuginfo is not + // shown when debuginfo is already turned on. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("build.rs", "fn main() { std::process::exit(101); }") + .build(); + p.cargo("build -v") + .env("RUST_BACKTRACE", "1") + .env("CARGO_PROFILE_DEV_BUILD_OVERRIDE_DEBUG", "true") + .with_status(101) + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]` +[RUNNING] `[..]/build-script-build` +[ERROR] failed to run custom build command for `foo v0.5.0 ([CWD])` + +Caused by: + process didn't exit successfully: `[..]/build-script-build` (exit [..]: 101)", + ) + .run(); +} + +#[cargo_test] +fn custom_build_env_vars() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [features] + bar_feat = ["bar/foo"] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + + [features] + foo = [] + "#, + ) + .file("bar/src/lib.rs", "pub fn hello() {}"); + + let cargo = cargo_exe().canonicalize().unwrap(); + let cargo = cargo.to_str().unwrap(); + let rustc = paths::resolve_executable("rustc".as_ref()) + .unwrap() + .canonicalize() + .unwrap(); + let rustc = rustc.to_str().unwrap(); + let file_content = format!( + r##" + use std::env; + use std::path::Path; + + fn main() {{ + let _target = env::var("TARGET").unwrap(); + let _ncpus = env::var("NUM_JOBS").unwrap(); + let _dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + let opt = env::var("OPT_LEVEL").unwrap(); + assert_eq!(opt, "0"); + + let opt = env::var("PROFILE").unwrap(); + assert_eq!(opt, "debug"); + + let debug = env::var("DEBUG").unwrap(); + assert_eq!(debug, "true"); + + let out = env::var("OUT_DIR").unwrap(); + assert!(out.starts_with(r"{0}")); + assert!(Path::new(&out).is_dir()); + + let _host = env::var("HOST").unwrap(); + + let _feat = env::var("CARGO_FEATURE_FOO").unwrap(); + + let cargo = env::var("CARGO").unwrap(); + if env::var_os("CHECK_CARGO_IS_RUSTC").is_some() {{ + assert_eq!(cargo, r#"{rustc}"#); + }} else {{ + assert_eq!(cargo, r#"{cargo}"#); + }} + + let rustc = env::var("RUSTC").unwrap(); + assert_eq!(rustc, "rustc"); + + let rustdoc = env::var("RUSTDOC").unwrap(); + assert_eq!(rustdoc, "rustdoc"); + + assert!(env::var("RUSTC_WRAPPER").is_err()); + assert!(env::var("RUSTC_WORKSPACE_WRAPPER").is_err()); + + assert!(env::var("RUSTC_LINKER").is_err()); + + assert!(env::var("RUSTFLAGS").is_err()); + let rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(); + assert_eq!(rustflags, ""); + }} + "##, + p.root() + .join("target") + .join("debug") + .join("build") + .display(), + ); + + let p = p.file("bar/build.rs", &file_content).build(); + + p.cargo("build --features bar_feat").run(); + p.cargo("build --features bar_feat") + // we use rustc since $CARGO is only used if it points to a path that exists + .env("CHECK_CARGO_IS_RUSTC", "1") + .env(cargo::CARGO_ENV, rustc) + .run(); +} + +#[cargo_test] +fn custom_build_env_var_rustflags() { + let rustflags = "--cfg=special"; + let rustflags_alt = "--cfg=notspecial"; + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [build] + rustflags = ["{}"] + "#, + rustflags + ), + ) + .file( + "build.rs", + &format!( + r#" + use std::env; + + fn main() {{ + // Static assertion that exactly one of the cfg paths is always taken. + assert!(env::var("RUSTFLAGS").is_err()); + let x; + #[cfg(special)] + {{ assert_eq!(env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(), "{}"); x = String::new(); }} + #[cfg(notspecial)] + {{ assert_eq!(env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(), "{}"); x = String::new(); }} + let _ = x; + }} + "#, + rustflags, rustflags_alt, + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + // RUSTFLAGS overrides build.rustflags, so --cfg=special shouldn't be passed + p.cargo("check").env("RUSTFLAGS", rustflags_alt).run(); +} + +#[cargo_test] +fn custom_build_env_var_encoded_rustflags() { + // NOTE: We use "-Clink-arg=-B nope" here rather than, say, "-A missing_docs", since for the + // latter it won't matter if the whitespace accidentally gets split, as rustc will do the right + // thing either way. + let p = project() + .file( + ".cargo/config", + r#" + [build] + rustflags = ["-Clink-arg=-B nope", "--cfg=foo"] + "#, + ) + .file( + "build.rs", + r#" + use std::env; + + fn main() {{ + assert_eq!(env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(), "-Clink-arg=-B nope\x1f--cfg=foo"); + }} + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn custom_build_env_var_rustc_wrapper() { + let wrapper = tools::echo_wrapper(); + let p = project() + .file( + "build.rs", + r#" + use std::env; + + fn main() {{ + assert_eq!( + env::var("RUSTC_WRAPPER").unwrap(), + env::var("CARGO_RUSTC_WRAPPER_CHECK").unwrap() + ); + }} + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .env("CARGO_BUILD_RUSTC_WRAPPER", &wrapper) + .env("CARGO_RUSTC_WRAPPER_CHECK", &wrapper) + .run(); +} + +#[cargo_test] +fn custom_build_env_var_rustc_workspace_wrapper() { + let wrapper = tools::echo_wrapper(); + + // Workspace wrapper should be set for any crate we're operating directly on. + let p = project() + .file( + "build.rs", + r#" + use std::env; + + fn main() {{ + assert_eq!( + env::var("RUSTC_WORKSPACE_WRAPPER").unwrap(), + env::var("CARGO_RUSTC_WORKSPACE_WRAPPER_CHECK").unwrap() + ); + }} + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .env("CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER", &wrapper) + .env("CARGO_RUSTC_WORKSPACE_WRAPPER_CHECK", &wrapper) + .run(); + + // But should not be set for a crate from the registry, as then it's not in a workspace. + Package::new("bar", "0.1.0") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + links = "a" + "#, + ) + .file( + "build.rs", + r#" + use std::env; + + fn main() {{ + assert!(env::var("RUSTC_WORKSPACE_WRAPPER").is_err()); + }} + "#, + ) + .file("src/lib.rs", "") + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .env("CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER", &wrapper) + .run(); +} + +#[cargo_test] +fn custom_build_env_var_rustc_linker() { + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + linker = "/path/to/linker" + "#, + target + ), + ) + .file( + "build.rs", + r#" + use std::env; + + fn main() { + assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker")); + } + "#, + ) + .file("src/lib.rs", "") + .build(); + + // no crate type set => linker never called => build succeeds if and + // only if build.rs succeeds, despite linker binary not existing. + p.cargo("build --target").arg(&target).run(); +} + +#[cargo_test] +fn custom_build_env_var_rustc_linker_bad_host_target() { + let target = rustc_host(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + linker = "/path/to/linker" + "#, + target + ), + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .build(); + + // build.rs should fail since host == target when no target is set + p.cargo("build --verbose") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/linker [..]` +[ERROR] linker `[..]/path/to/linker` not found +" + ) + .run(); +} + +#[cargo_test] +fn custom_build_env_var_rustc_linker_host_target() { + let target = rustc_host(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + target-applies-to-host = false + [target.{}] + linker = "/path/to/linker" + "#, + target + ), + ) + .file( + "build.rs", + r#" + use std::env; + + fn main() { + assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker")); + } + "#, + ) + .file("src/lib.rs", "") + .build(); + + // no crate type set => linker never called => build succeeds if and + // only if build.rs succeeds, despite linker binary not existing. + p.cargo("build -Z target-applies-to-host --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .run(); +} + +#[cargo_test] +fn custom_build_env_var_rustc_linker_host_target_env() { + let target = rustc_host(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + linker = "/path/to/linker" + "#, + target + ), + ) + .file( + "build.rs", + r#" + use std::env; + + fn main() { + assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/linker")); + } + "#, + ) + .file("src/lib.rs", "") + .build(); + + // no crate type set => linker never called => build succeeds if and + // only if build.rs succeeds, despite linker binary not existing. + p.cargo("build -Z target-applies-to-host --target") + .env("CARGO_TARGET_APPLIES_TO_HOST", "false") + .arg(&target) + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .run(); +} + +#[cargo_test] +fn custom_build_invalid_host_config_feature_flag() { + let target = rustc_host(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + linker = "/path/to/linker" + "#, + target + ), + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .build(); + + // build.rs should fail due to -Zhost-config being set without -Ztarget-applies-to-host + p.cargo("build -Z host-config --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["host-config"]) + .with_status(101) + .with_stderr_contains( + "\ +error: the -Zhost-config flag requires the -Ztarget-applies-to-host flag to be set +", + ) + .run(); +} + +#[cargo_test] +fn custom_build_linker_host_target_with_bad_host_config() { + let target = rustc_host(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [host] + linker = "/path/to/host/linker" + [target.{}] + linker = "/path/to/target/linker" + "#, + target + ), + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .build(); + + // build.rs should fail due to bad host linker being set + p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/linker [..]` +[ERROR] linker `[..]/path/to/host/linker` not found +" + ) + .run(); +} + +#[cargo_test] +fn custom_build_linker_bad_host() { + let target = rustc_host(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [host] + linker = "/path/to/host/linker" + [target.{}] + linker = "/path/to/target/linker" + "#, + target + ), + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .build(); + + // build.rs should fail due to bad host linker being set + p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/linker [..]` +[ERROR] linker `[..]/path/to/host/linker` not found +" + ) + .run(); +} + +#[cargo_test] +fn custom_build_linker_bad_host_with_arch() { + let target = rustc_host(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [host] + linker = "/path/to/host/linker" + [host.{}] + linker = "/path/to/host/arch/linker" + [target.{}] + linker = "/path/to/target/linker" + "#, + target, target + ), + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .build(); + + // build.rs should fail due to bad host linker being set + p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/arch/linker [..]` +[ERROR] linker `[..]/path/to/host/arch/linker` not found +" + ) + .run(); +} + +#[cargo_test] +fn custom_build_env_var_rustc_linker_cross_arch_host() { + let target = rustc_host(); + let cross_target = cross_compile::alternate(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [host.{}] + linker = "/path/to/host/arch/linker" + [target.{}] + linker = "/path/to/target/linker" + "#, + cross_target, target + ), + ) + .file( + "build.rs", + r#" + use std::env; + + fn main() { + assert!(env::var("RUSTC_LINKER").unwrap().ends_with("/path/to/target/linker")); + } + "#, + ) + .file("src/lib.rs", "") + .build(); + + // build.rs should be built fine since cross target != host target. + // assertion should succeed since it's still passed the target linker + p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .run(); +} + +#[cargo_test] +fn custom_build_linker_bad_cross_arch_host() { + let target = rustc_host(); + let cross_target = cross_compile::alternate(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [host] + linker = "/path/to/host/linker" + [host.{}] + linker = "/path/to/host/arch/linker" + [target.{}] + linker = "/path/to/target/linker" + "#, + cross_target, target + ), + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .build(); + + // build.rs should fail due to bad host linker being set + p.cargo("build -Z target-applies-to-host -Z host-config --verbose --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin [..]-C linker=[..]/path/to/host/linker [..]` +[ERROR] linker `[..]/path/to/host/linker` not found +" + ) + .run(); +} + +#[cargo_test] +fn custom_build_script_wrong_rustc_flags() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#"fn main() { println!("cargo:rustc-flags=-aaa -bbb"); }"#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr_contains( + "[ERROR] Only `-l` and `-L` flags are allowed in build script of `foo v0.5.0 ([CWD])`: \ + `-aaa -bbb`", + ) + .run(); +} + +#[cargo_test] +fn custom_build_script_rustc_flags() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.foo] + path = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "foo/Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + "#, + ) + .file("foo/src/lib.rs", "") + .file( + "foo/build.rs", + r#" + fn main() { + println!("cargo:rustc-flags=-l nonexistinglib -L /dummy/path1 -L /dummy/path2"); + } + "#, + ) + .build(); + + p.cargo("build --verbose") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build foo/build.rs [..] +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc --crate-name foo foo/src/lib.rs [..]\ + -L dependency=[CWD]/target/debug/deps \ + -L /dummy/path1 -L /dummy/path2 -l nonexistinglib` +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar src/main.rs [..]\ + -L dependency=[CWD]/target/debug/deps \ + --extern foo=[..]libfoo-[..] \ + -L /dummy/path1 -L /dummy/path2` +[FINISHED] dev [..] +", + ) + .run(); +} + +#[cargo_test] +fn custom_build_script_rustc_flags_no_space() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.foo] + path = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "foo/Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + "#, + ) + .file("foo/src/lib.rs", "") + .file( + "foo/build.rs", + r#" + fn main() { + println!("cargo:rustc-flags=-lnonexistinglib -L/dummy/path1 -L/dummy/path2"); + } + "#, + ) + .build(); + + p.cargo("build --verbose") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build foo/build.rs [..] +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc --crate-name foo foo/src/lib.rs [..]\ + -L dependency=[CWD]/target/debug/deps \ + -L /dummy/path1 -L /dummy/path2 -l nonexistinglib` +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar src/main.rs [..]\ + -L dependency=[CWD]/target/debug/deps \ + --extern foo=[..]libfoo-[..] \ + -L /dummy/path1 -L /dummy/path2` +[FINISHED] dev [..] +", + ) + .run(); +} + +#[cargo_test] +fn links_no_build_cmd() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + links = "a" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + package `foo v0.5.0 ([CWD])` specifies that it links to `a` but does \ +not have a custom build script +", + ) + .run(); +} + +#[cargo_test] +fn links_duplicates() { + // this tests that the links_duplicates are caught at resolver time + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + links = "a" + build = "build.rs" + + [dependencies.a-sys] + path = "a-sys" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "") + .file( + "a-sys/Cargo.toml", + r#" + [package] + name = "a-sys" + version = "0.5.0" + authors = [] + links = "a" + build = "build.rs" + "#, + ) + .file("a-sys/src/lib.rs", "") + .file("a-sys/build.rs", "") + .build(); + + p.cargo("build").with_status(101) + .with_stderr("\ +error: failed to select a version for `a-sys`. + ... required by package `foo v0.5.0 ([..])` +versions that meet the requirements `*` are: 0.5.0 + +the package `a-sys` links to the native library `a`, but it conflicts with a previous package which links to `a` as well: +package `foo v0.5.0 ([..])` +Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the links ='a-sys' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links. + +failed to select a version for `a-sys` which could resolve this conflict +").run(); +} + +#[cargo_test] +fn links_duplicates_old_registry() { + // Test old links validator. See `validate_links`. + Package::new("bar", "0.1.0") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + links = "a" + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + links = "a" + + [dependencies] + bar = "0.1" + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 ([..]) +[ERROR] multiple packages link to native library `a`, \ + but a native library can be linked only once + +package `bar v0.1.0` + ... which satisfies dependency `bar = \"^0.1\"` (locked to 0.1.0) of package `foo v0.1.0 ([..]foo)` +links to native library `a` + +package `foo v0.1.0 ([..]foo)` +also links to native library `a` +", + ) + .run(); +} + +#[cargo_test] +fn links_duplicates_deep_dependency() { + // this tests that the links_duplicates are caught at resolver time + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + links = "a" + build = "build.rs" + + [dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + build = "build.rs" + + [dependencies.a-sys] + path = "a-sys" + "#, + ) + .file("a/src/lib.rs", "") + .file("a/build.rs", "") + .file( + "a/a-sys/Cargo.toml", + r#" + [package] + name = "a-sys" + version = "0.5.0" + authors = [] + links = "a" + build = "build.rs" + "#, + ) + .file("a/a-sys/src/lib.rs", "") + .file("a/a-sys/build.rs", "") + .build(); + + p.cargo("build").with_status(101) + .with_stderr("\ +error: failed to select a version for `a-sys`. + ... required by package `a v0.5.0 ([..])` + ... which satisfies path dependency `a` of package `foo v0.5.0 ([..])` +versions that meet the requirements `*` are: 0.5.0 + +the package `a-sys` links to the native library `a`, but it conflicts with a previous package which links to `a` as well: +package `foo v0.5.0 ([..])` +Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the links ='a-sys' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links. + +failed to select a version for `a-sys` which could resolve this conflict +").run(); +} + +#[cargo_test] +fn overrides_and_links() { + let target = rustc_host(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + + [dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + fn main() { + assert_eq!(env::var("DEP_FOO_FOO").ok().expect("FOO missing"), + "bar"); + assert_eq!(env::var("DEP_FOO_BAR").ok().expect("BAR missing"), + "baz"); + } + "#, + ) + .file( + ".cargo/config", + &format!( + r#" + [target.{}.foo] + rustc-flags = "-L foo -L bar" + foo = "bar" + bar = "baz" + "#, + target + ), + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#, + ) + .file("a/src/lib.rs", "") + .file("a/build.rs", "not valid rust code") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[..] +[..] +[..] +[..] +[..] +[RUNNING] `rustc --crate-name foo [..] -L foo -L bar` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn unused_overrides() { + let target = rustc_host(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .file( + ".cargo/config", + &format!( + r#" + [target.{}.foo] + rustc-flags = "-L foo -L bar" + foo = "bar" + bar = "baz" + "#, + target + ), + ) + .build(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn links_passes_env_vars() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + + [dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + fn main() { + assert_eq!(env::var("DEP_FOO_FOO").unwrap(), "bar"); + assert_eq!(env::var("DEP_FOO_BAR").unwrap(), "baz"); + } + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#, + ) + .file("a/src/lib.rs", "") + .file( + "a/build.rs", + r#" + use std::env; + fn main() { + let lib = env::var("CARGO_MANIFEST_LINKS").unwrap(); + assert_eq!(lib, "foo"); + + println!("cargo:foo=bar"); + println!("cargo:bar=baz"); + } + "#, + ) + .build(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn only_rerun_build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + p.cargo("build -v").run(); + p.root().move_into_the_past(); + + p.change_file("some-new-file", ""); + p.root().move_into_the_past(); + + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo v0.5.0 ([CWD]): the precalculated components changed +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `[..]/build-script-build` +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rebuild_continues_to_pass_env_vars() { + let a = project() + .at("a") + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::time::Duration; + fn main() { + println!("cargo:foo=bar"); + println!("cargo:bar=baz"); + std::thread::sleep(Duration::from_millis(500)); + } + "#, + ) + .build(); + a.root().move_into_the_past(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + + [dependencies.a] + path = '{}' + "#, + a.root().display() + ), + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + fn main() { + assert_eq!(env::var("DEP_FOO_FOO").unwrap(), "bar"); + assert_eq!(env::var("DEP_FOO_BAR").unwrap(), "baz"); + } + "#, + ) + .build(); + + p.cargo("build -v").run(); + p.root().move_into_the_past(); + + p.change_file("some-new-file", ""); + p.root().move_into_the_past(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn testing_and_such() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + println!("build"); + p.cargo("build -v").run(); + p.root().move_into_the_past(); + + p.change_file("src/lib.rs", ""); + p.root().move_into_the_past(); + + println!("test"); + p.cargo("test -vj1") + .with_stderr( + "\ +[DIRTY] foo v0.5.0 ([CWD]): the precalculated components changed +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `[..]/build-script-build` +[RUNNING] `rustc --crate-name foo [..]` +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]/foo-[..][EXE]` +[DOCTEST] foo +[RUNNING] `rustdoc [..]--test [..]`", + ) + .with_stdout_contains_n("running 0 tests", 2) + .run(); + + println!("doc"); + p.cargo("doc -v") + .with_stderr( + "\ +[DOCUMENTING] foo v0.5.0 ([CWD]) +[RUNNING] `rustdoc [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.change_file("src/main.rs", "fn main() {}"); + println!("run"); + p.cargo("run") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn propagation_of_l_flags() { + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + links = "bar" + build = "build.rs" + + [dependencies.b] + path = "../b" + "#, + ) + .file("a/src/lib.rs", "") + .file( + "a/build.rs", + r#"fn main() { println!("cargo:rustc-flags=-L bar"); }"#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#, + ) + .file("b/src/lib.rs", "") + .file("b/build.rs", "bad file") + .file( + ".cargo/config", + &format!( + r#" + [target.{}.foo] + rustc-flags = "-L foo" + "#, + target + ), + ) + .build(); + + p.cargo("build -v -j1") + .with_stderr_contains( + "\ +[RUNNING] `rustc --crate-name a [..] -L bar[..]-L foo[..]` +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc --crate-name foo [..] -L bar -L foo` +", + ) + .run(); +} + +#[cargo_test] +fn propagation_of_l_flags_new() { + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + links = "bar" + build = "build.rs" + + [dependencies.b] + path = "../b" + "#, + ) + .file("a/src/lib.rs", "") + .file( + "a/build.rs", + r#" + fn main() { + println!("cargo:rustc-link-search=bar"); + } + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#, + ) + .file("b/src/lib.rs", "") + .file("b/build.rs", "bad file") + .file( + ".cargo/config", + &format!( + r#" + [target.{}.foo] + rustc-link-search = ["foo"] + "#, + target + ), + ) + .build(); + + p.cargo("build -v -j1") + .with_stderr_contains( + "\ +[RUNNING] `rustc --crate-name a [..] -L bar[..]-L foo[..]` +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc --crate-name foo [..] -L bar -L foo` +", + ) + .run(); +} + +#[cargo_test] +fn build_deps_simple() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + [build-dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + " + #[allow(unused_extern_crates)] + extern crate a; + fn main() {} + ", + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.5.0")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] a v0.5.0 ([CWD]/a) +[RUNNING] `rustc --crate-name a [..]` +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc [..] build.rs [..] --extern a=[..]` +[RUNNING] `[..]/foo-[..]/build-script-build` +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_deps_not_for_normal() { + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + [build-dependencies.aaaaa] + path = "a" + "#, + ) + .file( + "src/lib.rs", + "#[allow(unused_extern_crates)] extern crate aaaaa;", + ) + .file( + "build.rs", + " + #[allow(unused_extern_crates)] + extern crate aaaaa; + fn main() {} + ", + ) + .file("a/Cargo.toml", &basic_manifest("aaaaa", "0.5.0")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build -v --target") + .arg(&target) + .with_status(101) + .with_stderr_contains("[..]can't find crate for `aaaaa`[..]") + .with_stderr_contains( + "\ +[ERROR] could not compile `foo` due to previous error + +Caused by: + process didn't exit successfully: [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_cmd_with_a_build_cmd() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + + [build-dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + " + #[allow(unused_extern_crates)] + extern crate a; + fn main() {} + ", + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + build = "build.rs" + + [build-dependencies.b] + path = "../b" + "#, + ) + .file("a/src/lib.rs", "") + .file( + "a/build.rs", + "#[allow(unused_extern_crates)] extern crate b; fn main() {}", + ) + .file("b/Cargo.toml", &basic_manifest("b", "0.5.0")) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] b v0.5.0 ([CWD]/b) +[RUNNING] `rustc --crate-name b [..]` +[COMPILING] a v0.5.0 ([CWD]/a) +[RUNNING] `rustc [..] a/build.rs [..] --extern b=[..]` +[RUNNING] `[..]/a-[..]/build-script-build` +[RUNNING] `rustc --crate-name a [..]lib.rs [..]--crate-type lib \ + --emit=[..]link[..] \ + -C metadata=[..] \ + --out-dir [..]target/debug/deps \ + -L [..]target/debug/deps` +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc --crate-name build_script_build build.rs [..]--crate-type bin \ + --emit=[..]link[..]\ + -C metadata=[..] --out-dir [..] \ + -L [..]target/debug/deps \ + --extern a=[..]liba[..].rlib` +[RUNNING] `[..]/foo-[..]/build-script-build` +[RUNNING] `rustc --crate-name foo [..]lib.rs [..]--crate-type lib \ + --emit=[..]link[..]-C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L [..]target/debug/deps` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn out_dir_is_preserved() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + use std::fs::File; + use std::path::Path; + fn main() { + let out = env::var("OUT_DIR").unwrap(); + File::create(Path::new(&out).join("foo")).unwrap(); + } + "#, + ) + .build(); + + // Make the file + p.cargo("build -v").run(); + + // Change to asserting that it's there + p.change_file( + "build.rs", + r#" + use std::env; + use std::fs::File; + use std::path::Path; + fn main() { + let out = env::var("OUT_DIR").unwrap(); + File::open(&Path::new(&out).join("foo")).unwrap(); + } + "#, + ); + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo [..]: the file `build.rs` has changed ([..]) +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build [..] +[RUNNING] `[..]/build-script-build` +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] [..] +", + ) + .run(); + + // Run a fresh build where file should be preserved + p.cargo("build -v") + .with_stderr( + "\ +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); + + // One last time to make sure it's still there. + p.change_file("foo", ""); + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo [..]: the precalculated components changed +[COMPILING] foo [..] +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn output_separate_lines() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-flags=-L foo"); + println!("cargo:rustc-flags=-l static=foo"); + } + "#, + ) + .build(); + p.cargo("build -v") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc [..] build.rs [..]` +[RUNNING] `[..]/foo-[..]/build-script-build` +[RUNNING] `rustc --crate-name foo [..] -L foo -l static=foo` +[ERROR] could not find native static library [..] +", + ) + .run(); +} + +#[cargo_test] +fn output_separate_lines_new() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-search=foo"); + println!("cargo:rustc-link-lib=static=foo"); + println!("cargo:rustc-link-lib=bar"); + println!("cargo:rustc-link-search=bar"); + } + "#, + ) + .build(); + // The order of the arguments passed to rustc is important. + p.cargo("build -v") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc [..] build.rs [..]` +[RUNNING] `[..]/foo-[..]/build-script-build` +[RUNNING] `rustc --crate-name foo [..] -L foo -L bar -l static=foo -l bar` +[ERROR] could not find native static library [..] +", + ) + .run(); +} + +#[cfg(not(windows))] // FIXME(#867) +#[cargo_test] +fn code_generation() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "src/main.rs", + r#" + include!(concat!(env!("OUT_DIR"), "/hello.rs")); + + fn main() { + println!("{}", message()); + } + "#, + ) + .file( + "build.rs", + r#" + use std::env; + use std::fs; + use std::path::PathBuf; + + fn main() { + let dst = PathBuf::from(env::var("OUT_DIR").unwrap()); + fs::write(dst.join("hello.rs"), + " + pub fn message() -> &'static str { + \"Hello, World!\" + } + ") + .unwrap(); + } + "#, + ) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo`", + ) + .with_stdout("Hello, World!") + .run(); + + p.cargo("test").run(); +} + +#[cargo_test] +fn release_with_build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() {} + "#, + ) + .build(); + + p.cargo("build -v --release").run(); +} + +#[cargo_test] +fn build_script_only() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = "build.rs" + "#, + ) + .file("build.rs", r#"fn main() {}"#) + .build(); + p.cargo("build -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + no targets specified in the manifest + either src/lib.rs, src/main.rs, a [lib] section, or [[bin]] section must be present", + ) + .run(); +} + +#[cargo_test] +fn shared_dep_with_a_build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + + [dependencies.a] + path = "a" + + [build-dependencies.b] + path = "b" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("a/build.rs", "fn main() {}") + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + + [dependencies.a] + path = "../a" + "#, + ) + .file("b/src/lib.rs", "") + .build(); + p.cargo("build -v").run(); +} + +#[cargo_test] +fn transitive_dep_host() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + + [build-dependencies.b] + path = "b" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#, + ) + .file("a/build.rs", "fn main() {}") + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + + [lib] + name = "b" + plugin = true + + [dependencies.a] + path = "../a" + "#, + ) + .file("b/src/lib.rs", "") + .build(); + p.cargo("build").run(); +} + +#[cargo_test] +fn test_a_lib_with_a_build_command() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "src/lib.rs", + r#" + include!(concat!(env!("OUT_DIR"), "/foo.rs")); + + /// ``` + /// foo::bar(); + /// ``` + pub fn bar() { + assert_eq!(foo(), 1); + } + "#, + ) + .file( + "build.rs", + r#" + use std::env; + use std::fs; + use std::path::PathBuf; + + fn main() { + let out = PathBuf::from(env::var("OUT_DIR").unwrap()); + fs::write(out.join("foo.rs"), "fn foo() -> i32 { 1 }").unwrap(); + } + "#, + ) + .build(); + p.cargo("test").run(); +} + +#[cargo_test] +fn test_dev_dep_build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dev-dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("a/build.rs", "fn main() {}") + .file("a/src/lib.rs", "") + .build(); + + p.cargo("test").run(); +} + +#[cargo_test] +fn build_script_with_dynamic_native_dependency() { + let build = project() + .at("builder") + .file( + "Cargo.toml", + r#" + [package] + name = "builder" + version = "0.0.1" + authors = [] + + [lib] + name = "builder" + crate-type = ["dylib"] + "#, + ) + .file("src/lib.rs", "#[no_mangle] pub extern fn foo() {}") + .build(); + + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + + [build-dependencies.bar] + path = "bar" + "#, + ) + .file("build.rs", "extern crate bar; fn main() { bar::bar() }") + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file( + "bar/build.rs", + r#" + use std::env; + use std::fs; + use std::path::PathBuf; + + fn main() { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let root = PathBuf::from(env::var("BUILDER_ROOT").unwrap()); + let file = format!("{}builder{}", + env::consts::DLL_PREFIX, + env::consts::DLL_SUFFIX); + let src = root.join(&file); + let dst = out_dir.join(&file); + fs::copy(src, dst).unwrap(); + if cfg!(target_env = "msvc") { + fs::copy(root.join("builder.dll.lib"), + out_dir.join("builder.dll.lib")).unwrap(); + } + println!("cargo:rustc-link-search=native={}", out_dir.display()); + } + "#, + ) + .file( + "bar/src/lib.rs", + r#" + pub fn bar() { + #[cfg_attr(not(target_env = "msvc"), link(name = "builder"))] + #[cfg_attr(target_env = "msvc", link(name = "builder.dll"))] + extern { fn foo(); } + unsafe { foo() } + } + "#, + ) + .build(); + + build + .cargo("build -v") + .env("CARGO_LOG", "cargo::ops::cargo_rustc") + .run(); + + let root = build.root().join("target").join("debug"); + foo.cargo("build -v") + .env("BUILDER_ROOT", root) + .env("CARGO_LOG", "cargo::ops::cargo_rustc") + .run(); +} + +#[cargo_test] +fn profile_and_opt_level_set_correctly() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + + fn main() { + assert_eq!(env::var("OPT_LEVEL").unwrap(), "3"); + assert_eq!(env::var("PROFILE").unwrap(), "release"); + assert_eq!(env::var("DEBUG").unwrap(), "false"); + } + "#, + ) + .build(); + p.cargo("bench").run(); +} + +#[cargo_test] +fn profile_debug_0() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [profile.dev] + debug = 0 + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + + fn main() { + assert_eq!(env::var("OPT_LEVEL").unwrap(), "0"); + assert_eq!(env::var("PROFILE").unwrap(), "debug"); + assert_eq!(env::var("DEBUG").unwrap(), "false"); + } + "#, + ) + .build(); + p.cargo("build").run(); +} + +#[cargo_test] +fn build_script_with_lto() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + + [profile.dev] + lto = true + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + p.cargo("build").run(); +} + +#[cargo_test] +fn test_duplicate_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + build = "build.rs" + + [dependencies.bar] + path = "bar" + + [build-dependencies.bar] + path = "bar" + "#, + ) + .file( + "src/main.rs", + r#" + extern crate bar; + fn main() { bar::do_nothing() } + "#, + ) + .file( + "build.rs", + r#" + extern crate bar; + fn main() { bar::do_nothing() } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn do_nothing() {}") + .build(); + + p.cargo("build").run(); +} + +#[cargo_test] +fn cfg_feedback() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("src/main.rs", "#[cfg(foo)] fn main() {}") + .file( + "build.rs", + r#"fn main() { println!("cargo:rustc-cfg=foo"); }"#, + ) + .build(); + p.cargo("build -v").run(); +} + +#[cargo_test] +fn cfg_override() { + let target = rustc_host(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + links = "a" + build = "build.rs" + "#, + ) + .file("src/main.rs", "#[cfg(foo)] fn main() {}") + .file("build.rs", "") + .file( + ".cargo/config", + &format!( + r#" + [target.{}.a] + rustc-cfg = ["foo"] + "#, + target + ), + ) + .build(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn cfg_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + r#"fn main() { println!("cargo:rustc-cfg=foo"); }"#, + ) + .file( + "src/lib.rs", + r#" + /// + /// ``` + /// extern crate foo; + /// + /// fn main() { + /// foo::foo() + /// } + /// ``` + /// + #[cfg(foo)] + pub fn foo() {} + + #[cfg(foo)] + #[test] + fn test_foo() { + foo() + } + "#, + ) + .file("tests/test.rs", "#[cfg(foo)] #[test] fn test_bar() {}") + .build(); + p.cargo("test -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] [..] build.rs [..] +[RUNNING] `[..]/build-script-build` +[RUNNING] [..] --cfg foo[..] +[RUNNING] [..] --cfg foo[..] +[RUNNING] [..] --cfg foo[..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]/foo-[..][EXE]` +[RUNNING] `[..]/test-[..][EXE]` +[DOCTEST] foo +[RUNNING] [..] --cfg foo[..]", + ) + .with_stdout_contains("test test_foo ... ok") + .with_stdout_contains("test test_bar ... ok") + .with_stdout_contains_n("test [..] ... ok", 3) + .run(); +} + +#[cargo_test] +fn cfg_doc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + + [dependencies.bar] + path = "bar" + "#, + ) + .file( + "build.rs", + r#"fn main() { println!("cargo:rustc-cfg=foo"); }"#, + ) + .file("src/lib.rs", "#[cfg(foo)] pub fn foo() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file( + "bar/build.rs", + r#"fn main() { println!("cargo:rustc-cfg=bar"); }"#, + ) + .file("bar/src/lib.rs", "#[cfg(bar)] pub fn bar() {}") + .build(); + p.cargo("doc").run(); + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/foo/fn.foo.html").is_file()); + assert!(p.root().join("target/doc/bar/fn.bar.html").is_file()); +} + +#[cargo_test] +fn cfg_override_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + links = "a" + "#, + ) + .file("build.rs", "") + .file( + ".cargo/config", + &format!( + r#" + [target.{}.a] + rustc-cfg = ["foo"] + "#, + rustc_host() + ), + ) + .file( + "src/lib.rs", + r#" + /// + /// ``` + /// extern crate foo; + /// + /// fn main() { + /// foo::foo() + /// } + /// ``` + /// + #[cfg(foo)] + pub fn foo() {} + + #[cfg(foo)] + #[test] + fn test_foo() { + foo() + } + "#, + ) + .file("tests/test.rs", "#[cfg(foo)] #[test] fn test_bar() {}") + .build(); + p.cargo("test -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `[..]` +[RUNNING] `[..]` +[RUNNING] `[..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]/foo-[..][EXE]` +[RUNNING] `[..]/test-[..][EXE]` +[DOCTEST] foo +[RUNNING] [..] --cfg foo[..]", + ) + .with_stdout_contains("test test_foo ... ok") + .with_stdout_contains("test test_bar ... ok") + .with_stdout_contains_n("test [..] ... ok", 3) + .run(); +} + +#[cargo_test] +fn cfg_override_doc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + links = "a" + + [dependencies.bar] + path = "bar" + "#, + ) + .file( + ".cargo/config", + &format!( + r#" + [target.{target}.a] + rustc-cfg = ["foo"] + [target.{target}.b] + rustc-cfg = ["bar"] + "#, + target = rustc_host() + ), + ) + .file("build.rs", "") + .file("src/lib.rs", "#[cfg(foo)] pub fn foo() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + build = "build.rs" + links = "b" + "#, + ) + .file("bar/build.rs", "") + .file("bar/src/lib.rs", "#[cfg(bar)] pub fn bar() {}") + .build(); + p.cargo("doc").run(); + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/foo/fn.foo.html").is_file()); + assert!(p.root().join("target/doc/bar/fn.bar.html").is_file()); +} + +#[cargo_test] +fn env_build() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file( + "src/main.rs", + r#" + const FOO: &'static str = env!("FOO"); + fn main() { + println!("{}", FOO); + } + "#, + ) + .file( + "build.rs", + r#"fn main() { println!("cargo:rustc-env=FOO=foo"); }"#, + ) + .build(); + p.cargo("build -v").run(); + p.cargo("run -v").with_stdout("foo\n").run(); +} + +#[cargo_test] +fn env_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + r#"fn main() { println!("cargo:rustc-env=FOO=foo"); }"#, + ) + .file( + "src/lib.rs", + r#"pub const FOO: &'static str = env!("FOO"); "#, + ) + .file( + "tests/test.rs", + r#" + extern crate foo; + + #[test] + fn test_foo() { + assert_eq!("foo", foo::FOO); + } + "#, + ) + .build(); + p.cargo("test -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] [..] build.rs [..] +[RUNNING] `[..]/build-script-build` +[RUNNING] [..] --crate-name foo[..] +[RUNNING] [..] --crate-name foo[..] +[RUNNING] [..] --crate-name test[..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]/foo-[..][EXE]` +[RUNNING] `[..]/test-[..][EXE]` +[DOCTEST] foo +[RUNNING] [..] --crate-name foo[..]", + ) + .with_stdout_contains_n("running 0 tests", 2) + .with_stdout_contains("test test_foo ... ok") + .run(); +} + +#[cargo_test] +fn env_doc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file( + "src/main.rs", + r#" + const FOO: &'static str = env!("FOO"); + fn main() {} + "#, + ) + .file( + "build.rs", + r#"fn main() { println!("cargo:rustc-env=FOO=foo"); }"#, + ) + .build(); + p.cargo("doc -v").run(); +} + +#[cargo_test] +fn flags_go_into_tests() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + b = { path = "b" } + "#, + ) + .file("src/lib.rs", "") + .file("tests/foo.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + [dependencies] + a = { path = "../a" } + "#, + ) + .file("b/src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("a/src/lib.rs", "") + .file( + "a/build.rs", + r#" + fn main() { + println!("cargo:rustc-link-search=test"); + } + "#, + ) + .build(); + + p.cargo("test -v --test=foo") + .with_stderr( + "\ +[COMPILING] a v0.5.0 ([..] +[RUNNING] `rustc [..] a/build.rs [..]` +[RUNNING] `[..]/build-script-build` +[RUNNING] `rustc [..] a/src/lib.rs [..] -L test[..]` +[COMPILING] b v0.5.0 ([..] +[RUNNING] `rustc [..] b/src/lib.rs [..] -L test[..]` +[COMPILING] foo v0.5.0 ([..] +[RUNNING] `rustc [..] src/lib.rs [..] -L test[..]` +[RUNNING] `rustc [..] tests/foo.rs [..] -L test[..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]/foo-[..][EXE]`", + ) + .with_stdout_contains("running 0 tests") + .run(); + + p.cargo("test -v -pb --lib") + .with_stderr( + "\ +[FRESH] a v0.5.0 ([..] +[COMPILING] b v0.5.0 ([..] +[RUNNING] `rustc [..] b/src/lib.rs [..] -L test[..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]/b-[..][EXE]`", + ) + .with_stdout_contains("running 0 tests") + .run(); +} + +#[cargo_test] +fn diamond_passes_args_only_once() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + a = { path = "a" } + b = { path = "b" } + "#, + ) + .file("src/lib.rs", "") + .file("tests/foo.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + [dependencies] + b = { path = "../b" } + c = { path = "../c" } + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + [dependencies] + c = { path = "../c" } + "#, + ) + .file("b/src/lib.rs", "") + .file( + "c/Cargo.toml", + r#" + [package] + name = "c" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "c/build.rs", + r#" + fn main() { + println!("cargo:rustc-link-search=native=test"); + } + "#, + ) + .file("c/src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] c v0.5.0 ([..] +[RUNNING] `rustc [..]` +[RUNNING] `[..]` +[RUNNING] `rustc [..]` +[COMPILING] b v0.5.0 ([..] +[RUNNING] `rustc [..]` +[COMPILING] a v0.5.0 ([..] +[RUNNING] `rustc [..]` +[COMPILING] foo v0.5.0 ([..] +[RUNNING] `[..]rmeta -L native=test` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn adding_an_override_invalidates() { + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file(".cargo/config", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-search=native=foo"); + } + "#, + ) + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([..] +[RUNNING] `rustc [..]` +[RUNNING] `[..]` +[RUNNING] `rustc [..] -L native=foo` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.change_file( + ".cargo/config", + &format!( + " + [target.{}.foo] + rustc-link-search = [\"native=bar\"] + ", + target + ), + ); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([..] +[RUNNING] `rustc [..] -L native=bar` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn changing_an_override_invalidates() { + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + &format!( + " + [target.{}.foo] + rustc-link-search = [\"native=foo\"] + ", + target + ), + ) + .file("build.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([..] +[RUNNING] `rustc [..] -L native=foo` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.change_file( + ".cargo/config", + &format!( + " + [target.{}.foo] + rustc-link-search = [\"native=bar\"] + ", + target + ), + ); + + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo v0.5.0 ([..]): the precalculated components changed +[COMPILING] foo v0.5.0 ([..] +[RUNNING] `rustc [..] -L native=bar` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn fresh_builds_possible_with_link_libs() { + // The bug is non-deterministic. Sometimes you can get a fresh build + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + links = "nativefoo" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + &format!( + " + [target.{}.nativefoo] + rustc-link-lib = [\"a\"] + rustc-link-search = [\"./b\"] + rustc-flags = \"-l z -L ./\" + ", + target + ), + ) + .file("build.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([..] +[RUNNING] `rustc [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("build -v") + .with_stderr( + "\ +[FRESH] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn fresh_builds_possible_with_multiple_metadata_overrides() { + // The bug is non-deterministic. Sometimes you can get a fresh build + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + &format!( + " + [target.{}.foo] + a = \"\" + b = \"\" + c = \"\" + d = \"\" + e = \"\" + ", + target + ), + ) + .file("build.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([..] +[RUNNING] `rustc [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("build -v") + .env("CARGO_LOG", "cargo::ops::cargo_rustc::fingerprint=info") + .with_stderr( + "\ +[FRESH] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn generate_good_d_files() { + // this is here to stop regression on an issue where build.rs rerun-if-changed paths aren't + // made absolute properly, which in turn interacts poorly with the dep-info-basedir setting, + // and the dep-info files have other-crate-relative paths spat out in them + let p = project() + .file( + "awoo/Cargo.toml", + r#" + [package] + name = "awoo" + version = "0.5.0" + build = "build.rs" + "#, + ) + .file("awoo/src/lib.rs", "") + .file( + "awoo/build.rs", + r#" + fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=barkbarkbark"); + } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "meow" + version = "0.5.0" + [dependencies] + awoo = { path = "awoo" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v").run(); + + let dot_d_path = p.bin("meow").with_extension("d"); + println!("*meow at* {:?}", dot_d_path); + let dot_d = fs::read_to_string(&dot_d_path).unwrap(); + + println!("*.d file content*: {}", &dot_d); + + assert_match_exact( + "[..]/target/debug/meow[EXE]: [..]/awoo/barkbarkbark [..]/awoo/build.rs[..]", + &dot_d, + ); + + // paths relative to dependency roots should not be allowed + assert!(!dot_d + .split_whitespace() + .any(|v| v == "barkbarkbark" || v == "build.rs")); + + p.change_file( + ".cargo/config.toml", + r#" + [build] + dep-info-basedir="." + "#, + ); + p.cargo("build -v").run(); + + let dot_d = fs::read_to_string(&dot_d_path).unwrap(); + + println!("*.d file content with dep-info-basedir*: {}", &dot_d); + + assert_match_exact( + "target/debug/meow[EXE]: awoo/barkbarkbark awoo/build.rs[..]", + &dot_d, + ); + + // paths relative to dependency roots should not be allowed + assert!(!dot_d + .split_whitespace() + .any(|v| v == "barkbarkbark" || v == "build.rs")); +} + +#[cargo_test] +fn generate_good_d_files_for_external_tools() { + // This tests having a relative paths going out of the + // project root in config's dep-info-basedir + let p = project_in("rust_things") + .file( + "awoo/Cargo.toml", + r#" + [package] + name = "awoo" + version = "0.5.0" + build = "build.rs" + "#, + ) + .file("awoo/src/lib.rs", "") + .file( + "awoo/build.rs", + r#" + fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=barkbarkbark"); + } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "meow" + version = "0.5.0" + [dependencies] + awoo = { path = "awoo" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config.toml", + r#" + [build] + dep-info-basedir="../.." + "#, + ) + .build(); + + p.cargo("build -v").run(); + + let dot_d_path = p.bin("meow").with_extension("d"); + let dot_d = fs::read_to_string(&dot_d_path).unwrap(); + + println!("*.d file content with dep-info-basedir*: {}", &dot_d); + + assert_match_exact( + concat!( + "rust_things/foo/target/debug/meow[EXE]:", + " rust_things/foo/awoo/barkbarkbark", + " rust_things/foo/awoo/build.rs", + " rust_things/foo/awoo/src/lib.rs", + " rust_things/foo/src/main.rs", + ), + &dot_d, + ); +} + +#[cargo_test] +fn rebuild_only_on_explicit_paths() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-changed=foo"); + println!("cargo:rerun-if-changed=bar"); + } + "#, + ) + .build(); + + p.cargo("build -v").run(); + + // files don't exist, so should always rerun if they don't exist + println!("run without"); + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo v0.5.0 ([..]): the file `foo` is missing +[COMPILING] foo v0.5.0 ([..]) +[RUNNING] `[..]/build-script-build` +[RUNNING] `rustc [..] src/lib.rs [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + sleep_ms(1000); + p.change_file("foo", ""); + p.change_file("bar", ""); + sleep_ms(1000); // make sure the to-be-created outfile has a timestamp distinct from the infiles + + // now the exist, so run once, catch the mtime, then shouldn't run again + println!("run with"); + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo v0.5.0 ([..]): the file `foo` has changed ([..]) +[COMPILING] foo v0.5.0 ([..]) +[RUNNING] `[..]/build-script-build` +[RUNNING] `rustc [..] src/lib.rs [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + println!("run with2"); + p.cargo("build -v") + .with_stderr( + "\ +[FRESH] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + sleep_ms(1000); + + // random other files do not affect freshness + println!("run baz"); + p.change_file("baz", ""); + p.cargo("build -v") + .with_stderr( + "\ +[FRESH] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + // but changing dependent files does + println!("run foo change"); + p.change_file("foo", ""); + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo v0.5.0 ([..]): the file `foo` has changed ([..]) +[COMPILING] foo v0.5.0 ([..]) +[RUNNING] `[..]/build-script-build` +[RUNNING] `rustc [..] src/lib.rs [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + // .. as does deleting a file + println!("run bar delete"); + fs::remove_file(p.root().join("bar")).unwrap(); + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo v0.5.0 ([..]): the file `bar` is missing +[COMPILING] foo v0.5.0 ([..]) +[RUNNING] `[..]/build-script-build` +[RUNNING] `rustc [..] src/lib.rs [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn doctest_receives_build_link_args() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + links = "bar" + build = "build.rs" + "#, + ) + .file("a/src/lib.rs", "") + .file( + "a/build.rs", + r#" + fn main() { + println!("cargo:rustc-link-search=native=bar"); + } + "#, + ) + .build(); + + p.cargo("test -v") + .with_stderr_contains( + "[RUNNING] `rustdoc [..]--crate-name foo --test [..]-L native=bar[..]`", + ) + .run(); +} + +#[cargo_test] +fn please_respect_the_dag() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + + [dependencies] + a = { path = 'a' } + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-search=native=foo"); + } + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + links = "bar" + build = "build.rs" + "#, + ) + .file("a/src/lib.rs", "") + .file( + "a/build.rs", + r#" + fn main() { + println!("cargo:rustc-link-search=native=bar"); + } + "#, + ) + .build(); + + p.cargo("build -v") + .with_stderr_contains("[RUNNING] `rustc [..] -L native=foo -L native=bar[..]`") + .run(); +} + +#[cargo_test] +fn non_utf8_output() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + r#" + use std::io::prelude::*; + + fn main() { + let mut out = std::io::stdout(); + // print something that's not utf8 + out.write_all(b"\xff\xff\n").unwrap(); + + // now print some cargo metadata that's utf8 + println!("cargo:rustc-cfg=foo"); + + // now print more non-utf8 + out.write_all(b"\xff\xff\n").unwrap(); + } + "#, + ) + .file("src/main.rs", "#[cfg(foo)] fn main() {}") + .build(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn custom_target_dir() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [build] + target-dir = 'test' + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("a/build.rs", "fn main() {}") + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn panic_abort_with_build_scripts() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [profile.release] + panic = 'abort' + + [dependencies] + a = { path = "a" } + "#, + ) + .file( + "src/lib.rs", + "#[allow(unused_extern_crates)] extern crate a;", + ) + .file("build.rs", "fn main() {}") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + build = "build.rs" + + [build-dependencies] + b = { path = "../b" } + "#, + ) + .file("a/src/lib.rs", "") + .file( + "a/build.rs", + "#[allow(unused_extern_crates)] extern crate b; fn main() {}", + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("build -v --release").run(); + + p.root().join("target").rm_rf(); + + p.cargo("test --release -v") + .with_stderr_does_not_contain("[..]panic=abort[..]") + .run(); +} + +#[cargo_test] +fn warnings_emitted() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:warning=foo"); + println!("cargo:warning=bar"); + } + "#, + ) + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([..]) +[RUNNING] `rustc [..]` +[RUNNING] `[..]` +warning: foo +warning: bar +[RUNNING] `rustc [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn warnings_emitted_when_build_script_panics() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:warning=foo"); + println!("cargo:warning=bar"); + panic!(); + } + "#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stdout("") + .with_stderr_contains("warning: foo\nwarning: bar") + .run(); +} + +#[cargo_test] +fn warnings_hidden_for_upstream() { + Package::new("bar", "0.1.0") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:warning=foo"); + println!("cargo:warning=bar"); + } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 ([..]) +[COMPILING] bar v0.1.0 +[RUNNING] `rustc [..]` +[RUNNING] `[..]` +[RUNNING] `rustc [..]` +[COMPILING] foo v0.5.0 ([..]) +[RUNNING] `rustc [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn warnings_printed_on_vv() { + Package::new("bar", "0.1.0") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:warning=foo"); + println!("cargo:warning=bar"); + } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -vv") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 ([..]) +[COMPILING] bar v0.1.0 +[RUNNING] `[..] rustc [..]` +[RUNNING] `[..]` +warning: foo +warning: bar +[RUNNING] `[..] rustc [..]` +[COMPILING] foo v0.5.0 ([..]) +[RUNNING] `[..] rustc [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn output_shows_on_vv() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::io::prelude::*; + + fn main() { + std::io::stderr().write_all(b"stderr\n").unwrap(); + std::io::stdout().write_all(b"stdout\n").unwrap(); + } + "#, + ) + .build(); + + p.cargo("build -vv") + .with_stdout("[foo 0.5.0] stdout") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([..]) +[RUNNING] `[..] rustc [..]` +[RUNNING] `[..]` +[foo 0.5.0] stderr +[RUNNING] `[..] rustc [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn links_with_dots() { + let target = rustc_host(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + links = "a.b" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-search=bar") + } + "#, + ) + .file( + ".cargo/config", + &format!( + r#" + [target.{}.'a.b'] + rustc-link-search = ["foo"] + "#, + target + ), + ) + .build(); + + p.cargo("build -v") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..] [..] -L foo[..]`") + .run(); +} + +#[cargo_test] +fn rustc_and_rustdoc_set_correctly() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + + fn main() { + assert_eq!(env::var("RUSTC").unwrap(), "rustc"); + assert_eq!(env::var("RUSTDOC").unwrap(), "rustdoc"); + } + "#, + ) + .build(); + p.cargo("bench").run(); +} + +#[cargo_test] +fn cfg_env_vars_available() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + + fn main() { + let fam = env::var("CARGO_CFG_TARGET_FAMILY").unwrap(); + if cfg!(unix) { + assert_eq!(fam, "unix"); + } else { + assert_eq!(fam, "windows"); + } + } + "#, + ) + .build(); + p.cargo("bench").run(); +} + +#[cargo_test] +fn switch_features_rerun() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + + [features] + foo = [] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + println!(include_str!(concat!(env!("OUT_DIR"), "/output"))); + } + "#, + ) + .file( + "build.rs", + r#" + use std::env; + use std::fs; + use std::path::Path; + + fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let output = Path::new(&out_dir).join("output"); + + if env::var_os("CARGO_FEATURE_FOO").is_some() { + fs::write(output, "foo").unwrap(); + } else { + fs::write(output, "bar").unwrap(); + } + } + "#, + ) + .build(); + + p.cargo("build -v --features=foo").run(); + p.rename_run("foo", "with_foo").with_stdout("foo\n").run(); + p.cargo("build -v").run(); + p.rename_run("foo", "without_foo") + .with_stdout("bar\n") + .run(); + p.cargo("build -v --features=foo").run(); + p.rename_run("foo", "with_foo2").with_stdout("foo\n").run(); +} + +#[cargo_test] +fn assume_build_script_when_build_rs_present() { + let p = project() + .file( + "src/main.rs", + r#" + fn main() { + if ! cfg!(foo) { + panic!("the build script was not run"); + } + } + "#, + ) + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-cfg=foo"); + } + "#, + ) + .build(); + + p.cargo("run -v").run(); +} + +#[cargo_test] +fn if_build_set_to_false_dont_treat_build_rs_as_build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = false + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + if cfg!(foo) { + panic!("the build script was run"); + } + } + "#, + ) + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-cfg=foo"); + } + "#, + ) + .build(); + + p.cargo("run -v").run(); +} + +#[cargo_test] +fn deterministic_rustc_dependency_flags() { + // This bug is non-deterministic hence the large number of dependencies + // in the hopes it will have a much higher chance of triggering it. + + Package::new("dep1", "0.1.0") + .file( + "Cargo.toml", + r#" + [package] + name = "dep1" + version = "0.1.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-flags=-L native=test1"); + } + "#, + ) + .file("src/lib.rs", "") + .publish(); + Package::new("dep2", "0.1.0") + .file( + "Cargo.toml", + r#" + [package] + name = "dep2" + version = "0.1.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-flags=-L native=test2"); + } + "#, + ) + .file("src/lib.rs", "") + .publish(); + Package::new("dep3", "0.1.0") + .file( + "Cargo.toml", + r#" + [package] + name = "dep3" + version = "0.1.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-flags=-L native=test3"); + } + "#, + ) + .file("src/lib.rs", "") + .publish(); + Package::new("dep4", "0.1.0") + .file( + "Cargo.toml", + r#" + [package] + name = "dep4" + version = "0.1.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-flags=-L native=test4"); + } + "#, + ) + .file("src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + dep1 = "*" + dep2 = "*" + dep3 = "*" + dep4 = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .with_stderr_contains( + "\ +[RUNNING] `rustc --crate-name foo [..] -L native=test1 -L native=test2 \ +-L native=test3 -L native=test4` +", + ) + .run(); +} + +#[cargo_test] +fn links_duplicates_with_cycle() { + // this tests that the links_duplicates are caught at resolver time + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + links = "a" + build = "build.rs" + + [dependencies.a] + path = "a" + + [dev-dependencies] + b = { path = "b" } + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + links = "a" + build = "build.rs" + "#, + ) + .file("a/src/lib.rs", "") + .file("a/build.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + + [dependencies] + foo = { path = ".." } + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("build").with_status(101) + .with_stderr("\ +error: failed to select a version for `a`. + ... required by package `foo v0.5.0 ([..])` +versions that meet the requirements `*` are: 0.5.0 + +the package `a` links to the native library `a`, but it conflicts with a previous package which links to `a` as well: +package `foo v0.5.0 ([..])` +Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the links ='a' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links. + +failed to select a version for `a` which could resolve this conflict +").run(); +} + +#[cargo_test] +fn rename_with_link_search_path() { + _rename_with_link_search_path(false); +} + +#[cargo_test] +#[cfg_attr( + target_os = "macos", + ignore = "don't have a cdylib cross target on macos" +)] +fn rename_with_link_search_path_cross() { + if cross_compile::disabled() { + return; + } + + _rename_with_link_search_path(true); +} + +fn _rename_with_link_search_path(cross: bool) { + let target_arg = if cross { + format!(" --target={}", cross_compile::alternate()) + } else { + "".to_string() + }; + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [lib] + crate-type = ["cdylib"] + "#, + ) + .file( + "src/lib.rs", + "#[no_mangle] pub extern fn cargo_test_foo() {}", + ); + let p = p.build(); + + p.cargo(&format!("build{}", target_arg)).run(); + + let p2 = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file( + "build.rs", + r#" + use std::env; + use std::fs; + use std::path::PathBuf; + + fn main() { + // Move the `libfoo.so` from the root of our project into the + // build directory. This way Cargo should automatically manage + // `LD_LIBRARY_PATH` and such. + let root = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); + let file = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); + let src = root.join(&file); + + let dst_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let dst = dst_dir.join(&file); + + fs::copy(&src, &dst).unwrap(); + // handle windows, like below + drop(fs::copy(root.join("foo.dll.lib"), dst_dir.join("foo.dll.lib"))); + + println!("cargo:rerun-if-changed=build.rs"); + if cfg!(target_env = "msvc") { + println!("cargo:rustc-link-lib=foo.dll"); + } else { + println!("cargo:rustc-link-lib=foo"); + } + println!("cargo:rustc-link-search=all={}", + dst.parent().unwrap().display()); + } + "#, + ) + .file( + "src/main.rs", + r#" + extern { + #[link_name = "cargo_test_foo"] + fn foo(); + } + + fn main() { + unsafe { foo(); } + } + "#, + ); + let p2 = p2.build(); + + // Move the output `libfoo.so` into the directory of `p2`, and then delete + // the `p` project. On macOS, the `libfoo.dylib` artifact references the + // original path in `p` so we want to make sure that it can't find it (hence + // the deletion). + let root = if cross { + p.root() + .join("target") + .join(cross_compile::alternate()) + .join("debug") + .join("deps") + } else { + p.root().join("target").join("debug").join("deps") + }; + let file = format!("{}foo{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX); + let src = root.join(&file); + + let dst = p2.root().join(&file); + + fs::copy(&src, &dst).unwrap(); + // copy the import library for windows, if it exists + drop(fs::copy( + &root.join("foo.dll.lib"), + p2.root().join("foo.dll.lib"), + )); + remove_dir_all(p.root()).unwrap(); + + // Everything should work the first time + p2.cargo(&format!("run{}", target_arg)).run(); + + // Now rename the root directory and rerun `cargo run`. Not only should we + // not build anything but we also shouldn't crash. + let mut new = p2.root(); + new.pop(); + new.push("bar2"); + + // For whatever reason on Windows right after we execute a binary it's very + // unlikely that we're able to successfully delete or rename that binary. + // It's not really clear why this is the case or if it's a bug in Cargo + // holding a handle open too long. In an effort to reduce the flakiness of + // this test though we throw this in a loop + // + // For some more information see #5481 and rust-lang/rust#48775 + let mut i = 0; + loop { + let error = match fs::rename(p2.root(), &new) { + Ok(()) => break, + Err(e) => e, + }; + i += 1; + if !cfg!(windows) || error.kind() != io::ErrorKind::PermissionDenied || i > 10 { + panic!("failed to rename: {}", error); + } + println!("assuming {} is spurious, waiting to try again", error); + thread::sleep(slow_cpu_multiplier(100)); + } + + p2.cargo(&format!("run{}", target_arg)) + .cwd(&new) + .with_stderr( + "\ +[FINISHED] [..] +[RUNNING] [..] +", + ) + .run(); +} + +#[cargo_test] +fn optional_build_script_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + bar = { path = "bar", optional = true } + + [build-dependencies] + bar = { path = "bar", optional = true } + "#, + ) + .file( + "build.rs", + r#" + #[cfg(feature = "bar")] + extern crate bar; + + fn main() { + #[cfg(feature = "bar")] { + println!("cargo:rustc-env=FOO={}", bar::bar()); + return + } + println!("cargo:rustc-env=FOO=0"); + } + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(feature = "bar")] + extern crate bar; + + fn main() { + println!("{}", env!("FOO")); + } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("bar/src/lib.rs", "pub fn bar() -> u32 { 1 }"); + let p = p.build(); + + p.cargo("run").with_stdout("0\n").run(); + p.cargo("run --features bar").with_stdout("1\n").run(); +} + +#[cargo_test] +fn optional_build_dep_and_required_normal_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "./bar", optional = true } + + [build-dependencies] + bar = { path = "./bar" } + "#, + ) + .file("build.rs", "extern crate bar; fn main() { bar::bar(); }") + .file( + "src/main.rs", + r#" + #[cfg(feature = "bar")] + extern crate bar; + + fn main() { + #[cfg(feature = "bar")] { + println!("{}", bar::bar()); + } + #[cfg(not(feature = "bar"))] { + println!("0"); + } + } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("bar/src/lib.rs", "pub fn bar() -> u32 { 1 }"); + let p = p.build(); + + p.cargo("run") + .with_stdout("0") + .with_stderr( + "\ +[COMPILING] bar v0.5.0 ([..]) +[COMPILING] foo v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]foo[EXE]`", + ) + .run(); + + p.cargo("run --all-features") + .with_stdout("1") + .with_stderr( + "\ +[COMPILING] bar v0.5.0 ([..]) +[COMPILING] foo v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]foo[EXE]`", + ) + .run(); +} + +#[cargo_test] +fn using_rerun_if_changed_does_not_rebuild() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#, + ) + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-changed=build.rs"); + } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("build").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn links_interrupted_can_restart() { + // Test for a `links` dependent build script getting canceled and then + // restarted. Steps: + // 1. Build to establish fingerprints. + // 2. Change something (an env var in this case) that triggers the + // dependent build script to run again. Kill the top-level build script + // while it is running (such as hitting Ctrl-C). + // 3. Run the build again, it should re-run the build script. + let bar = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-env-changed=SOMEVAR"); + } + "#, + ) + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + + [dependencies.bar] + path = '{}' + "#, + bar.root().display() + ), + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + fn main() { + println!("cargo:rebuild-if-changed=build.rs"); + if std::path::Path::new("abort").exists() { + panic!("Crash!"); + } + } + "#, + ) + .build(); + + p.cargo("build").run(); + // Simulate the user hitting Ctrl-C during a build. + p.change_file("abort", ""); + // Set SOMEVAR to trigger a rebuild. + p.cargo("build") + .env("SOMEVAR", "1") + .with_stderr_contains("[..]Crash![..]") + .with_status(101) + .run(); + fs::remove_file(p.root().join("abort")).unwrap(); + // Try again without aborting the script. + // ***This is currently broken, the script does not re-run. + p.cargo("build -v") + .env("SOMEVAR", "1") + .with_stderr_contains("[RUNNING] [..]/foo-[..]/build-script-build[..]") + .run(); +} + +#[cargo_test] +fn dev_dep_with_links() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + links = "x" + + [dev-dependencies] + bar = { path = "./bar" } + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + links = "y" + + [dependencies] + foo = { path = ".." } + "#, + ) + .file("bar/build.rs", "fn main() {}") + .file("bar/src/lib.rs", "") + .build(); + p.cargo("check --tests").run() +} + +#[cargo_test] +fn rerun_if_directory() { + if !symlink_supported() { + return; + } + + // rerun-if-changed of a directory should rerun if any file in the directory changes. + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-changed=somedir"); + } + "#, + ) + .build(); + + let dirty = |dirty_line: &str, compile_build_script: bool| { + let mut dirty_line = dirty_line.to_string(); + + if !dirty_line.is_empty() { + dirty_line.push('\n'); + } + + let compile_build_script_line = if compile_build_script { + "[RUNNING] `rustc --crate-name build_script_build [..]\n" + } else { + "" + }; + + p.cargo("check -v") + .with_stderr(format!( + "\ +{dirty_line}\ +[COMPILING] foo [..] +{compile_build_script_line}\ +[RUNNING] `[..]build-script-build[..]` +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] [..]", + )) + .run(); + }; + + let fresh = || { + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + }; + + // Start with a missing directory. + dirty("", true); + // Because the directory doesn't exist, it will trigger a rebuild every time. + // https://github.com/rust-lang/cargo/issues/6003 + dirty( + "[DIRTY] foo v0.1.0 ([..]): the file `somedir` is missing", + false, + ); + + if is_coarse_mtime() { + sleep_ms(1000); + } + + // Empty directory. + fs::create_dir(p.root().join("somedir")).unwrap(); + dirty( + "[DIRTY] foo v0.1.0 ([..]): the file `somedir` has changed ([..])", + false, + ); + fresh(); + + if is_coarse_mtime() { + sleep_ms(1000); + } + + // Add a file. + p.change_file("somedir/foo", ""); + p.change_file("somedir/bar", ""); + dirty( + "[DIRTY] foo v0.1.0 ([..]): the file `somedir` has changed ([..])", + false, + ); + fresh(); + + if is_coarse_mtime() { + sleep_ms(1000); + } + + // Add a symlink. + p.symlink("foo", "somedir/link"); + dirty( + "[DIRTY] foo v0.1.0 ([..]): the file `somedir` has changed ([..])", + false, + ); + fresh(); + + if is_coarse_mtime() { + sleep_ms(1000); + } + + // Move the symlink. + fs::remove_file(p.root().join("somedir/link")).unwrap(); + p.symlink("bar", "somedir/link"); + dirty( + "[DIRTY] foo v0.1.0 ([..]): the file `somedir` has changed ([..])", + false, + ); + fresh(); + + if is_coarse_mtime() { + sleep_ms(1000); + } + + // Remove a file. + fs::remove_file(p.root().join("somedir/foo")).unwrap(); + dirty( + "[DIRTY] foo v0.1.0 ([..]): the file `somedir` has changed ([..])", + false, + ); + fresh(); +} + +#[cargo_test] +fn rerun_if_published_directory() { + // build script of a dependency contains a `rerun-if-changed` pointing to a directory + Package::new("mylib-sys", "1.0.0") + .file("mylib/balrog.c", "") + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + // Changing to mylib/balrog.c will not trigger a rebuild + println!("cargo:rerun-if-changed=mylib"); + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + mylib-sys = "1.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check").run(); + + // Delete regitry src to make directories being recreated with the latest timestamp. + cargo_home().join("registry/src").rm_rf(); + + p.cargo("check --verbose") + .with_stderr( + "\ +[FRESH] mylib-sys v1.0.0 +[FRESH] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + // Upgrade of a package should still trigger a rebuild + Package::new("mylib-sys", "1.0.1") + .file("mylib/balrog.c", "") + .file("mylib/balrog.h", "") + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-changed=mylib"); + } + "#, + ) + .publish(); + p.cargo("update").run(); + p.cargo("fetch").run(); + + p.cargo("check -v") + .with_stderr(format!( + "\ +[COMPILING] mylib-sys [..] +[RUNNING] `rustc --crate-name build_script_build [..] +[RUNNING] `[..]build-script-build[..]` +[RUNNING] `rustc --crate-name mylib_sys [..] +[CHECKING] foo [..] +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] [..]", + )) + .run(); +} + +#[cargo_test] +fn test_with_dep_metadata() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = 'bar' } + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + assert_eq!(std::env::var("DEP_BAR_FOO").unwrap(), "bar"); + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + links = 'bar' + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "bar/build.rs", + r#" + fn main() { + println!("cargo:foo=bar"); + } + "#, + ) + .build(); + p.cargo("test --lib").run(); +} + +#[cargo_test] +fn duplicate_script_with_extra_env() { + // Test where a build script is run twice, that emits different rustc-env + // and rustc-cfg values. In this case, one is run for host, the other for + // target. + if !cross_compile::can_run_on_host() { + return; + } + + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "pm"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + pm = { path = "../pm" } + "#, + ) + .file( + "foo/src/lib.rs", + &r#" + //! ```rust + //! #[cfg(not(mycfg="{target}"))] + //! compile_error!{"expected mycfg set"} + //! assert_eq!(env!("CRATE_TARGET"), "{target}"); + //! assert_eq!(std::env::var("CRATE_TARGET").unwrap(), "{target}"); + //! ``` + + #[test] + fn check_target() { + #[cfg(not(mycfg="{target}"))] + compile_error!{"expected mycfg set"} + // Compile-time assertion. + assert_eq!(env!("CRATE_TARGET"), "{target}"); + // Run-time assertion. + assert_eq!(std::env::var("CRATE_TARGET").unwrap(), "{target}"); + } + "# + .replace("{target}", target), + ) + .file( + "foo/build.rs", + r#" + fn main() { + println!("cargo:rustc-env=CRATE_TARGET={}", std::env::var("TARGET").unwrap()); + println!("cargo:rustc-cfg=mycfg=\"{}\"", std::env::var("TARGET").unwrap()); + } + "#, + ) + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + # This is just here to speed things up. + doctest = false + + [dev-dependencies] + foo = { path = "../foo" } + "#, + ) + .file("pm/src/lib.rs", "") + .build(); + + p.cargo("test --workspace --target") + .arg(&target) + .with_stdout_contains("test check_target ... ok") + .run(); + + if cargo_test_support::is_nightly() { + p.cargo("test --workspace -Z doctest-xcompile --doc --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["doctest-xcompile"]) + .with_stdout_contains("test src/lib.rs - (line 2) ... ok") + .run(); + } +} + +#[cargo_test] +fn wrong_output() { + let p = project() + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:example"); + } + "#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[COMPILING] foo [..] +error: invalid output in build script of `foo v0.0.1 ([ROOT]/foo)`: `cargo:example` +Expected a line with `cargo:key=value` with an `=` character, but none was found. +See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script \ +for more information about build script outputs. +", + ) + .run(); +} + +#[cargo_test] +fn custom_build_closes_stdin() { + // Ensure stdin is closed to prevent deadlock. + // See https://github.com/rust-lang/cargo/issues/11196 + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#"fn main() { + let mut line = String::new(); + std::io::stdin().read_line(&mut line).unwrap(); + }"#, + ) + .build(); + p.cargo("build").run(); +} diff --git a/tests/testsuite/build_script_env.rs b/tests/testsuite/build_script_env.rs new file mode 100644 index 0000000..6ad4ab9 --- /dev/null +++ b/tests/testsuite/build_script_env.rs @@ -0,0 +1,241 @@ +//! Tests for build.rs rerun-if-env-changed and rustc-env + +use cargo_test_support::basic_manifest; +use cargo_test_support::project; +use cargo_test_support::sleep_ms; + +#[cargo_test] +fn rerun_if_env_changes() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-env-changed=FOO"); + } + "#, + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); + p.cargo("check") + .env("FOO", "bar") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); + p.cargo("check") + .env("FOO", "baz") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); + p.cargo("check") + .env("FOO", "baz") + .with_stderr("[FINISHED] [..]") + .run(); + p.cargo("check") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn rerun_if_env_or_file_changes() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-env-changed=FOO"); + println!("cargo:rerun-if-changed=foo"); + } + "#, + ) + .file("foo", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); + p.cargo("check") + .env("FOO", "bar") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); + p.cargo("check") + .env("FOO", "bar") + .with_stderr("[FINISHED] [..]") + .run(); + sleep_ms(1000); + p.change_file("foo", ""); + p.cargo("check") + .env("FOO", "bar") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustc_bootstrap() { + let build_rs = r#" + fn main() { + println!("cargo:rustc-env=RUSTC_BOOTSTRAP=1"); + } + "#; + let p = project() + .file("Cargo.toml", &basic_manifest("has-dashes", "0.0.1")) + .file("src/lib.rs", "#![feature(rustc_attrs)]") + .file("build.rs", build_rs) + .build(); + // RUSTC_BOOTSTRAP unset on stable should error + p.cargo("check") + .with_stderr_contains("error: Cannot set `RUSTC_BOOTSTRAP=1` [..]") + .with_stderr_contains( + "help: [..] set the environment variable `RUSTC_BOOTSTRAP=has_dashes` [..]", + ) + .with_status(101) + .run(); + // nightly should warn whether or not RUSTC_BOOTSTRAP is set + p.cargo("check") + .masquerade_as_nightly_cargo(&["RUSTC_BOOTSTRAP"]) + // NOTE: uses RUSTC_BOOTSTRAP so it will be propagated to rustc + // (this matters when tests are being run with a beta or stable cargo) + .env("RUSTC_BOOTSTRAP", "1") + .with_stderr_contains("warning: Cannot set `RUSTC_BOOTSTRAP=1` [..]") + .run(); + // RUSTC_BOOTSTRAP set to the name of the library should warn + p.cargo("check") + .env("RUSTC_BOOTSTRAP", "has_dashes") + .with_stderr_contains("warning: Cannot set `RUSTC_BOOTSTRAP=1` [..]") + .run(); + // RUSTC_BOOTSTRAP set to some random value should error + p.cargo("check") + .env("RUSTC_BOOTSTRAP", "bar") + .with_stderr_contains("error: Cannot set `RUSTC_BOOTSTRAP=1` [..]") + .with_stderr_contains( + "help: [..] set the environment variable `RUSTC_BOOTSTRAP=has_dashes` [..]", + ) + .with_status(101) + .run(); + + // Tests for binaries instead of libraries + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.0.1")) + .file("src/main.rs", "#![feature(rustc_attrs)] fn main() {}") + .file("build.rs", build_rs) + .build(); + // nightly should warn when there's no library whether or not RUSTC_BOOTSTRAP is set + p.cargo("check") + .masquerade_as_nightly_cargo(&["RUSTC_BOOTSTRAP"]) + // NOTE: uses RUSTC_BOOTSTRAP so it will be propagated to rustc + // (this matters when tests are being run with a beta or stable cargo) + .env("RUSTC_BOOTSTRAP", "1") + .with_stderr_contains("warning: Cannot set `RUSTC_BOOTSTRAP=1` [..]") + .run(); + // RUSTC_BOOTSTRAP conditionally set when there's no library should error (regardless of the value) + p.cargo("check") + .env("RUSTC_BOOTSTRAP", "foo") + .with_stderr_contains("error: Cannot set `RUSTC_BOOTSTRAP=1` [..]") + .with_stderr_contains("help: [..] set the environment variable `RUSTC_BOOTSTRAP=1` [..]") + .with_status(101) + .run(); +} + +#[cargo_test] +#[cfg(target_arch = "x86_64")] +fn build_script_sees_cfg_target_feature() { + let build_rs = r#" + fn main() { + let cfg = std::env::var("CARGO_CFG_TARGET_FEATURE").unwrap(); + eprintln!("CARGO_CFG_TARGET_FEATURE={cfg}"); + } + "#; + + let configs = [ + r#" + [build] + rustflags = ["-Ctarget-feature=+sse4.1,+sse4.2"] + "#, + r#" + [target.'cfg(target_arch = "x86_64")'] + rustflags = ["-Ctarget-feature=+sse4.1,+sse4.2"] + "#, + ]; + + for config in configs { + let p = project() + .file(".cargo/config.toml", config) + .file("src/lib.rs", r#""#) + .file("build.rs", build_rs) + .build(); + + p.cargo("check -vv") + .with_stderr_contains("[foo 0.0.1] CARGO_CFG_TARGET_FEATURE=[..]sse4.2[..]") + .with_stderr_contains("[..]-Ctarget-feature=[..]+sse4.2[..]") + .run(); + } +} + +/// In this test, the cfg is self-contradictory. There's no *right* answer as to +/// what the value of `RUSTFLAGS` should be in this case. We chose to give a +/// warning. However, no matter what we do, it's important that build scripts +/// and rustc see a consistent picture +#[cargo_test] +fn cfg_paradox() { + let build_rs = r#" + fn main() { + let cfg = std::env::var("CARGO_CFG_BERTRAND").is_ok(); + eprintln!("cfg!(bertrand)={cfg}"); + } + "#; + + let config = r#" + [target.'cfg(not(bertrand))'] + rustflags = ["--cfg=bertrand"] + "#; + + let p = project() + .file(".cargo/config.toml", config) + .file("src/lib.rs", r#""#) + .file("build.rs", build_rs) + .build(); + + p.cargo("check -vv") + .with_stderr_contains("[WARNING] non-trivial mutual dependency between target-specific configuration and RUSTFLAGS") + .with_stderr_contains("[foo 0.0.1] cfg!(bertrand)=true") + .with_stderr_contains("[..]--cfg=bertrand[..]") + .run(); +} diff --git a/tests/testsuite/build_script_extra_link_arg.rs b/tests/testsuite/build_script_extra_link_arg.rs new file mode 100644 index 0000000..ade262f --- /dev/null +++ b/tests/testsuite/build_script_extra_link_arg.rs @@ -0,0 +1,376 @@ +//! Tests for additional link arguments. + +// NOTE: Many of these tests use `without_status()` when passing bogus flags +// because MSVC link.exe just gives a warning on unknown flags (how helpful!), +// and other linkers will return an error. + +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, basic_manifest, project}; + +#[cargo_test] +fn build_script_extra_link_arg_bin() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-arg-bins=--this-is-a-bogus-flag"); + } + "#, + ) + .build(); + + p.cargo("build -v") + .without_status() + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]", + ) + .run(); +} + +#[cargo_test] +fn build_script_extra_link_arg_bin_single() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foobar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [[bin]] + name = "foo" + [[bin]] + name = "bar" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-arg-bins=--bogus-flag-all"); + println!("cargo:rustc-link-arg-bin=foo=--bogus-flag-foo"); + println!("cargo:rustc-link-arg-bin=bar=--bogus-flag-bar"); + } + "#, + ) + .build(); + + p.cargo("build -v") + .without_status() + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-foo[..]", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-bar[..]", + ) + .run(); +} + +#[cargo_test] +fn build_script_extra_link_arg() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-arg=--this-is-a-bogus-flag"); + } + "#, + ) + .build(); + + p.cargo("build -v") + .without_status() + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]", + ) + .run(); +} + +#[cargo_test] +fn link_arg_missing_target() { + // Errors when a given target doesn't exist. + let p = project() + .file("src/lib.rs", "") + .file( + "build.rs", + r#"fn main() { println!("cargo:rustc-link-arg-cdylib=--bogus"); }"#, + ) + .build(); + + // TODO: Uncomment this if cdylib restriction is re-added (see + // cdylib_link_arg_transitive below). + // p.cargo("check") + // .with_status(101) + // .with_stderr("\ + // [COMPILING] foo [..] + // error: invalid instruction `cargo:rustc-link-arg-cdylib` from build script of `foo v0.0.1 ([ROOT]/foo)` + // The package foo v0.0.1 ([ROOT]/foo) does not have a cdylib target. + // ") + // .run(); + + p.change_file( + "build.rs", + r#"fn main() { println!("cargo:rustc-link-arg-bins=--bogus"); }"#, + ); + + p.cargo("check") + .with_status(101) + .with_stderr("\ +[COMPILING] foo [..] +error: invalid instruction `cargo:rustc-link-arg-bins` from build script of `foo v0.0.1 ([ROOT]/foo)` +The package foo v0.0.1 ([ROOT]/foo) does not have a bin target. +") + .run(); + + p.change_file( + "build.rs", + r#"fn main() { println!("cargo:rustc-link-arg-bin=abc=--bogus"); }"#, + ); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[COMPILING] foo [..] +error: invalid instruction `cargo:rustc-link-arg-bin` from build script of `foo v0.0.1 ([ROOT]/foo)` +The package foo v0.0.1 ([ROOT]/foo) does not have a bin target with the name `abc`. +", + ) + .run(); + + p.change_file( + "build.rs", + r#"fn main() { println!("cargo:rustc-link-arg-bin=abc"); }"#, + ); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[COMPILING] foo [..] +error: invalid instruction `cargo:rustc-link-arg-bin=abc` from build script of `foo v0.0.1 ([ROOT]/foo)` +The instruction should have the form cargo:rustc-link-arg-bin=BIN=ARG +", + ) + .run(); +} + +#[cargo_test] +fn cdylib_link_arg_transitive() { + // There was an unintended regression in 1.50 where rustc-link-arg-cdylib + // arguments from dependencies were being applied in the parent package. + // Previously it was silently ignored. + // See https://github.com/rust-lang/cargo/issues/9562 + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [lib] + crate-type = ["cdylib"] + + [dependencies] + bar = {path="bar"} + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0")) + .file("bar/src/lib.rs", "") + .file( + "bar/build.rs", + r#" + fn main() { + println!("cargo:rustc-link-arg-cdylib=--bogus"); + } + "#, + ) + .build(); + p.cargo("build -v") + .without_status() + .with_stderr_contains( + "\ +[COMPILING] bar v1.0.0 [..] +[RUNNING] `rustc --crate-name build_script_build bar/build.rs [..] +[RUNNING] `[..]build-script-build[..] +warning: cargo:rustc-link-arg-cdylib was specified in the build script of bar v1.0.0 \ +([ROOT]/foo/bar), but that package does not contain a cdylib target + +Allowing this was an unintended change in the 1.50 release, and may become an error in \ +the future. For more information, see . +[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..] +[COMPILING] foo v0.1.0 [..] +[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C link-arg=--bogus[..]` +", + ) + .run(); +} + +#[cargo_test] +fn link_arg_transitive_not_allowed() { + // Verify that transitive dependencies don't pass link args. + // + // Note that rustc-link-arg doesn't have any errors or warnings when it is + // unused. Perhaps that could be more aggressive, but it is difficult + // since it could be used for test binaries. + Package::new("bar", "1.0.0") + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-arg=--bogus"); + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [lib] + crate-type = ["cdylib"] + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] [..] +[DOWNLOADED] [..] +[COMPILING] bar v1.0.0 +[RUNNING] `rustc --crate-name build_script_build [..] +[RUNNING] `[..]/build-script-build[..] +[RUNNING] `rustc --crate-name bar [..] +[COMPILING] foo v0.1.0 [..] +[RUNNING] `rustc --crate-name foo src/lib.rs [..] +[FINISHED] dev [..] +", + ) + .with_stderr_does_not_contain("--bogus") + .run(); +} + +#[cargo_test] +fn link_arg_with_doctest() { + let p = project() + .file( + "src/lib.rs", + r#" + //! ``` + //! let x = 5; + //! assert_eq!(x, 5); + //! ``` + "#, + ) + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-arg=--this-is-a-bogus-flag"); + } + "#, + ) + .build(); + + p.cargo("test --doc -v") + .without_status() + .with_stderr_contains( + "[RUNNING] `rustdoc [..]--crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]", + ) + .run(); +} + +#[cargo_test] +fn build_script_extra_link_arg_tests() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file("tests/test_foo.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-arg-tests=--this-is-a-bogus-flag"); + } + "#, + ) + .build(); + + p.cargo("test -v") + .without_status() + .with_stderr_contains( + "[RUNNING] `rustc --crate-name test_foo [..]-C link-arg=--this-is-a-bogus-flag[..]", + ) + .run(); +} + +#[cargo_test] +fn build_script_extra_link_arg_benches() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file("benches/bench_foo.rs", "") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-arg-benches=--this-is-a-bogus-flag"); + } + "#, + ) + .build(); + + p.cargo("bench -v") + .without_status() + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bench_foo [..]-C link-arg=--this-is-a-bogus-flag[..]", + ) + .run(); +} + +#[cargo_test] +fn build_script_extra_link_arg_examples() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file("examples/example_foo.rs", "fn main() {}") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-link-arg-examples=--this-is-a-bogus-flag"); + } + "#, + ) + .build(); + + p.cargo("build -v --examples") + .without_status() + .with_stderr_contains( + "[RUNNING] `rustc --crate-name example_foo [..]-C link-arg=--this-is-a-bogus-flag[..]", + ) + .run(); +} diff --git a/tests/testsuite/cache_messages.rs b/tests/testsuite/cache_messages.rs new file mode 100644 index 0000000..b856ed1 --- /dev/null +++ b/tests/testsuite/cache_messages.rs @@ -0,0 +1,488 @@ +//! Tests for caching compiler diagnostics. + +use super::messages::raw_rustc_output; +use cargo_test_support::tools; +use cargo_test_support::{basic_manifest, is_coarse_mtime, project, registry::Package, sleep_ms}; + +fn as_str(bytes: &[u8]) -> &str { + std::str::from_utf8(bytes).expect("valid utf-8") +} + +#[cargo_test] +fn simple() { + // A simple example that generates two warnings (unused functions). + let p = project() + .file( + "src/lib.rs", + " + fn a() {} + fn b() {} + ", + ) + .build(); + + // Capture what rustc actually emits. This is done to avoid relying on the + // exact message formatting in rustc. + let rustc_output = raw_rustc_output(&p, "src/lib.rs", &[]); + + // -q so the output is the same as rustc (no "Compiling" or "Finished"). + let cargo_output1 = p + .cargo("check -q --color=never") + .exec_with_output() + .expect("cargo to run"); + assert_eq!(rustc_output, as_str(&cargo_output1.stderr)); + assert!(cargo_output1.stdout.is_empty()); + // Check that the cached version is exactly the same. + let cargo_output2 = p + .cargo("check -q") + .exec_with_output() + .expect("cargo to run"); + assert_eq!(rustc_output, as_str(&cargo_output2.stderr)); + assert!(cargo_output2.stdout.is_empty()); +} + +// same as `simple`, except everything is using the short format +#[cargo_test] +fn simple_short() { + let p = project() + .file( + "src/lib.rs", + " + fn a() {} + fn b() {} + ", + ) + .build(); + + let rustc_output = raw_rustc_output(&p, "src/lib.rs", &["--error-format=short"]); + + let cargo_output1 = p + .cargo("check -q --color=never --message-format=short") + .exec_with_output() + .expect("cargo to run"); + assert_eq!(rustc_output, as_str(&cargo_output1.stderr)); + // assert!(cargo_output1.stdout.is_empty()); + let cargo_output2 = p + .cargo("check -q --message-format=short") + .exec_with_output() + .expect("cargo to run"); + println!("{}", String::from_utf8_lossy(&cargo_output2.stdout)); + assert_eq!(rustc_output, as_str(&cargo_output2.stderr)); + assert!(cargo_output2.stdout.is_empty()); +} + +#[cargo_test] +fn color() { + // Check enabling/disabling color. + let p = project().file("src/lib.rs", "fn a() {}").build(); + + // Hack for issue in fwdansi 1.1. It is squashing multiple resets + // into a single reset. + // https://github.com/kennytm/fwdansi/issues/2 + fn normalize(s: &str) -> String { + #[cfg(windows)] + return s.replace("\x1b[0m\x1b[0m", "\x1b[0m"); + #[cfg(not(windows))] + return s.to_string(); + } + + let compare = |a, b| { + assert_eq!(normalize(a), normalize(b)); + }; + + // Capture the original color output. + let rustc_color = raw_rustc_output(&p, "src/lib.rs", &["--color=always"]); + assert!(rustc_color.contains("\x1b[")); + + // Capture the original non-color output. + let rustc_nocolor = raw_rustc_output(&p, "src/lib.rs", &[]); + assert!(!rustc_nocolor.contains("\x1b[")); + + // First pass, non-cached, with color, should be the same. + let cargo_output1 = p + .cargo("check -q --color=always") + .exec_with_output() + .expect("cargo to run"); + compare(&rustc_color, as_str(&cargo_output1.stderr)); + + // Replay cached, with color. + let cargo_output2 = p + .cargo("check -q --color=always") + .exec_with_output() + .expect("cargo to run"); + compare(&rustc_color, as_str(&cargo_output2.stderr)); + + // Replay cached, no color. + let cargo_output_nocolor = p + .cargo("check -q --color=never") + .exec_with_output() + .expect("cargo to run"); + compare(&rustc_nocolor, as_str(&cargo_output_nocolor.stderr)); +} + +#[cargo_test] +fn cached_as_json() { + // Check that cached JSON output is the same. + let p = project().file("src/lib.rs", "fn a() {}").build(); + + // Grab the non-cached output, feature disabled. + // NOTE: When stabilizing, this will need to be redone. + let cargo_output = p + .cargo("check --message-format=json") + .exec_with_output() + .expect("cargo to run"); + assert!(cargo_output.status.success()); + let orig_cargo_out = as_str(&cargo_output.stdout); + assert!(orig_cargo_out.contains("compiler-message")); + p.cargo("clean").run(); + + // Check JSON output, not fresh. + let cargo_output1 = p + .cargo("check --message-format=json") + .exec_with_output() + .expect("cargo to run"); + assert_eq!(as_str(&cargo_output1.stdout), orig_cargo_out); + + // Check JSON output, fresh. + let cargo_output2 = p + .cargo("check --message-format=json") + .exec_with_output() + .expect("cargo to run"); + // The only difference should be this field. + let fix_fresh = as_str(&cargo_output2.stdout).replace("\"fresh\":true", "\"fresh\":false"); + assert_eq!(fix_fresh, orig_cargo_out); +} + +#[cargo_test] +fn clears_cache_after_fix() { + // Make sure the cache is invalidated when there is no output. + let p = project().file("src/lib.rs", "fn asdf() {}").build(); + // Fill the cache. + p.cargo("check").with_stderr_contains("[..]asdf[..]").run(); + let cpath = p + .glob("target/debug/.fingerprint/foo-*/output-*") + .next() + .unwrap() + .unwrap(); + assert!(std::fs::read_to_string(cpath).unwrap().contains("asdf")); + + // Fix it. + if is_coarse_mtime() { + sleep_ms(1000); + } + p.change_file("src/lib.rs", ""); + + p.cargo("check") + .with_stdout("") + .with_stderr( + "\ +[CHECKING] foo [..] +[FINISHED] [..] +", + ) + .run(); + assert_eq!( + p.glob("target/debug/.fingerprint/foo-*/output-*").count(), + 0 + ); + + // And again, check the cache is correct. + p.cargo("check") + .with_stdout("") + .with_stderr( + "\ +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustdoc() { + // Create a warning in rustdoc. + let p = project() + .file( + "src/lib.rs", + " + #![warn(missing_docs)] + pub fn f() {} + ", + ) + .build(); + + let rustdoc_output = p + .cargo("doc -q --color=always") + .exec_with_output() + .expect("rustdoc to run"); + assert!(rustdoc_output.status.success()); + let rustdoc_stderr = as_str(&rustdoc_output.stderr); + assert!(rustdoc_stderr.contains("missing")); + assert!(rustdoc_stderr.contains("\x1b[")); + assert_eq!( + p.glob("target/debug/.fingerprint/foo-*/output-*").count(), + 1 + ); + + // Check the cached output. + let rustdoc_output = p + .cargo("doc -q --color=always") + .exec_with_output() + .expect("rustdoc to run"); + assert_eq!(as_str(&rustdoc_output.stderr), rustdoc_stderr); +} + +#[cargo_test] +fn fix() { + // Make sure `fix` is not broken by caching. + let p = project().file("src/lib.rs", "pub fn try() {}").build(); + + p.cargo("fix --edition --allow-no-vcs").run(); + + assert_eq!(p.read_file("src/lib.rs"), "pub fn r#try() {}"); +} + +#[cargo_test] +fn very_verbose() { + // Handle cap-lints in dependencies. + Package::new("bar", "1.0.0") + .file("src/lib.rs", "fn not_used() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -vv") + .with_stderr_contains("[..]not_used[..]") + .run(); + + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + + p.cargo("check -vv") + .with_stderr_contains("[..]not_used[..]") + .run(); +} + +#[cargo_test] +fn doesnt_create_extra_files() { + // Ensure it doesn't create `output` files when not needed. + Package::new("dep", "1.0.0") + .file("src/lib.rs", "fn unused() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = "1.0" + "#, + ) + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check").run(); + + assert_eq!( + p.glob("target/debug/.fingerprint/foo-*/output-*").count(), + 0 + ); + assert_eq!( + p.glob("target/debug/.fingerprint/dep-*/output-*").count(), + 0 + ); + if is_coarse_mtime() { + sleep_ms(1000); + } + p.change_file("src/lib.rs", "fn unused() {}"); + p.cargo("check").run(); + assert_eq!( + p.glob("target/debug/.fingerprint/foo-*/output-*").count(), + 1 + ); +} + +#[cargo_test] +fn replay_non_json() { + // Handles non-json output. + let rustc = project() + .at("rustc") + .file("Cargo.toml", &basic_manifest("rustc_alt", "1.0.0")) + .file( + "src/main.rs", + r#" + fn main() { + eprintln!("line 1"); + eprintln!("line 2"); + let r = std::process::Command::new("rustc") + .args(std::env::args_os().skip(1)) + .status(); + std::process::exit(r.unwrap().code().unwrap_or(2)); + } + "#, + ) + .build(); + rustc.cargo("build").run(); + let p = project().file("src/lib.rs", "").build(); + p.cargo("check") + .env("RUSTC", rustc.bin("rustc_alt")) + .with_stderr( + "\ +[CHECKING] foo [..] +line 1 +line 2 +[FINISHED] dev [..] +", + ) + .run(); + + p.cargo("check") + .env("RUSTC", rustc.bin("rustc_alt")) + .with_stderr( + "\ +line 1 +line 2 +[FINISHED] dev [..] +", + ) + .run(); +} + +#[cargo_test] +fn caching_large_output() { + // Handles large number of messages. + // This is an arbitrary amount that is greater than the 100 used in + // job_queue. This is here to check for deadlocks or any other problems. + const COUNT: usize = 250; + let rustc = project() + .at("rustc") + .file("Cargo.toml", &basic_manifest("rustc_alt", "1.0.0")) + .file( + "src/main.rs", + &format!( + r#" + fn main() {{ + for i in 0..{} {{ + eprintln!("{{{{\"message\": \"test message {{}}\", \"level\": \"warning\", \ + \"spans\": [], \"children\": [], \"rendered\": \"test message {{}}\"}}}}", + i, i); + }} + let r = std::process::Command::new("rustc") + .args(std::env::args_os().skip(1)) + .status(); + std::process::exit(r.unwrap().code().unwrap_or(2)); + }} + "#, + COUNT + ), + ) + .build(); + + let mut expected = String::new(); + for i in 0..COUNT { + expected.push_str(&format!("test message {}\n", i)); + } + + rustc.cargo("build").run(); + let p = project().file("src/lib.rs", "").build(); + p.cargo("check") + .env("RUSTC", rustc.bin("rustc_alt")) + .with_stderr(&format!( + "\ +[CHECKING] foo [..] +{}warning: `foo` (lib) generated 250 warnings +[FINISHED] dev [..] +", + expected + )) + .run(); + + p.cargo("check") + .env("RUSTC", rustc.bin("rustc_alt")) + .with_stderr(&format!( + "\ +{}warning: `foo` (lib) generated 250 warnings +[FINISHED] dev [..] +", + expected + )) + .run(); +} + +#[cargo_test] +fn rustc_workspace_wrapper() { + let p = project() + .file( + "src/lib.rs", + "pub fn f() { assert!(true); }\n\ + fn unused_func() {}", + ) + .build(); + + p.cargo("check -v") + .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) + .with_stderr_contains("WRAPPER CALLED: rustc --crate-name foo src/lib.rs [..]") + .run(); + + // Check without a wrapper should rebuild + p.cargo("check -v") + .with_stderr_contains( + "\ +[CHECKING] foo [..] +[RUNNING] `rustc[..] +[WARNING] [..]unused_func[..] +", + ) + .with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name foo src/lib.rs [..]") + .run(); + + // Again, reading from the cache. + p.cargo("check -v") + .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) + .with_stderr_contains("[FRESH] foo [..]") + .with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name foo src/lib.rs [..]") + .run(); + + // And `check` should also be fresh, reading from cache. + p.cargo("check -v") + .with_stderr_contains("[FRESH] foo [..]") + .with_stderr_contains("[WARNING] [..]unused_func[..]") + .with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name foo src/lib.rs [..]") + .run(); +} + +#[cargo_test] +fn wacky_hashless_fingerprint() { + // On Windows, executables don't have hashes. This checks for a bad + // assumption that caused bad caching. + let p = project() + .file("src/bin/a.rs", "fn main() { let unused = 1; }") + .file("src/bin/b.rs", "fn main() {}") + .build(); + p.cargo("check --bin b") + .with_stderr_does_not_contain("[..]unused[..]") + .run(); + p.cargo("check --bin a") + .with_stderr_contains("[..]unused[..]") + .run(); + // This should not pick up the cache from `a`. + p.cargo("check --bin b") + .with_stderr_does_not_contain("[..]unused[..]") + .run(); +} diff --git a/tests/testsuite/cargo_add/add-basic.in/Cargo.toml b/tests/testsuite/cargo_add/add-basic.in/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/add-basic.in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/add-basic.in/src/lib.rs b/tests/testsuite/cargo_add/add-basic.in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/add_basic/in b/tests/testsuite/cargo_add/add_basic/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/add_basic/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/add_basic/mod.rs b/tests/testsuite/cargo_add/add_basic/mod.rs new file mode 100644 index 0000000..33889df --- /dev/null +++ b/tests/testsuite/cargo_add/add_basic/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/add_basic/out/Cargo.toml b/tests/testsuite/cargo_add/add_basic/out/Cargo.toml new file mode 100644 index 0000000..5964c87 --- /dev/null +++ b/tests/testsuite/cargo_add/add_basic/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package = "99999.0.0" diff --git a/tests/testsuite/cargo_add/add_basic/stderr.log b/tests/testsuite/cargo_add/add_basic/stderr.log new file mode 100644 index 0000000..fd6b711 --- /dev/null +++ b/tests/testsuite/cargo_add/add_basic/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding my-package v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/add_basic/stdout.log b/tests/testsuite/cargo_add/add_basic/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/add_multiple/in b/tests/testsuite/cargo_add/add_multiple/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/add_multiple/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/add_multiple/mod.rs b/tests/testsuite/cargo_add/add_multiple/mod.rs new file mode 100644 index 0000000..a9cc205 --- /dev/null +++ b/tests/testsuite/cargo_add/add_multiple/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/add_multiple/out/Cargo.toml b/tests/testsuite/cargo_add/add_multiple/out/Cargo.toml new file mode 100644 index 0000000..ba8d7ea --- /dev/null +++ b/tests/testsuite/cargo_add/add_multiple/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = "99999.0.0" +my-package2 = "99999.0.0" diff --git a/tests/testsuite/cargo_add/add_multiple/stderr.log b/tests/testsuite/cargo_add/add_multiple/stderr.log new file mode 100644 index 0000000..d0b4e73 --- /dev/null +++ b/tests/testsuite/cargo_add/add_multiple/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies. + Adding my-package2 v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/add_multiple/stdout.log b/tests/testsuite/cargo_add/add_multiple/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/add_normalized_name_external/in b/tests/testsuite/cargo_add/add_normalized_name_external/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/add_normalized_name_external/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs b/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs new file mode 100644 index 0000000..63605d8 --- /dev/null +++ b/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("linked_hash_map Inflector") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/add_normalized_name_external/out/Cargo.toml b/tests/testsuite/cargo_add/add_normalized_name_external/out/Cargo.toml new file mode 100644 index 0000000..3d0dec3 --- /dev/null +++ b/tests/testsuite/cargo_add/add_normalized_name_external/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +inflector = "0.11.4" +linked-hash-map = "0.5.4" diff --git a/tests/testsuite/cargo_add/add_normalized_name_external/stderr.log b/tests/testsuite/cargo_add/add_normalized_name_external/stderr.log new file mode 100644 index 0000000..c7d4511 --- /dev/null +++ b/tests/testsuite/cargo_add/add_normalized_name_external/stderr.log @@ -0,0 +1,18 @@ + Updating `dummy-registry` index +warning: translating `linked_hash_map` to `linked-hash-map` +warning: translating `Inflector` to `inflector` + Adding linked-hash-map v0.5.4 to dependencies. + Features: + - clippy + - heapsize + - heapsize_impl + - nightly + - serde + - serde_impl + - serde_test + Adding inflector v0.11.4 to dependencies. + Features: + + heavyweight + + lazy_static + + regex + - unstable diff --git a/tests/testsuite/cargo_add/add_normalized_name_external/stdout.log b/tests/testsuite/cargo_add/add_normalized_name_external/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/build/in b/tests/testsuite/cargo_add/build/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/build/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/build/mod.rs b/tests/testsuite/cargo_add/build/mod.rs new file mode 100644 index 0000000..130ecfb --- /dev/null +++ b/tests/testsuite/cargo_add/build/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("--build my-build-package1 my-build-package2") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/build/out/Cargo.toml b/tests/testsuite/cargo_add/build/out/Cargo.toml new file mode 100644 index 0000000..cceb448 --- /dev/null +++ b/tests/testsuite/cargo_add/build/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[build-dependencies] +my-build-package1 = "99999.0.0" +my-build-package2 = "99999.0.0" diff --git a/tests/testsuite/cargo_add/build/stderr.log b/tests/testsuite/cargo_add/build/stderr.log new file mode 100644 index 0000000..b873c5a --- /dev/null +++ b/tests/testsuite/cargo_add/build/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-build-package1 v99999.0.0 to build-dependencies. + Adding my-build-package2 v99999.0.0 to build-dependencies. diff --git a/tests/testsuite/cargo_add/build/stdout.log b/tests/testsuite/cargo_add/build/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/build_prefer_existing_version/in/Cargo.toml b/tests/testsuite/cargo_add/build_prefer_existing_version/in/Cargo.toml new file mode 100644 index 0000000..6a6ac82 --- /dev/null +++ b/tests/testsuite/cargo_add/build_prefer_existing_version/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +exclude = ["dependency"] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["one", "two"], registry = "alternative" } diff --git a/tests/testsuite/cargo_add/build_prefer_existing_version/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/build_prefer_existing_version/in/dependency/Cargo.toml new file mode 100644 index 0000000..58b909c --- /dev/null +++ b/tests/testsuite/cargo_add/build_prefer_existing_version/in/dependency/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" + +[features] +one = [] +two = [] diff --git a/tests/testsuite/cargo_add/build_prefer_existing_version/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/build_prefer_existing_version/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/build_prefer_existing_version/in/src/lib.rs b/tests/testsuite/cargo_add/build_prefer_existing_version/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs b/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs new file mode 100644 index 0000000..b0bb2e0 --- /dev/null +++ b/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs @@ -0,0 +1,28 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_alt_registry; + +#[cargo_test] +fn case() { + init_alt_registry(); + let project = + Project::from_template("tests/testsuite/cargo_add/build_prefer_existing_version/in"); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency --build") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path("tests/testsuite/cargo_add/build_prefer_existing_version/stdout.log") + .stderr_matches_path("tests/testsuite/cargo_add/build_prefer_existing_version/stderr.log"); + + assert_ui().subset_matches( + "tests/testsuite/cargo_add/build_prefer_existing_version/out", + &project_root, + ); +} diff --git a/tests/testsuite/cargo_add/build_prefer_existing_version/out/Cargo.toml b/tests/testsuite/cargo_add/build_prefer_existing_version/out/Cargo.toml new file mode 100644 index 0000000..123af6d --- /dev/null +++ b/tests/testsuite/cargo_add/build_prefer_existing_version/out/Cargo.toml @@ -0,0 +1,12 @@ +[workspace] +exclude = ["dependency"] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["one", "two"], registry = "alternative" } + +[build-dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", path = "dependency", registry = "alternative" } diff --git a/tests/testsuite/cargo_add/build_prefer_existing_version/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/build_prefer_existing_version/out/dependency/Cargo.toml new file mode 100644 index 0000000..58b909c --- /dev/null +++ b/tests/testsuite/cargo_add/build_prefer_existing_version/out/dependency/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" + +[features] +one = [] +two = [] diff --git a/tests/testsuite/cargo_add/build_prefer_existing_version/stderr.log b/tests/testsuite/cargo_add/build_prefer_existing_version/stderr.log new file mode 100644 index 0000000..554aa2e --- /dev/null +++ b/tests/testsuite/cargo_add/build_prefer_existing_version/stderr.log @@ -0,0 +1,4 @@ + Adding cargo-list-test-fixture-dependency (local) to build-dependencies. + Features: + - one + - two diff --git a/tests/testsuite/cargo_add/build_prefer_existing_version/stdout.log b/tests/testsuite/cargo_add/build_prefer_existing_version/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/change_rename_target/in/Cargo.toml b/tests/testsuite/cargo_add/change_rename_target/in/Cargo.toml new file mode 100644 index 0000000..e81a76b --- /dev/null +++ b/tests/testsuite/cargo_add/change_rename_target/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +some-package = { package = "my-package1", version = "0.1.1", optional = true } diff --git a/tests/testsuite/cargo_add/change_rename_target/in/src/lib.rs b/tests/testsuite/cargo_add/change_rename_target/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/change_rename_target/mod.rs b/tests/testsuite/cargo_add/change_rename_target/mod.rs new file mode 100644 index 0000000..94309b3 --- /dev/null +++ b/tests/testsuite/cargo_add/change_rename_target/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package2 --rename some-package") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml b/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml new file mode 100644 index 0000000..70cd318 --- /dev/null +++ b/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +some-package = { package = "my-package2", version = "99999.0.0", optional = true } diff --git a/tests/testsuite/cargo_add/change_rename_target/stderr.log b/tests/testsuite/cargo_add/change_rename_target/stderr.log new file mode 100644 index 0000000..674f626 --- /dev/null +++ b/tests/testsuite/cargo_add/change_rename_target/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding my-package2 v99999.0.0 to optional dependencies. diff --git a/tests/testsuite/cargo_add/change_rename_target/stdout.log b/tests/testsuite/cargo_add/change_rename_target/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/default_features/in b/tests/testsuite/cargo_add/default_features/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/default_features/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/default_features/mod.rs b/tests/testsuite/cargo_add/default_features/mod.rs new file mode 100644 index 0000000..88bdd80 --- /dev/null +++ b/tests/testsuite/cargo_add/default_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --default-features") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/default_features/out/Cargo.toml b/tests/testsuite/cargo_add/default_features/out/Cargo.toml new file mode 100644 index 0000000..c5e0178 --- /dev/null +++ b/tests/testsuite/cargo_add/default_features/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = "99999.0.0" +my-package2 = "0.4.1" diff --git a/tests/testsuite/cargo_add/default_features/stderr.log b/tests/testsuite/cargo_add/default_features/stderr.log new file mode 100644 index 0000000..fb8d490 --- /dev/null +++ b/tests/testsuite/cargo_add/default_features/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies. + Adding my-package2 v0.4.1 to dependencies. diff --git a/tests/testsuite/cargo_add/default_features/stdout.log b/tests/testsuite/cargo_add/default_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/deprecated_default_features/in/Cargo.toml b/tests/testsuite/cargo_add/deprecated_default_features/in/Cargo.toml new file mode 100644 index 0000000..c0fc374 --- /dev/null +++ b/tests/testsuite/cargo_add/deprecated_default_features/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package = { version = "99999.0.0", default_features = false } diff --git a/tests/testsuite/cargo_add/deprecated_default_features/in/src/lib.rs b/tests/testsuite/cargo_add/deprecated_default_features/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/deprecated_default_features/mod.rs b/tests/testsuite/cargo_add/deprecated_default_features/mod.rs new file mode 100644 index 0000000..10d4e4e --- /dev/null +++ b/tests/testsuite/cargo_add/deprecated_default_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package") + .current_dir(&cwd) + .assert() + .failure() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/deprecated_default_features/out/Cargo.toml b/tests/testsuite/cargo_add/deprecated_default_features/out/Cargo.toml new file mode 100644 index 0000000..c0fc374 --- /dev/null +++ b/tests/testsuite/cargo_add/deprecated_default_features/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package = { version = "99999.0.0", default_features = false } diff --git a/tests/testsuite/cargo_add/deprecated_default_features/stderr.log b/tests/testsuite/cargo_add/deprecated_default_features/stderr.log new file mode 100644 index 0000000..46d99d1 --- /dev/null +++ b/tests/testsuite/cargo_add/deprecated_default_features/stderr.log @@ -0,0 +1 @@ +error: Use of `default_features` in `my-package` is unsupported, please switch to `default-features` diff --git a/tests/testsuite/cargo_add/deprecated_default_features/stdout.log b/tests/testsuite/cargo_add/deprecated_default_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/deprecated_section/in/Cargo.toml b/tests/testsuite/cargo_add/deprecated_section/in/Cargo.toml new file mode 100644 index 0000000..a83d2c6 --- /dev/null +++ b/tests/testsuite/cargo_add/deprecated_section/in/Cargo.toml @@ -0,0 +1,11 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dev_dependencies] +my-package = "99999.0.0" + +[build_dependencies] +my-package = "99999.0.0" diff --git a/tests/testsuite/cargo_add/deprecated_section/in/src/lib.rs b/tests/testsuite/cargo_add/deprecated_section/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/deprecated_section/mod.rs b/tests/testsuite/cargo_add/deprecated_section/mod.rs new file mode 100644 index 0000000..10d4e4e --- /dev/null +++ b/tests/testsuite/cargo_add/deprecated_section/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package") + .current_dir(&cwd) + .assert() + .failure() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/deprecated_section/out/Cargo.toml b/tests/testsuite/cargo_add/deprecated_section/out/Cargo.toml new file mode 100644 index 0000000..a83d2c6 --- /dev/null +++ b/tests/testsuite/cargo_add/deprecated_section/out/Cargo.toml @@ -0,0 +1,11 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dev_dependencies] +my-package = "99999.0.0" + +[build_dependencies] +my-package = "99999.0.0" diff --git a/tests/testsuite/cargo_add/deprecated_section/stderr.log b/tests/testsuite/cargo_add/deprecated_section/stderr.log new file mode 100644 index 0000000..b3b9c10 --- /dev/null +++ b/tests/testsuite/cargo_add/deprecated_section/stderr.log @@ -0,0 +1 @@ +error: Deprecated dependency sections are unsupported: dev_dependencies, build_dependencies diff --git a/tests/testsuite/cargo_add/deprecated_section/stdout.log b/tests/testsuite/cargo_add/deprecated_section/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/in/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit/in/Cargo.toml new file mode 100644 index 0000000..24c5055 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit/in/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/detect_workspace_inherit/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/in/primary/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit/in/primary/Cargo.toml new file mode 100644 index 0000000..b867edb --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "bar" +version = "0.0.0" \ No newline at end of file diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/in/primary/src/lib.rs b/tests/testsuite/cargo_add/detect_workspace_inherit/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs b/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs new file mode 100644 index 0000000..065fb4f --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "-p", "bar"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/out/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit/out/Cargo.toml new file mode 100644 index 0000000..24c5055 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit/out/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/out/primary/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit/out/primary/Cargo.toml new file mode 100644 index 0000000..a574094 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo.workspace = true diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/stderr.log b/tests/testsuite/cargo_add/detect_workspace_inherit/stderr.log new file mode 100644 index 0000000..d2efcc0 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit/stderr.log @@ -0,0 +1 @@ + Adding foo (workspace) to dependencies. diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit/stdout.log b/tests/testsuite/cargo_add/detect_workspace_inherit/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/Cargo.toml new file mode 100644 index 0000000..b1d9b39 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency", features = ["merge"] } diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/dependency/Cargo.toml new file mode 100644 index 0000000..f34d7a6 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/dependency/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "foo" +version = "0.0.0" + +[features] +default-base = [] +default-test-base = [] +default-merge-base = [] +default = ["default-base", "default-test-base", "default-merge-base"] +test-base = [] +test = ["test-base", "default-test-base"] +merge-base = [] +merge = ["merge-base", "default-merge-base"] +unrelated = [] \ No newline at end of file diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/primary/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/primary/Cargo.toml new file mode 100644 index 0000000..b867edb --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "bar" +version = "0.0.0" \ No newline at end of file diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/primary/src/lib.rs b/tests/testsuite/cargo_add/detect_workspace_inherit_features/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs b/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs new file mode 100644 index 0000000..11ab2b1 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "-p", "bar", "--features", "test"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/out/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_features/out/Cargo.toml new file mode 100644 index 0000000..b1d9b39 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_features/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency", features = ["merge"] } diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_features/out/dependency/Cargo.toml new file mode 100644 index 0000000..f34d7a6 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_features/out/dependency/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "foo" +version = "0.0.0" + +[features] +default-base = [] +default-test-base = [] +default-merge-base = [] +default = ["default-base", "default-test-base", "default-merge-base"] +test-base = [] +test = ["test-base", "default-test-base"] +merge-base = [] +merge = ["merge-base", "default-merge-base"] +unrelated = [] \ No newline at end of file diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/out/primary/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_features/out/primary/Cargo.toml new file mode 100644 index 0000000..fb4a126 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_features/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, features = ["test"] } diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/stderr.log b/tests/testsuite/cargo_add/detect_workspace_inherit_features/stderr.log new file mode 100644 index 0000000..02dde7a --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_features/stderr.log @@ -0,0 +1,10 @@ + Adding foo (workspace) to dependencies. + Features as of v0.0.0: + + default-base + + default-merge-base + + default-test-base + + merge + + merge-base + + test + + test-base + - unrelated diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_features/stdout.log b/tests/testsuite/cargo_add/detect_workspace_inherit_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/Cargo.toml new file mode 100644 index 0000000..24c5055 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/primary/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/primary/Cargo.toml new file mode 100644 index 0000000..b867edb --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "bar" +version = "0.0.0" \ No newline at end of file diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/primary/src/lib.rs b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs new file mode 100644 index 0000000..7557b52 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "-p", "bar", "--optional"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/Cargo.toml new file mode 100644 index 0000000..24c5055 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml new file mode 100644 index 0000000..6dd7fb6 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, optional = true } diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/stderr.log b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/stderr.log new file mode 100644 index 0000000..da03b11 --- /dev/null +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/stderr.log @@ -0,0 +1 @@ + Adding foo (workspace) to optional dependencies. diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/stdout.log b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/dev/in b/tests/testsuite/cargo_add/dev/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/dev/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/dev/mod.rs b/tests/testsuite/cargo_add/dev/mod.rs new file mode 100644 index 0000000..112e922 --- /dev/null +++ b/tests/testsuite/cargo_add/dev/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("--dev my-dev-package1 my-dev-package2") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/dev/out/Cargo.toml b/tests/testsuite/cargo_add/dev/out/Cargo.toml new file mode 100644 index 0000000..28d9e81 --- /dev/null +++ b/tests/testsuite/cargo_add/dev/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dev-dependencies] +my-dev-package1 = "99999.0.0" +my-dev-package2 = "99999.0.0" diff --git a/tests/testsuite/cargo_add/dev/stderr.log b/tests/testsuite/cargo_add/dev/stderr.log new file mode 100644 index 0000000..f8e187c --- /dev/null +++ b/tests/testsuite/cargo_add/dev/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-dev-package1 v99999.0.0 to dev-dependencies. + Adding my-dev-package2 v99999.0.0 to dev-dependencies. diff --git a/tests/testsuite/cargo_add/dev/stdout.log b/tests/testsuite/cargo_add/dev/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/dev_build_conflict/in b/tests/testsuite/cargo_add/dev_build_conflict/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/dev_build_conflict/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/dev_build_conflict/mod.rs b/tests/testsuite/cargo_add/dev_build_conflict/mod.rs new file mode 100644 index 0000000..3f57c6b --- /dev/null +++ b/tests/testsuite/cargo_add/dev_build_conflict/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package --dev --build") + .current_dir(cwd) + .assert() + .code(1) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/dev_build_conflict/out/Cargo.toml b/tests/testsuite/cargo_add/dev_build_conflict/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/dev_build_conflict/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/dev_build_conflict/stderr.log b/tests/testsuite/cargo_add/dev_build_conflict/stderr.log new file mode 100644 index 0000000..69c5209 --- /dev/null +++ b/tests/testsuite/cargo_add/dev_build_conflict/stderr.log @@ -0,0 +1,7 @@ +error: the argument '--dev' cannot be used with '--build' + +Usage: cargo add [OPTIONS] [@] ... + cargo add [OPTIONS] --path ... + cargo add [OPTIONS] --git ... + +For more information, try '--help'. diff --git a/tests/testsuite/cargo_add/dev_build_conflict/stdout.log b/tests/testsuite/cargo_add/dev_build_conflict/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/dev_prefer_existing_version/in/Cargo.toml b/tests/testsuite/cargo_add/dev_prefer_existing_version/in/Cargo.toml new file mode 100644 index 0000000..6a6ac82 --- /dev/null +++ b/tests/testsuite/cargo_add/dev_prefer_existing_version/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +exclude = ["dependency"] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["one", "two"], registry = "alternative" } diff --git a/tests/testsuite/cargo_add/dev_prefer_existing_version/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/dev_prefer_existing_version/in/dependency/Cargo.toml new file mode 100644 index 0000000..58b909c --- /dev/null +++ b/tests/testsuite/cargo_add/dev_prefer_existing_version/in/dependency/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" + +[features] +one = [] +two = [] diff --git a/tests/testsuite/cargo_add/dev_prefer_existing_version/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/dev_prefer_existing_version/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/dev_prefer_existing_version/in/src/lib.rs b/tests/testsuite/cargo_add/dev_prefer_existing_version/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs b/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs new file mode 100644 index 0000000..1785ac8 --- /dev/null +++ b/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_alt_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_alt_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency --dev") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/dev_prefer_existing_version/out/Cargo.toml b/tests/testsuite/cargo_add/dev_prefer_existing_version/out/Cargo.toml new file mode 100644 index 0000000..247f345 --- /dev/null +++ b/tests/testsuite/cargo_add/dev_prefer_existing_version/out/Cargo.toml @@ -0,0 +1,12 @@ +[workspace] +exclude = ["dependency"] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["one", "two"], registry = "alternative" } + +[dev-dependencies] +cargo-list-test-fixture-dependency = { path = "dependency" } diff --git a/tests/testsuite/cargo_add/dev_prefer_existing_version/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/dev_prefer_existing_version/out/dependency/Cargo.toml new file mode 100644 index 0000000..58b909c --- /dev/null +++ b/tests/testsuite/cargo_add/dev_prefer_existing_version/out/dependency/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" + +[features] +one = [] +two = [] diff --git a/tests/testsuite/cargo_add/dev_prefer_existing_version/stderr.log b/tests/testsuite/cargo_add/dev_prefer_existing_version/stderr.log new file mode 100644 index 0000000..32f9a3e --- /dev/null +++ b/tests/testsuite/cargo_add/dev_prefer_existing_version/stderr.log @@ -0,0 +1,4 @@ + Adding cargo-list-test-fixture-dependency (local) to dev-dependencies. + Features as of v0.0.0: + - one + - two diff --git a/tests/testsuite/cargo_add/dev_prefer_existing_version/stdout.log b/tests/testsuite/cargo_add/dev_prefer_existing_version/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/dry_run/in b/tests/testsuite/cargo_add/dry_run/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/dry_run/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/dry_run/mod.rs b/tests/testsuite/cargo_add/dry_run/mod.rs new file mode 100644 index 0000000..209d208 --- /dev/null +++ b/tests/testsuite/cargo_add/dry_run/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package --dry-run") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/dry_run/out/Cargo.toml b/tests/testsuite/cargo_add/dry_run/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/dry_run/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/dry_run/stderr.log b/tests/testsuite/cargo_add/dry_run/stderr.log new file mode 100644 index 0000000..c80dba9 --- /dev/null +++ b/tests/testsuite/cargo_add/dry_run/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package v99999.0.0 to dependencies. +warning: aborting add due to dry run diff --git a/tests/testsuite/cargo_add/dry_run/stdout.log b/tests/testsuite/cargo_add/dry_run/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/features/in b/tests/testsuite/cargo_add/features/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/features/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/features/mod.rs b/tests/testsuite/cargo_add/features/mod.rs new file mode 100644 index 0000000..5e41153 --- /dev/null +++ b/tests/testsuite/cargo_add/features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --features eyes") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/features/out/Cargo.toml b/tests/testsuite/cargo_add/features/out/Cargo.toml new file mode 100644 index 0000000..11419b2 --- /dev/null +++ b/tests/testsuite/cargo_add/features/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "99999.0.0", features = ["eyes"] } diff --git a/tests/testsuite/cargo_add/features/stderr.log b/tests/testsuite/cargo_add/features/stderr.log new file mode 100644 index 0000000..386f3db --- /dev/null +++ b/tests/testsuite/cargo_add/features/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + + eyes + - ears + - mouth + - nose diff --git a/tests/testsuite/cargo_add/features/stdout.log b/tests/testsuite/cargo_add/features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/features_empty/in b/tests/testsuite/cargo_add/features_empty/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/features_empty/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/features_empty/mod.rs b/tests/testsuite/cargo_add/features_empty/mod.rs new file mode 100644 index 0000000..81dffc1 --- /dev/null +++ b/tests/testsuite/cargo_add/features_empty/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --features ''") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/features_empty/out/Cargo.toml b/tests/testsuite/cargo_add/features_empty/out/Cargo.toml new file mode 100644 index 0000000..79d735a --- /dev/null +++ b/tests/testsuite/cargo_add/features_empty/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = "99999.0.0" diff --git a/tests/testsuite/cargo_add/features_empty/stderr.log b/tests/testsuite/cargo_add/features_empty/stderr.log new file mode 100644 index 0000000..796b960 --- /dev/null +++ b/tests/testsuite/cargo_add/features_empty/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + - ears + - eyes + - mouth + - nose diff --git a/tests/testsuite/cargo_add/features_empty/stdout.log b/tests/testsuite/cargo_add/features_empty/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/features_multiple_occurrences/in b/tests/testsuite/cargo_add/features_multiple_occurrences/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/features_multiple_occurrences/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs b/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs new file mode 100644 index 0000000..db47f86 --- /dev/null +++ b/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --features eyes --features nose") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/features_multiple_occurrences/out/Cargo.toml b/tests/testsuite/cargo_add/features_multiple_occurrences/out/Cargo.toml new file mode 100644 index 0000000..0060d24 --- /dev/null +++ b/tests/testsuite/cargo_add/features_multiple_occurrences/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "99999.0.0", features = ["eyes", "nose"] } diff --git a/tests/testsuite/cargo_add/features_multiple_occurrences/stderr.log b/tests/testsuite/cargo_add/features_multiple_occurrences/stderr.log new file mode 100644 index 0000000..6154590 --- /dev/null +++ b/tests/testsuite/cargo_add/features_multiple_occurrences/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + + eyes + + nose + - ears + - mouth diff --git a/tests/testsuite/cargo_add/features_multiple_occurrences/stdout.log b/tests/testsuite/cargo_add/features_multiple_occurrences/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/features_preserve/in/Cargo.toml b/tests/testsuite/cargo_add/features_preserve/in/Cargo.toml new file mode 100644 index 0000000..11419b2 --- /dev/null +++ b/tests/testsuite/cargo_add/features_preserve/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "99999.0.0", features = ["eyes"] } diff --git a/tests/testsuite/cargo_add/features_preserve/in/src/lib.rs b/tests/testsuite/cargo_add/features_preserve/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/features_preserve/mod.rs b/tests/testsuite/cargo_add/features_preserve/mod.rs new file mode 100644 index 0000000..ed99a31 --- /dev/null +++ b/tests/testsuite/cargo_add/features_preserve/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/features_preserve/out/Cargo.toml b/tests/testsuite/cargo_add/features_preserve/out/Cargo.toml new file mode 100644 index 0000000..11419b2 --- /dev/null +++ b/tests/testsuite/cargo_add/features_preserve/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "99999.0.0", features = ["eyes"] } diff --git a/tests/testsuite/cargo_add/features_preserve/stderr.log b/tests/testsuite/cargo_add/features_preserve/stderr.log new file mode 100644 index 0000000..386f3db --- /dev/null +++ b/tests/testsuite/cargo_add/features_preserve/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + + eyes + - ears + - mouth + - nose diff --git a/tests/testsuite/cargo_add/features_preserve/stdout.log b/tests/testsuite/cargo_add/features_preserve/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/features_spaced_values/in b/tests/testsuite/cargo_add/features_spaced_values/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/features_spaced_values/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/features_spaced_values/mod.rs b/tests/testsuite/cargo_add/features_spaced_values/mod.rs new file mode 100644 index 0000000..2ef212e --- /dev/null +++ b/tests/testsuite/cargo_add/features_spaced_values/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --features eyes,nose") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/features_spaced_values/out/Cargo.toml b/tests/testsuite/cargo_add/features_spaced_values/out/Cargo.toml new file mode 100644 index 0000000..0060d24 --- /dev/null +++ b/tests/testsuite/cargo_add/features_spaced_values/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "99999.0.0", features = ["eyes", "nose"] } diff --git a/tests/testsuite/cargo_add/features_spaced_values/stderr.log b/tests/testsuite/cargo_add/features_spaced_values/stderr.log new file mode 100644 index 0000000..6154590 --- /dev/null +++ b/tests/testsuite/cargo_add/features_spaced_values/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + + eyes + + nose + - ears + - mouth diff --git a/tests/testsuite/cargo_add/features_spaced_values/stdout.log b/tests/testsuite/cargo_add/features_spaced_values/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/features_unknown/in b/tests/testsuite/cargo_add/features_unknown/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/features_unknown/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/features_unknown/mod.rs b/tests/testsuite/cargo_add/features_unknown/mod.rs new file mode 100644 index 0000000..7fd8d95 --- /dev/null +++ b/tests/testsuite/cargo_add/features_unknown/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --features noze") + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/features_unknown/out/Cargo.toml b/tests/testsuite/cargo_add/features_unknown/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/features_unknown/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/features_unknown/stderr.log b/tests/testsuite/cargo_add/features_unknown/stderr.log new file mode 100644 index 0000000..58afcb6 --- /dev/null +++ b/tests/testsuite/cargo_add/features_unknown/stderr.log @@ -0,0 +1,5 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. +error: unrecognized feature for crate your-face: noze +disabled features: + ears, eyes, mouth, nose diff --git a/tests/testsuite/cargo_add/features_unknown/stdout.log b/tests/testsuite/cargo_add/features_unknown/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/features_unknown_no_features/in b/tests/testsuite/cargo_add/features_unknown_no_features/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/features_unknown_no_features/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs b/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs new file mode 100644 index 0000000..9f59a03 --- /dev/null +++ b/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package --features noze") + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/features_unknown_no_features/out/Cargo.toml b/tests/testsuite/cargo_add/features_unknown_no_features/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/features_unknown_no_features/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/features_unknown_no_features/stderr.log b/tests/testsuite/cargo_add/features_unknown_no_features/stderr.log new file mode 100644 index 0000000..f1d046d --- /dev/null +++ b/tests/testsuite/cargo_add/features_unknown_no_features/stderr.log @@ -0,0 +1,4 @@ + Updating `dummy-registry` index + Adding my-package v99999.0.0 to dependencies. +error: unrecognized feature for crate my-package: noze +no features available for crate my-package diff --git a/tests/testsuite/cargo_add/features_unknown_no_features/stdout.log b/tests/testsuite/cargo_add/features_unknown_no_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git/in b/tests/testsuite/cargo_add/git/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/git/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/git/mod.rs b/tests/testsuite/cargo_add/git/mod.rs new file mode 100644 index 0000000..bd82b30 --- /dev/null +++ b/tests/testsuite/cargo_add/git/mod.rs @@ -0,0 +1,34 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let git_dep = cargo_test_support::git::new("git-package", |project| { + project + .file( + "Cargo.toml", + &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), + ) + .file("src/lib.rs", "") + }); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["git-package", "--git", &git_url]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git/out/Cargo.toml b/tests/testsuite/cargo_add/git/out/Cargo.toml new file mode 100644 index 0000000..7f2d2f1 --- /dev/null +++ b/tests/testsuite/cargo_add/git/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +git-package = { git = "[ROOTURL]/git-package", version = "0.3.0" } diff --git a/tests/testsuite/cargo_add/git/stderr.log b/tests/testsuite/cargo_add/git/stderr.log new file mode 100644 index 0000000..839d8bb --- /dev/null +++ b/tests/testsuite/cargo_add/git/stderr.log @@ -0,0 +1,3 @@ + Updating git repository `[ROOTURL]/git-package` + Adding git-package (git) to dependencies. + Updating git repository `[ROOTURL]/git-package` diff --git a/tests/testsuite/cargo_add/git/stdout.log b/tests/testsuite/cargo_add/git/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_branch/in b/tests/testsuite/cargo_add/git_branch/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/git_branch/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/git_branch/mod.rs b/tests/testsuite/cargo_add/git_branch/mod.rs new file mode 100644 index 0000000..0515645 --- /dev/null +++ b/tests/testsuite/cargo_add/git_branch/mod.rs @@ -0,0 +1,37 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let (git_dep, git_repo) = cargo_test_support::git::new_repo("git-package", |project| { + project + .file( + "Cargo.toml", + &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), + ) + .file("src/lib.rs", "") + }); + let branch = "dev"; + let find_head = || (git_repo.head().unwrap().peel_to_commit().unwrap()); + git_repo.branch(branch, &find_head(), false).unwrap(); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["git-package", "--git", &git_url, "--branch", branch]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git_branch/out/Cargo.toml b/tests/testsuite/cargo_add/git_branch/out/Cargo.toml new file mode 100644 index 0000000..2eb2955 --- /dev/null +++ b/tests/testsuite/cargo_add/git_branch/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +git-package = { git = "[ROOTURL]/git-package", branch = "dev", version = "0.3.0" } diff --git a/tests/testsuite/cargo_add/git_branch/stderr.log b/tests/testsuite/cargo_add/git_branch/stderr.log new file mode 100644 index 0000000..839d8bb --- /dev/null +++ b/tests/testsuite/cargo_add/git_branch/stderr.log @@ -0,0 +1,3 @@ + Updating git repository `[ROOTURL]/git-package` + Adding git-package (git) to dependencies. + Updating git repository `[ROOTURL]/git-package` diff --git a/tests/testsuite/cargo_add/git_branch/stdout.log b/tests/testsuite/cargo_add/git_branch/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_conflicts_namever/in b/tests/testsuite/cargo_add/git_conflicts_namever/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/git_conflicts_namever/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs b/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs new file mode 100644 index 0000000..f123298 --- /dev/null +++ b/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs @@ -0,0 +1,29 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args([ + "my-package@0.4.3", + "--git", + "https://github.com/dcjanus/invalid", + ]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git_conflicts_namever/out/Cargo.toml b/tests/testsuite/cargo_add/git_conflicts_namever/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/git_conflicts_namever/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/git_conflicts_namever/stderr.log b/tests/testsuite/cargo_add/git_conflicts_namever/stderr.log new file mode 100644 index 0000000..207e0de --- /dev/null +++ b/tests/testsuite/cargo_add/git_conflicts_namever/stderr.log @@ -0,0 +1 @@ +error: cannot specify a git URL (`https://github.com/dcjanus/invalid`) with a version (`0.4.3`). diff --git a/tests/testsuite/cargo_add/git_conflicts_namever/stdout.log b/tests/testsuite/cargo_add/git_conflicts_namever/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_dev/in b/tests/testsuite/cargo_add/git_dev/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/git_dev/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/git_dev/mod.rs b/tests/testsuite/cargo_add/git_dev/mod.rs new file mode 100644 index 0000000..9e14a40 --- /dev/null +++ b/tests/testsuite/cargo_add/git_dev/mod.rs @@ -0,0 +1,34 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let git_dep = cargo_test_support::git::new("git-package", |project| { + project + .file( + "Cargo.toml", + &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), + ) + .file("src/lib.rs", "") + }); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["git-package", "--git", &git_url, "--dev"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git_dev/out/Cargo.toml b/tests/testsuite/cargo_add/git_dev/out/Cargo.toml new file mode 100644 index 0000000..ceb1317 --- /dev/null +++ b/tests/testsuite/cargo_add/git_dev/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dev-dependencies] +git-package = { git = "[ROOTURL]/git-package" } diff --git a/tests/testsuite/cargo_add/git_dev/stderr.log b/tests/testsuite/cargo_add/git_dev/stderr.log new file mode 100644 index 0000000..8e53bb4 --- /dev/null +++ b/tests/testsuite/cargo_add/git_dev/stderr.log @@ -0,0 +1,3 @@ + Updating git repository `[ROOTURL]/git-package` + Adding git-package (git) to dev-dependencies. + Updating git repository `[ROOTURL]/git-package` diff --git a/tests/testsuite/cargo_add/git_dev/stdout.log b/tests/testsuite/cargo_add/git_dev/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_inferred_name/in b/tests/testsuite/cargo_add/git_inferred_name/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/git_inferred_name/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/git_inferred_name/mod.rs b/tests/testsuite/cargo_add/git_inferred_name/mod.rs new file mode 100644 index 0000000..52183ad --- /dev/null +++ b/tests/testsuite/cargo_add/git_inferred_name/mod.rs @@ -0,0 +1,34 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let git_dep = cargo_test_support::git::new("git-package", |project| { + project + .file( + "Cargo.toml", + &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), + ) + .file("src/lib.rs", "") + }); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["--git", &git_url]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git_inferred_name/out/Cargo.toml b/tests/testsuite/cargo_add/git_inferred_name/out/Cargo.toml new file mode 100644 index 0000000..7f2d2f1 --- /dev/null +++ b/tests/testsuite/cargo_add/git_inferred_name/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +git-package = { git = "[ROOTURL]/git-package", version = "0.3.0" } diff --git a/tests/testsuite/cargo_add/git_inferred_name/stderr.log b/tests/testsuite/cargo_add/git_inferred_name/stderr.log new file mode 100644 index 0000000..b5e8b1c --- /dev/null +++ b/tests/testsuite/cargo_add/git_inferred_name/stderr.log @@ -0,0 +1,4 @@ + Updating git repository `[ROOTURL]/git-package` + Updating git repository `[ROOTURL]/git-package` + Adding git-package (git) to dependencies. + Updating git repository `[ROOTURL]/git-package` diff --git a/tests/testsuite/cargo_add/git_inferred_name/stdout.log b/tests/testsuite/cargo_add/git_inferred_name/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_inferred_name_multiple/in b/tests/testsuite/cargo_add/git_inferred_name_multiple/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/git_inferred_name_multiple/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs b/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs new file mode 100644 index 0000000..a708a8a --- /dev/null +++ b/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs @@ -0,0 +1,74 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let git_dep = cargo_test_support::git::new("git-package", |project| { + project + .file( + "p1/Cargo.toml", + &cargo_test_support::basic_manifest("my-package1", "0.3.0+my-package1"), + ) + .file("p1/src/lib.rs", "") + .file( + "p2/Cargo.toml", + &cargo_test_support::basic_manifest("my-package2", "0.3.0+my-package2"), + ) + .file("p2/src/lib.rs", "") + .file( + "p3/Cargo.toml", + &cargo_test_support::basic_manifest("my-package3", "0.3.0+my-package2"), + ) + .file("p3/src/lib.rs", "") + .file( + "p4/Cargo.toml", + &cargo_test_support::basic_manifest("my-package4", "0.3.0+my-package2"), + ) + .file("p4/src/lib.rs", "") + .file( + "p5/Cargo.toml", + &cargo_test_support::basic_manifest("my-package5", "0.3.0+my-package2"), + ) + .file("p5/src/lib.rs", "") + .file( + "p6/Cargo.toml", + &cargo_test_support::basic_manifest("my-package6", "0.3.0+my-package2"), + ) + .file("p6/src/lib.rs", "") + .file( + "p7/Cargo.toml", + &cargo_test_support::basic_manifest("my-package7", "0.3.0+my-package2"), + ) + .file("p7/src/lib.rs", "") + .file( + "p8/Cargo.toml", + &cargo_test_support::basic_manifest("my-package8", "0.3.0+my-package2"), + ) + .file("p8/src/lib.rs", "") + .file( + "p9/Cargo.toml", + &cargo_test_support::basic_manifest("my-package9", "0.3.0+my-package2"), + ) + .file("p9/src/lib.rs", "") + }); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["--git", &git_url]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git_inferred_name_multiple/out/Cargo.toml b/tests/testsuite/cargo_add/git_inferred_name_multiple/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/git_inferred_name_multiple/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/git_inferred_name_multiple/stderr.log b/tests/testsuite/cargo_add/git_inferred_name_multiple/stderr.log new file mode 100644 index 0000000..2e045db --- /dev/null +++ b/tests/testsuite/cargo_add/git_inferred_name_multiple/stderr.log @@ -0,0 +1,5 @@ + Updating git repository `[ROOTURL]/git-package` +error: multiple packages found at `[ROOTURL]/git-package`: + my-package1, my-package2, my-package3, my-package4, my-package5, my-package6 + my-package7, my-package8, my-package9 +To disambiguate, run `cargo add --git [ROOTURL]/git-package ` diff --git a/tests/testsuite/cargo_add/git_inferred_name_multiple/stdout.log b/tests/testsuite/cargo_add/git_inferred_name_multiple/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_multiple_names/in b/tests/testsuite/cargo_add/git_multiple_names/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/git_multiple_names/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/git_multiple_names/mod.rs b/tests/testsuite/cargo_add/git_multiple_names/mod.rs new file mode 100644 index 0000000..39eb6e6 --- /dev/null +++ b/tests/testsuite/cargo_add/git_multiple_names/mod.rs @@ -0,0 +1,39 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let git_dep = cargo_test_support::git::new("git-package", |project| { + project + .file( + "p1/Cargo.toml", + &cargo_test_support::basic_manifest("my-package1", "0.3.0+my-package1"), + ) + .file("p1/src/lib.rs", "") + .file( + "p2/Cargo.toml", + &cargo_test_support::basic_manifest("my-package2", "0.3.0+my-package2"), + ) + .file("p2/src/lib.rs", "") + }); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["my-package1", "my-package2", "--git", &git_url]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git_multiple_names/out/Cargo.toml b/tests/testsuite/cargo_add/git_multiple_names/out/Cargo.toml new file mode 100644 index 0000000..ba9d3c5 --- /dev/null +++ b/tests/testsuite/cargo_add/git_multiple_names/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { git = "[ROOTURL]/git-package", version = "0.3.0" } +my-package2 = { git = "[ROOTURL]/git-package", version = "0.3.0" } diff --git a/tests/testsuite/cargo_add/git_multiple_names/stderr.log b/tests/testsuite/cargo_add/git_multiple_names/stderr.log new file mode 100644 index 0000000..454f0c7 --- /dev/null +++ b/tests/testsuite/cargo_add/git_multiple_names/stderr.log @@ -0,0 +1,4 @@ + Updating git repository `[ROOTURL]/git-package` + Adding my-package1 (git) to dependencies. + Adding my-package2 (git) to dependencies. + Updating git repository `[ROOTURL]/git-package` diff --git a/tests/testsuite/cargo_add/git_multiple_names/stdout.log b/tests/testsuite/cargo_add/git_multiple_names/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_normalized_name/in b/tests/testsuite/cargo_add/git_normalized_name/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/git_normalized_name/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/git_normalized_name/mod.rs b/tests/testsuite/cargo_add/git_normalized_name/mod.rs new file mode 100644 index 0000000..03d8618 --- /dev/null +++ b/tests/testsuite/cargo_add/git_normalized_name/mod.rs @@ -0,0 +1,34 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let git_dep = cargo_test_support::git::new("git-package", |project| { + project + .file( + "Cargo.toml", + &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), + ) + .file("src/lib.rs", "") + }); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["git_package", "--git", &git_url]) + .current_dir(cwd) + .assert() + .failure() // Fuzzy searching for paths isn't supported at this time + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git_normalized_name/out/Cargo.toml b/tests/testsuite/cargo_add/git_normalized_name/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/git_normalized_name/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/git_normalized_name/stderr.log b/tests/testsuite/cargo_add/git_normalized_name/stderr.log new file mode 100644 index 0000000..fedf828 --- /dev/null +++ b/tests/testsuite/cargo_add/git_normalized_name/stderr.log @@ -0,0 +1,2 @@ + Updating git repository `[ROOTURL]/git-package` +error: the crate `git_package@[ROOTURL]/git-package` could not be found at `[ROOTURL]/git-package` diff --git a/tests/testsuite/cargo_add/git_normalized_name/stdout.log b/tests/testsuite/cargo_add/git_normalized_name/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_registry/in/Cargo.toml b/tests/testsuite/cargo_add/git_registry/in/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/git_registry/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/git_registry/in/src/lib.rs b/tests/testsuite/cargo_add/git_registry/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_registry/mod.rs b/tests/testsuite/cargo_add/git_registry/mod.rs new file mode 100644 index 0000000..6bf6f89 --- /dev/null +++ b/tests/testsuite/cargo_add/git_registry/mod.rs @@ -0,0 +1,40 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_alt_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_alt_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let git_dep = cargo_test_support::git::new("versioned-package", |project| { + project + .file( + "Cargo.toml", + &cargo_test_support::basic_manifest("versioned-package", "0.3.0+versioned-package"), + ) + .file("src/lib.rs", "") + }); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args([ + "versioned-package", + "--git", + &git_url, + "--registry", + "alternative", + ]) + .current_dir(cwd) + .assert() + .failure() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git_registry/out/Cargo.toml b/tests/testsuite/cargo_add/git_registry/out/Cargo.toml new file mode 100644 index 0000000..3773d1c --- /dev/null +++ b/tests/testsuite/cargo_add/git_registry/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +versioned-package = { git = "[ROOTURL]/versioned-package", version = "0.3.0", registry = "alternative" } diff --git a/tests/testsuite/cargo_add/git_registry/stderr.log b/tests/testsuite/cargo_add/git_registry/stderr.log new file mode 100644 index 0000000..c554c7e --- /dev/null +++ b/tests/testsuite/cargo_add/git_registry/stderr.log @@ -0,0 +1,6 @@ + Updating git repository `[ROOTURL]/versioned-package` + Adding versioned-package (git) to dependencies. +error: failed to parse manifest at `[ROOT]/case/Cargo.toml` + +Caused by: + dependency (versioned-package) specification is ambiguous. Only one of `git` or `registry` is allowed. diff --git a/tests/testsuite/cargo_add/git_registry/stdout.log b/tests/testsuite/cargo_add/git_registry/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_rev/in b/tests/testsuite/cargo_add/git_rev/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/git_rev/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/git_rev/mod.rs b/tests/testsuite/cargo_add/git_rev/mod.rs new file mode 100644 index 0000000..6126072 --- /dev/null +++ b/tests/testsuite/cargo_add/git_rev/mod.rs @@ -0,0 +1,36 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let (git_dep, git_repo) = cargo_test_support::git::new_repo("git-package", |project| { + project + .file( + "Cargo.toml", + &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), + ) + .file("src/lib.rs", "") + }); + let find_head = || (git_repo.head().unwrap().peel_to_commit().unwrap()); + let head = find_head().id().to_string(); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["git-package", "--git", &git_url, "--rev", &head]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git_rev/out/Cargo.toml b/tests/testsuite/cargo_add/git_rev/out/Cargo.toml new file mode 100644 index 0000000..efc00a0 --- /dev/null +++ b/tests/testsuite/cargo_add/git_rev/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +git-package = { git = "[ROOTURL]/git-package", rev = "[..]", version = "0.3.0" } diff --git a/tests/testsuite/cargo_add/git_rev/stderr.log b/tests/testsuite/cargo_add/git_rev/stderr.log new file mode 100644 index 0000000..839d8bb --- /dev/null +++ b/tests/testsuite/cargo_add/git_rev/stderr.log @@ -0,0 +1,3 @@ + Updating git repository `[ROOTURL]/git-package` + Adding git-package (git) to dependencies. + Updating git repository `[ROOTURL]/git-package` diff --git a/tests/testsuite/cargo_add/git_rev/stdout.log b/tests/testsuite/cargo_add/git_rev/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/git_tag/in b/tests/testsuite/cargo_add/git_tag/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/git_tag/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/git_tag/mod.rs b/tests/testsuite/cargo_add/git_tag/mod.rs new file mode 100644 index 0000000..b355b17 --- /dev/null +++ b/tests/testsuite/cargo_add/git_tag/mod.rs @@ -0,0 +1,36 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let (git_dep, git_repo) = cargo_test_support::git::new_repo("git-package", |project| { + project + .file( + "Cargo.toml", + &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), + ) + .file("src/lib.rs", "") + }); + let tag = "v1.0.0"; + cargo_test_support::git::tag(&git_repo, tag); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["git-package", "--git", &git_url, "--tag", tag]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/git_tag/out/Cargo.toml b/tests/testsuite/cargo_add/git_tag/out/Cargo.toml new file mode 100644 index 0000000..233f26e --- /dev/null +++ b/tests/testsuite/cargo_add/git_tag/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +git-package = { git = "[ROOTURL]/git-package", tag = "v1.0.0", version = "0.3.0" } diff --git a/tests/testsuite/cargo_add/git_tag/stderr.log b/tests/testsuite/cargo_add/git_tag/stderr.log new file mode 100644 index 0000000..839d8bb --- /dev/null +++ b/tests/testsuite/cargo_add/git_tag/stderr.log @@ -0,0 +1,3 @@ + Updating git repository `[ROOTURL]/git-package` + Adding git-package (git) to dependencies. + Updating git repository `[ROOTURL]/git-package` diff --git a/tests/testsuite/cargo_add/git_tag/stdout.log b/tests/testsuite/cargo_add/git_tag/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/infer_prerelease/in b/tests/testsuite/cargo_add/infer_prerelease/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/infer_prerelease/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/infer_prerelease/mod.rs b/tests/testsuite/cargo_add/infer_prerelease/mod.rs new file mode 100644 index 0000000..94533f9 --- /dev/null +++ b/tests/testsuite/cargo_add/infer_prerelease/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("prerelease_only") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/infer_prerelease/out/Cargo.toml b/tests/testsuite/cargo_add/infer_prerelease/out/Cargo.toml new file mode 100644 index 0000000..4a86322 --- /dev/null +++ b/tests/testsuite/cargo_add/infer_prerelease/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +prerelease_only = "0.2.0-alpha.1" diff --git a/tests/testsuite/cargo_add/infer_prerelease/stderr.log b/tests/testsuite/cargo_add/infer_prerelease/stderr.log new file mode 100644 index 0000000..0696d8f --- /dev/null +++ b/tests/testsuite/cargo_add/infer_prerelease/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding prerelease_only v0.2.0-alpha.1 to dependencies. diff --git a/tests/testsuite/cargo_add/infer_prerelease/stdout.log b/tests/testsuite/cargo_add/infer_prerelease/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_arg/in b/tests/testsuite/cargo_add/invalid_arg/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_arg/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/invalid_arg/mod.rs b/tests/testsuite/cargo_add/invalid_arg/mod.rs new file mode 100644 index 0000000..f103def --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_arg/mod.rs @@ -0,0 +1,26 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +#[ignore = "temporarily disabled for beta due to clap update"] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package --flag") + .current_dir(cwd) + .assert() + .code(1) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_arg/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_arg/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_arg/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_arg/stderr.log b/tests/testsuite/cargo_add/invalid_arg/stderr.log new file mode 100644 index 0000000..b5ee3cc --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_arg/stderr.log @@ -0,0 +1,9 @@ +error: unexpected argument '--flag' found + + note: argument '--tag' exists + +Usage: cargo add [OPTIONS] [@] ... + cargo add [OPTIONS] --path ... + cargo add [OPTIONS] --git ... + +For more information, try '--help'. diff --git a/tests/testsuite/cargo_add/invalid_arg/stdout.log b/tests/testsuite/cargo_add/invalid_arg/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_git_external/in b/tests/testsuite/cargo_add/invalid_git_external/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_git_external/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/invalid_git_external/mod.rs b/tests/testsuite/cargo_add/invalid_git_external/mod.rs new file mode 100644 index 0000000..705182f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_git_external/mod.rs @@ -0,0 +1,28 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let git_url = url::Url::from_directory_path(cwd.join("does-not-exist")) + .unwrap() + .to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["fake-git", "--git", &git_url]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_git_external/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_git_external/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_git_external/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_git_external/stderr.log b/tests/testsuite/cargo_add/invalid_git_external/stderr.log new file mode 100644 index 0000000..1865630 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_git_external/stderr.log @@ -0,0 +1,12 @@ + Updating git repository `[ROOTURL]/case/does-not-exist/` +... +error: failed to load source for dependency `fake-git` + +Caused by: + Unable to update [ROOTURL]/case/does-not-exist/ + +Caused by: + failed to clone into: [ROOT]/home/.cargo/git/db/does-not-exist-[..] + +Caused by: +... diff --git a/tests/testsuite/cargo_add/invalid_git_external/stdout.log b/tests/testsuite/cargo_add/invalid_git_external/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_git_name/in b/tests/testsuite/cargo_add/invalid_git_name/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_git_name/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/invalid_git_name/mod.rs b/tests/testsuite/cargo_add/invalid_git_name/mod.rs new file mode 100644 index 0000000..0aff8c0 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_git_name/mod.rs @@ -0,0 +1,34 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let git_dep = cargo_test_support::git::new("git-package", |project| { + project + .file( + "Cargo.toml", + &cargo_test_support::basic_manifest("git-package", "0.3.0+git-package"), + ) + .file("src/lib.rs", "") + }); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["not-in-git", "--git", &git_url]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_git_name/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_git_name/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_git_name/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_git_name/stderr.log b/tests/testsuite/cargo_add/invalid_git_name/stderr.log new file mode 100644 index 0000000..68fc4e4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_git_name/stderr.log @@ -0,0 +1,2 @@ + Updating git repository `[ROOTURL]/git-package` +error: the crate `not-in-git@[ROOTURL]/git-package` could not be found at `[ROOTURL]/git-package` diff --git a/tests/testsuite/cargo_add/invalid_git_name/stdout.log b/tests/testsuite/cargo_add/invalid_git_name/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/Cargo.toml new file mode 100644 index 0000000..afd30d4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/primary/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/primary/Cargo.toml new file mode 100644 index 0000000..dd275f4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "bar" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/primary/src/lib.rs b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/mod.rs b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/mod.rs new file mode 100644 index 0000000..837293e --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/mod.rs @@ -0,0 +1,23 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "--default-features", "-p", "bar"]) + .current_dir(cwd) + .assert() + .failure() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/Cargo.toml new file mode 100644 index 0000000..afd30d4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/primary/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/primary/Cargo.toml new file mode 100644 index 0000000..dd275f4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/out/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "bar" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/stderr.log b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/stderr.log new file mode 100644 index 0000000..85bd8da --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/stderr.log @@ -0,0 +1 @@ +error: cannot override workspace dependency with `--default-features`, either change `workspace.dependencies.foo.default-features` or define the dependency exclusively in the package's manifest diff --git a/tests/testsuite/cargo_add/invalid_key_inherit_dependency/stdout.log b/tests/testsuite/cargo_add/invalid_key_inherit_dependency/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/Cargo.toml new file mode 100644 index 0000000..afd30d4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/primary/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/primary/Cargo.toml new file mode 100644 index 0000000..a574094 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo.workspace = true diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/primary/src/lib.rs b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/mod.rs b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/mod.rs new file mode 100644 index 0000000..837293e --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/mod.rs @@ -0,0 +1,23 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "--default-features", "-p", "bar"]) + .current_dir(cwd) + .assert() + .failure() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/Cargo.toml new file mode 100644 index 0000000..afd30d4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/primary/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/primary/Cargo.toml new file mode 100644 index 0000000..a574094 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo.workspace = true diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/stderr.log b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/stderr.log new file mode 100644 index 0000000..85bd8da --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/stderr.log @@ -0,0 +1 @@ +error: cannot override workspace dependency with `--default-features`, either change `workspace.dependencies.foo.default-features` or define the dependency exclusively in the package's manifest diff --git a/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/stdout.log b/tests/testsuite/cargo_add/invalid_key_overwrite_inherit_dependency/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/Cargo.toml new file mode 100644 index 0000000..12c6ee5 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency", "dependency-alt"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency-alt/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency-alt/Cargo.toml new file mode 100644 index 0000000..bb64729 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency-alt/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo-alt" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency-alt/src/lib.rs b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency-alt/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/primary/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/primary/Cargo.toml new file mode 100644 index 0000000..dd275f4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "bar" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/primary/src/lib.rs b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/mod.rs b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/mod.rs new file mode 100644 index 0000000..bee1325 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/mod.rs @@ -0,0 +1,23 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["--rename", "foo", "foo-alt", "-p", "bar"]) + .current_dir(cwd) + .assert() + .failure() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/Cargo.toml new file mode 100644 index 0000000..12c6ee5 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency", "dependency-alt"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/dependency-alt/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/dependency-alt/Cargo.toml new file mode 100644 index 0000000..bb64729 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/dependency-alt/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo-alt" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/primary/Cargo.toml b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/primary/Cargo.toml new file mode 100644 index 0000000..dd275f4 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/out/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "bar" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/stderr.log b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/stderr.log new file mode 100644 index 0000000..35bcdb6 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/stderr.log @@ -0,0 +1 @@ +error: cannot override workspace dependency with `--rename`, either change `workspace.dependencies.foo.package` or define the dependency exclusively in the package's manifest diff --git a/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/stdout.log b/tests/testsuite/cargo_add/invalid_key_rename_inherit_dependency/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_manifest/in/Cargo.toml b/tests/testsuite/cargo_add/invalid_manifest/in/Cargo.toml new file mode 100644 index 0000000..94ee959 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_manifest/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "manifest-invalid-test-fixture" +version = "0.1.0" + +[invalid-section] +key = invalid-value diff --git a/tests/testsuite/cargo_add/invalid_manifest/in/src/lib.rs b/tests/testsuite/cargo_add/invalid_manifest/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_manifest/mod.rs b/tests/testsuite/cargo_add/invalid_manifest/mod.rs new file mode 100644 index 0000000..e385cfc --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_manifest/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package") + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_manifest/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_manifest/out/Cargo.toml new file mode 100644 index 0000000..94ee959 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_manifest/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "manifest-invalid-test-fixture" +version = "0.1.0" + +[invalid-section] +key = invalid-value diff --git a/tests/testsuite/cargo_add/invalid_manifest/stderr.log b/tests/testsuite/cargo_add/invalid_manifest/stderr.log new file mode 100644 index 0000000..3dabde3 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_manifest/stderr.log @@ -0,0 +1,12 @@ +error: failed to parse manifest at `[ROOT]/case/Cargo.toml` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 8, column 7 + | + 8 | key = invalid-value + | ^ + invalid string + expected `"`, `'` diff --git a/tests/testsuite/cargo_add/invalid_manifest/stdout.log b/tests/testsuite/cargo_add/invalid_manifest/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_name_external/in b/tests/testsuite/cargo_add/invalid_name_external/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_name_external/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/invalid_name_external/mod.rs b/tests/testsuite/cargo_add/invalid_name_external/mod.rs new file mode 100644 index 0000000..16e0417 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_name_external/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("lets_hope_nobody_ever_publishes_this_crate") + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_name_external/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_name_external/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_name_external/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_name_external/stderr.log b/tests/testsuite/cargo_add/invalid_name_external/stderr.log new file mode 100644 index 0000000..5e574ce --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_name_external/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index +error: the crate `lets_hope_nobody_ever_publishes_this_crate` could not be found in registry index. diff --git a/tests/testsuite/cargo_add/invalid_name_external/stdout.log b/tests/testsuite/cargo_add/invalid_name_external/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_path/in b/tests/testsuite/cargo_add/invalid_path/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/invalid_path/mod.rs b/tests/testsuite/cargo_add/invalid_path/mod.rs new file mode 100644 index 0000000..0d26b55 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture --path ./tests/fixtures/local") + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_path/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_path/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_path/stderr.log b/tests/testsuite/cargo_add/invalid_path/stderr.log new file mode 100644 index 0000000..f6c4043 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path/stderr.log @@ -0,0 +1,10 @@ +error: failed to load source for dependency `cargo-list-test-fixture` + +Caused by: + Unable to update [ROOT]/case/tests/fixtures/local + +Caused by: + failed to read `[ROOT]/case/tests/fixtures/local/Cargo.toml` + +Caused by: + [..] diff --git a/tests/testsuite/cargo_add/invalid_path/stdout.log b/tests/testsuite/cargo_add/invalid_path/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_path_name/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/invalid_path_name/in/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path_name/in/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_path_name/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/invalid_path_name/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_path_name/in/primary/Cargo.toml b/tests/testsuite/cargo_add/invalid_path_name/in/primary/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path_name/in/primary/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_path_name/in/primary/src/lib.rs b/tests/testsuite/cargo_add/invalid_path_name/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_path_name/mod.rs b/tests/testsuite/cargo_add/invalid_path_name/mod.rs new file mode 100644 index 0000000..10d8414 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path_name/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("not-at-path --path ../dependency") + .current_dir(&cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_path_name/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/invalid_path_name/out/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path_name/out/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_path_name/out/primary/Cargo.toml b/tests/testsuite/cargo_add/invalid_path_name/out/primary/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path_name/out/primary/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_path_name/stderr.log b/tests/testsuite/cargo_add/invalid_path_name/stderr.log new file mode 100644 index 0000000..b35ea82 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path_name/stderr.log @@ -0,0 +1 @@ +error: the crate `not-at-path@[ROOT]/case/dependency` could not be found at `[ROOT]/case/dependency` diff --git a/tests/testsuite/cargo_add/invalid_path_name/stdout.log b/tests/testsuite/cargo_add/invalid_path_name/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_path_self/in b/tests/testsuite/cargo_add/invalid_path_self/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path_self/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/invalid_path_self/mod.rs b/tests/testsuite/cargo_add/invalid_path_self/mod.rs new file mode 100644 index 0000000..a64190f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path_self/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture --path .") + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_path_self/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_path_self/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path_self/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_path_self/stderr.log b/tests/testsuite/cargo_add/invalid_path_self/stderr.log new file mode 100644 index 0000000..62a25db --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_path_self/stderr.log @@ -0,0 +1,2 @@ + Adding cargo-list-test-fixture (local) to dependencies. +error: cannot add `cargo-list-test-fixture` as a dependency to itself diff --git a/tests/testsuite/cargo_add/invalid_path_self/stdout.log b/tests/testsuite/cargo_add/invalid_path_self/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_target_empty/in b/tests/testsuite/cargo_add/invalid_target_empty/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_target_empty/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/invalid_target_empty/mod.rs b/tests/testsuite/cargo_add/invalid_target_empty/mod.rs new file mode 100644 index 0000000..da93c4e --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_target_empty/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package --target ''") + .current_dir(cwd) + .assert() + .code(1) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_target_empty/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_target_empty/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_target_empty/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_target_empty/stderr.log b/tests/testsuite/cargo_add/invalid_target_empty/stderr.log new file mode 100644 index 0000000..4b1a2c3 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_target_empty/stderr.log @@ -0,0 +1,3 @@ +error: a value is required for '--target ' but none was supplied + +For more information, try '--help'. diff --git a/tests/testsuite/cargo_add/invalid_target_empty/stdout.log b/tests/testsuite/cargo_add/invalid_target_empty/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/invalid_vers/in b/tests/testsuite/cargo_add/invalid_vers/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_vers/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/invalid_vers/mod.rs b/tests/testsuite/cargo_add/invalid_vers/mod.rs new file mode 100644 index 0000000..c3b4d1f --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_vers/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package@invalid-version-string") + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/invalid_vers/out/Cargo.toml b/tests/testsuite/cargo_add/invalid_vers/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_vers/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/invalid_vers/stderr.log b/tests/testsuite/cargo_add/invalid_vers/stderr.log new file mode 100644 index 0000000..64f908e --- /dev/null +++ b/tests/testsuite/cargo_add/invalid_vers/stderr.log @@ -0,0 +1,4 @@ +error: invalid version requirement `invalid-version-string` + +Caused by: + unexpected character 'i' while parsing major version number diff --git a/tests/testsuite/cargo_add/invalid_vers/stdout.log b/tests/testsuite/cargo_add/invalid_vers/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/list_features/in b/tests/testsuite/cargo_add/list_features/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/list_features/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/list_features/mod.rs b/tests/testsuite/cargo_add/list_features/mod.rs new file mode 100644 index 0000000..e1e1b21 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["your-face"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/list_features/out/Cargo.toml b/tests/testsuite/cargo_add/list_features/out/Cargo.toml new file mode 100644 index 0000000..79d735a --- /dev/null +++ b/tests/testsuite/cargo_add/list_features/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = "99999.0.0" diff --git a/tests/testsuite/cargo_add/list_features/stderr.log b/tests/testsuite/cargo_add/list_features/stderr.log new file mode 100644 index 0000000..796b960 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + - ears + - eyes + - mouth + - nose diff --git a/tests/testsuite/cargo_add/list_features/stdout.log b/tests/testsuite/cargo_add/list_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/list_features_path/in/Cargo.toml b/tests/testsuite/cargo_add/list_features_path/in/Cargo.toml new file mode 100644 index 0000000..299859e --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path/in/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency", "optional"] diff --git a/tests/testsuite/cargo_add/list_features_path/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/list_features_path/in/dependency/Cargo.toml new file mode 100644 index 0000000..18041a5 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path/in/dependency/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "your-face" +version = "0.1.3" + +[dependencies] +my-package = "0.1.1" +optional-dependency = { path = "../optional", optional = true } + +[features] +default = ["mouth"] +nose = [] +mouth = ["nose"] +eyes = [] diff --git a/tests/testsuite/cargo_add/list_features_path/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/list_features_path/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/list_features_path/in/optional/Cargo.toml b/tests/testsuite/cargo_add/list_features_path/in/optional/Cargo.toml new file mode 100644 index 0000000..cb61a05 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path/in/optional/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "optional-dependency" +version = "0.1.3" + +[dependencies] +my-package = "0.1.1" diff --git a/tests/testsuite/cargo_add/list_features_path/in/optional/src/lib.rs b/tests/testsuite/cargo_add/list_features_path/in/optional/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/list_features_path/in/primary/Cargo.toml b/tests/testsuite/cargo_add/list_features_path/in/primary/Cargo.toml new file mode 100644 index 0000000..5e20016 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/list_features_path/in/primary/src/lib.rs b/tests/testsuite/cargo_add/list_features_path/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/list_features_path/mod.rs b/tests/testsuite/cargo_add/list_features_path/mod.rs new file mode 100644 index 0000000..22733b8 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --path ../dependency") + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/list_features_path/out/Cargo.toml b/tests/testsuite/cargo_add/list_features_path/out/Cargo.toml new file mode 100644 index 0000000..299859e --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path/out/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency", "optional"] diff --git a/tests/testsuite/cargo_add/list_features_path/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/list_features_path/out/dependency/Cargo.toml new file mode 100644 index 0000000..18041a5 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path/out/dependency/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "your-face" +version = "0.1.3" + +[dependencies] +my-package = "0.1.1" +optional-dependency = { path = "../optional", optional = true } + +[features] +default = ["mouth"] +nose = [] +mouth = ["nose"] +eyes = [] diff --git a/tests/testsuite/cargo_add/list_features_path/out/primary/Cargo.toml b/tests/testsuite/cargo_add/list_features_path/out/primary/Cargo.toml new file mode 100644 index 0000000..2461d09 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "0.1.3", path = "../dependency" } diff --git a/tests/testsuite/cargo_add/list_features_path/stderr.log b/tests/testsuite/cargo_add/list_features_path/stderr.log new file mode 100644 index 0000000..af6747f --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path/stderr.log @@ -0,0 +1,7 @@ + Adding your-face (local) to dependencies. + Features: + + mouth + + nose + - eyes + - optional-dependency + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_add/list_features_path/stdout.log b/tests/testsuite/cargo_add/list_features_path/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/in/Cargo.toml b/tests/testsuite/cargo_add/list_features_path_no_default/in/Cargo.toml new file mode 100644 index 0000000..299859e --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path_no_default/in/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency", "optional"] diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/list_features_path_no_default/in/dependency/Cargo.toml new file mode 100644 index 0000000..18041a5 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path_no_default/in/dependency/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "your-face" +version = "0.1.3" + +[dependencies] +my-package = "0.1.1" +optional-dependency = { path = "../optional", optional = true } + +[features] +default = ["mouth"] +nose = [] +mouth = ["nose"] +eyes = [] diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/list_features_path_no_default/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/in/optional/Cargo.toml b/tests/testsuite/cargo_add/list_features_path_no_default/in/optional/Cargo.toml new file mode 100644 index 0000000..cb61a05 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path_no_default/in/optional/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "optional-dependency" +version = "0.1.3" + +[dependencies] +my-package = "0.1.1" diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/in/optional/src/lib.rs b/tests/testsuite/cargo_add/list_features_path_no_default/in/optional/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/in/primary/Cargo.toml b/tests/testsuite/cargo_add/list_features_path_no_default/in/primary/Cargo.toml new file mode 100644 index 0000000..5e20016 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path_no_default/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/in/primary/src/lib.rs b/tests/testsuite/cargo_add/list_features_path_no_default/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs b/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs new file mode 100644 index 0000000..f520b2a --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs @@ -0,0 +1,30 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args([ + "your-face", + "--path", + "../dependency", + "--no-default-features", + ]) + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/out/Cargo.toml b/tests/testsuite/cargo_add/list_features_path_no_default/out/Cargo.toml new file mode 100644 index 0000000..299859e --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path_no_default/out/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency", "optional"] diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/list_features_path_no_default/out/dependency/Cargo.toml new file mode 100644 index 0000000..18041a5 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path_no_default/out/dependency/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "your-face" +version = "0.1.3" + +[dependencies] +my-package = "0.1.1" +optional-dependency = { path = "../optional", optional = true } + +[features] +default = ["mouth"] +nose = [] +mouth = ["nose"] +eyes = [] diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/out/primary/Cargo.toml b/tests/testsuite/cargo_add/list_features_path_no_default/out/primary/Cargo.toml new file mode 100644 index 0000000..0b0400d --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path_no_default/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "0.1.3", path = "../dependency", default-features = false } diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/stderr.log b/tests/testsuite/cargo_add/list_features_path_no_default/stderr.log new file mode 100644 index 0000000..7f47a22 --- /dev/null +++ b/tests/testsuite/cargo_add/list_features_path_no_default/stderr.log @@ -0,0 +1,7 @@ + Adding your-face (local) to dependencies. + Features: + - eyes + - mouth + - nose + - optional-dependency + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_add/list_features_path_no_default/stdout.log b/tests/testsuite/cargo_add/list_features_path_no_default/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/locked_changed/in b/tests/testsuite/cargo_add/locked_changed/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/locked_changed/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/locked_changed/mod.rs b/tests/testsuite/cargo_add/locked_changed/mod.rs new file mode 100644 index 0000000..9e3e57f --- /dev/null +++ b/tests/testsuite/cargo_add/locked_changed/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package --locked") + .current_dir(cwd) + .assert() + .failure() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/locked_changed/out/Cargo.toml b/tests/testsuite/cargo_add/locked_changed/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/locked_changed/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/locked_changed/stderr.log b/tests/testsuite/cargo_add/locked_changed/stderr.log new file mode 100644 index 0000000..8af1683 --- /dev/null +++ b/tests/testsuite/cargo_add/locked_changed/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package v99999.0.0 to dependencies. +error: the manifest file [ROOT]/case/Cargo.toml needs to be updated but --locked was passed to prevent this diff --git a/tests/testsuite/cargo_add/locked_changed/stdout.log b/tests/testsuite/cargo_add/locked_changed/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/locked_unchanged/in/Cargo.lock b/tests/testsuite/cargo_add/locked_unchanged/in/Cargo.lock new file mode 100644 index 0000000..011b335 --- /dev/null +++ b/tests/testsuite/cargo_add/locked_unchanged/in/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cargo-list-test-fixture" +version = "0.0.0" +dependencies = [ + "my-package", +] + +[[package]] +name = "my-package" +version = "99999.0.0+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62c45acf9e11d2f97f5b318143219c0b4102eafef1c22a4b545b47104691d915" diff --git a/tests/testsuite/cargo_add/locked_unchanged/in/Cargo.toml b/tests/testsuite/cargo_add/locked_unchanged/in/Cargo.toml new file mode 100644 index 0000000..5964c87 --- /dev/null +++ b/tests/testsuite/cargo_add/locked_unchanged/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package = "99999.0.0" diff --git a/tests/testsuite/cargo_add/locked_unchanged/in/src/lib.rs b/tests/testsuite/cargo_add/locked_unchanged/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/locked_unchanged/mod.rs b/tests/testsuite/cargo_add/locked_unchanged/mod.rs new file mode 100644 index 0000000..aba9918 --- /dev/null +++ b/tests/testsuite/cargo_add/locked_unchanged/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package --locked") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/locked_unchanged/out/Cargo.toml b/tests/testsuite/cargo_add/locked_unchanged/out/Cargo.toml new file mode 100644 index 0000000..5964c87 --- /dev/null +++ b/tests/testsuite/cargo_add/locked_unchanged/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package = "99999.0.0" diff --git a/tests/testsuite/cargo_add/locked_unchanged/stderr.log b/tests/testsuite/cargo_add/locked_unchanged/stderr.log new file mode 100644 index 0000000..fd6b711 --- /dev/null +++ b/tests/testsuite/cargo_add/locked_unchanged/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding my-package v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/locked_unchanged/stdout.log b/tests/testsuite/cargo_add/locked_unchanged/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/lockfile_updated/in/Cargo.lock b/tests/testsuite/cargo_add/lockfile_updated/in/Cargo.lock new file mode 100644 index 0000000..d9bcc98 --- /dev/null +++ b/tests/testsuite/cargo_add/lockfile_updated/in/Cargo.lock @@ -0,0 +1,17 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cargo-list-test-fixture" +version = "0.0.0" +dependencies = [ + "my-package", + "unrelateed-crate", +] + +[[package]] +name = "unrelateed-crate" +version = "0.2.0+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266de4849a570b5dfda5e8e082a2aff885e9d2d4965dae8f8b6c8535e1ec731f" diff --git a/tests/testsuite/cargo_add/lockfile_updated/in/Cargo.toml b/tests/testsuite/cargo_add/lockfile_updated/in/Cargo.toml new file mode 100644 index 0000000..95276d7 --- /dev/null +++ b/tests/testsuite/cargo_add/lockfile_updated/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +unrelateed-crate = "0.2.0" diff --git a/tests/testsuite/cargo_add/lockfile_updated/in/src/lib.rs b/tests/testsuite/cargo_add/lockfile_updated/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/lockfile_updated/mod.rs b/tests/testsuite/cargo_add/lockfile_updated/mod.rs new file mode 100644 index 0000000..33889df --- /dev/null +++ b/tests/testsuite/cargo_add/lockfile_updated/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/lockfile_updated/out/Cargo.lock b/tests/testsuite/cargo_add/lockfile_updated/out/Cargo.lock new file mode 100644 index 0000000..4b5fb46 --- /dev/null +++ b/tests/testsuite/cargo_add/lockfile_updated/out/Cargo.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cargo-list-test-fixture" +version = "0.0.0" +dependencies = [ + "my-package", + "unrelateed-crate", +] + +[[package]] +name = "my-package" +version = "99999.0.0+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62c45acf9e11d2f97f5b318143219c0b4102eafef1c22a4b545b47104691d915" + +[[package]] +name = "unrelateed-crate" +version = "0.2.0+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266de4849a570b5dfda5e8e082a2aff885e9d2d4965dae8f8b6c8535e1ec731f" diff --git a/tests/testsuite/cargo_add/lockfile_updated/out/Cargo.toml b/tests/testsuite/cargo_add/lockfile_updated/out/Cargo.toml new file mode 100644 index 0000000..3176a98 --- /dev/null +++ b/tests/testsuite/cargo_add/lockfile_updated/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package = "99999.0.0" +unrelateed-crate = "0.2.0" diff --git a/tests/testsuite/cargo_add/lockfile_updated/stderr.log b/tests/testsuite/cargo_add/lockfile_updated/stderr.log new file mode 100644 index 0000000..fd6b711 --- /dev/null +++ b/tests/testsuite/cargo_add/lockfile_updated/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding my-package v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/lockfile_updated/stdout.log b/tests/testsuite/cargo_add/lockfile_updated/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/manifest_path_package/in/Cargo.toml b/tests/testsuite/cargo_add/manifest_path_package/in/Cargo.toml new file mode 100644 index 0000000..57e1f30 --- /dev/null +++ b/tests/testsuite/cargo_add/manifest_path_package/in/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency"] diff --git a/tests/testsuite/cargo_add/manifest_path_package/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/manifest_path_package/in/dependency/Cargo.toml new file mode 100644 index 0000000..ca4f36d --- /dev/null +++ b/tests/testsuite/cargo_add/manifest_path_package/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/manifest_path_package/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/manifest_path_package/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/manifest_path_package/in/primary/Cargo.toml b/tests/testsuite/cargo_add/manifest_path_package/in/primary/Cargo.toml new file mode 100644 index 0000000..5e20016 --- /dev/null +++ b/tests/testsuite/cargo_add/manifest_path_package/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/manifest_path_package/in/primary/src/lib.rs b/tests/testsuite/cargo_add/manifest_path_package/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/manifest_path_package/mod.rs b/tests/testsuite/cargo_add/manifest_path_package/mod.rs new file mode 100644 index 0000000..008c2d3 --- /dev/null +++ b/tests/testsuite/cargo_add/manifest_path_package/mod.rs @@ -0,0 +1,31 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args([ + "--manifest-path", + "Cargo.toml", + "--package", + "cargo-list-test-fixture", + "cargo-list-test-fixture-dependency", + ]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/manifest_path_package/out/Cargo.toml b/tests/testsuite/cargo_add/manifest_path_package/out/Cargo.toml new file mode 100644 index 0000000..57e1f30 --- /dev/null +++ b/tests/testsuite/cargo_add/manifest_path_package/out/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency"] diff --git a/tests/testsuite/cargo_add/manifest_path_package/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/manifest_path_package/out/dependency/Cargo.toml new file mode 100644 index 0000000..ca4f36d --- /dev/null +++ b/tests/testsuite/cargo_add/manifest_path_package/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/manifest_path_package/out/primary/Cargo.toml b/tests/testsuite/cargo_add/manifest_path_package/out/primary/Cargo.toml new file mode 100644 index 0000000..a693df5 --- /dev/null +++ b/tests/testsuite/cargo_add/manifest_path_package/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", path = "../dependency" } diff --git a/tests/testsuite/cargo_add/manifest_path_package/stderr.log b/tests/testsuite/cargo_add/manifest_path_package/stderr.log new file mode 100644 index 0000000..8109d3c --- /dev/null +++ b/tests/testsuite/cargo_add/manifest_path_package/stderr.log @@ -0,0 +1 @@ + Adding cargo-list-test-fixture-dependency (local) to dependencies. diff --git a/tests/testsuite/cargo_add/manifest_path_package/stdout.log b/tests/testsuite/cargo_add/manifest_path_package/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/merge_activated_features/in/Cargo.toml b/tests/testsuite/cargo_add/merge_activated_features/in/Cargo.toml new file mode 100644 index 0000000..b1d9b39 --- /dev/null +++ b/tests/testsuite/cargo_add/merge_activated_features/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency", features = ["merge"] } diff --git a/tests/testsuite/cargo_add/merge_activated_features/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/merge_activated_features/in/dependency/Cargo.toml new file mode 100644 index 0000000..f34d7a6 --- /dev/null +++ b/tests/testsuite/cargo_add/merge_activated_features/in/dependency/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "foo" +version = "0.0.0" + +[features] +default-base = [] +default-test-base = [] +default-merge-base = [] +default = ["default-base", "default-test-base", "default-merge-base"] +test-base = [] +test = ["test-base", "default-test-base"] +merge-base = [] +merge = ["merge-base", "default-merge-base"] +unrelated = [] \ No newline at end of file diff --git a/tests/testsuite/cargo_add/merge_activated_features/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/merge_activated_features/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/merge_activated_features/in/primary/Cargo.toml b/tests/testsuite/cargo_add/merge_activated_features/in/primary/Cargo.toml new file mode 100644 index 0000000..a131c94 --- /dev/null +++ b/tests/testsuite/cargo_add/merge_activated_features/in/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, features = ["test"] } \ No newline at end of file diff --git a/tests/testsuite/cargo_add/merge_activated_features/in/primary/src/lib.rs b/tests/testsuite/cargo_add/merge_activated_features/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/merge_activated_features/mod.rs b/tests/testsuite/cargo_add/merge_activated_features/mod.rs new file mode 100644 index 0000000..1617832 --- /dev/null +++ b/tests/testsuite/cargo_add/merge_activated_features/mod.rs @@ -0,0 +1,23 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "-p", "bar"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/merge_activated_features/out/Cargo.toml b/tests/testsuite/cargo_add/merge_activated_features/out/Cargo.toml new file mode 100644 index 0000000..b1d9b39 --- /dev/null +++ b/tests/testsuite/cargo_add/merge_activated_features/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency", features = ["merge"] } diff --git a/tests/testsuite/cargo_add/merge_activated_features/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/merge_activated_features/out/dependency/Cargo.toml new file mode 100644 index 0000000..f34d7a6 --- /dev/null +++ b/tests/testsuite/cargo_add/merge_activated_features/out/dependency/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "foo" +version = "0.0.0" + +[features] +default-base = [] +default-test-base = [] +default-merge-base = [] +default = ["default-base", "default-test-base", "default-merge-base"] +test-base = [] +test = ["test-base", "default-test-base"] +merge-base = [] +merge = ["merge-base", "default-merge-base"] +unrelated = [] \ No newline at end of file diff --git a/tests/testsuite/cargo_add/merge_activated_features/out/primary/Cargo.toml b/tests/testsuite/cargo_add/merge_activated_features/out/primary/Cargo.toml new file mode 100644 index 0000000..fb4a126 --- /dev/null +++ b/tests/testsuite/cargo_add/merge_activated_features/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, features = ["test"] } diff --git a/tests/testsuite/cargo_add/merge_activated_features/stderr.log b/tests/testsuite/cargo_add/merge_activated_features/stderr.log new file mode 100644 index 0000000..02dde7a --- /dev/null +++ b/tests/testsuite/cargo_add/merge_activated_features/stderr.log @@ -0,0 +1,10 @@ + Adding foo (workspace) to dependencies. + Features as of v0.0.0: + + default-base + + default-merge-base + + default-test-base + + merge + + merge-base + + test + + test-base + - unrelated diff --git a/tests/testsuite/cargo_add/merge_activated_features/stdout.log b/tests/testsuite/cargo_add/merge_activated_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/mod.rs b/tests/testsuite/cargo_add/mod.rs new file mode 100644 index 0000000..d824aed --- /dev/null +++ b/tests/testsuite/cargo_add/mod.rs @@ -0,0 +1,196 @@ +mod add_basic; +mod add_multiple; +mod add_normalized_name_external; +mod build; +mod build_prefer_existing_version; +mod change_rename_target; +mod default_features; +mod deprecated_default_features; +mod deprecated_section; +mod detect_workspace_inherit; +mod detect_workspace_inherit_features; +mod detect_workspace_inherit_optional; +mod dev; +mod dev_build_conflict; +mod dev_prefer_existing_version; +mod dry_run; +mod features; +mod features_empty; +mod features_multiple_occurrences; +mod features_preserve; +mod features_spaced_values; +mod features_unknown; +mod features_unknown_no_features; +mod git; +mod git_branch; +mod git_conflicts_namever; +mod git_dev; +mod git_inferred_name; +mod git_inferred_name_multiple; +mod git_multiple_names; +mod git_normalized_name; +mod git_registry; +mod git_rev; +mod git_tag; +mod infer_prerelease; +mod invalid_arg; +mod invalid_git_external; +mod invalid_git_name; +mod invalid_key_inherit_dependency; +mod invalid_key_overwrite_inherit_dependency; +mod invalid_key_rename_inherit_dependency; +mod invalid_manifest; +mod invalid_name_external; +mod invalid_path; +mod invalid_path_name; +mod invalid_path_self; +mod invalid_target_empty; +mod invalid_vers; +mod list_features; +mod list_features_path; +mod list_features_path_no_default; +mod locked_changed; +mod locked_unchanged; +mod lockfile_updated; +mod manifest_path_package; +mod merge_activated_features; +mod multiple_conflicts_with_features; +mod multiple_conflicts_with_rename; +mod namever; +mod no_args; +mod no_default_features; +mod no_optional; +mod offline_empty_cache; +mod optional; +mod overwrite_default_features; +mod overwrite_default_features_with_no_default_features; +mod overwrite_features; +mod overwrite_git_with_path; +mod overwrite_inherit_features_noop; +mod overwrite_inherit_noop; +mod overwrite_inherit_optional_noop; +mod overwrite_inline_features; +mod overwrite_name_dev_noop; +mod overwrite_name_noop; +mod overwrite_no_default_features; +mod overwrite_no_default_features_with_default_features; +mod overwrite_no_optional; +mod overwrite_no_optional_with_optional; +mod overwrite_optional; +mod overwrite_optional_with_no_optional; +mod overwrite_path_noop; +mod overwrite_path_with_version; +mod overwrite_preserves_inline_table; +mod overwrite_rename_with_no_rename; +mod overwrite_rename_with_rename; +mod overwrite_rename_with_rename_noop; +mod overwrite_version_with_git; +mod overwrite_version_with_path; +mod overwrite_with_rename; +mod overwrite_workspace_dep; +mod overwrite_workspace_dep_features; +mod path; +mod path_dev; +mod path_inferred_name; +mod path_inferred_name_conflicts_full_feature; +mod path_normalized_name; +mod preserve_sorted; +mod preserve_unsorted; +mod quiet; +mod registry; +mod rename; +mod require_weak; +mod sorted_table_with_dotted_item; +mod target; +mod target_cfg; +mod unknown_inherited_feature; +mod vers; +mod workspace_name; +mod workspace_path; +mod workspace_path_dev; + +fn init_registry() { + cargo_test_support::registry::init(); + add_registry_packages(false); +} + +fn init_alt_registry() { + cargo_test_support::registry::alt_init(); + add_registry_packages(true); +} + +fn add_registry_packages(alt: bool) { + for name in [ + "my-package", + "my-package1", + "my-package2", + "my-dev-package1", + "my-dev-package2", + "my-build-package1", + "my-build-package2", + "toml", + "versioned-package", + "cargo-list-test-fixture-dependency", + "unrelateed-crate", + ] { + cargo_test_support::registry::Package::new(name, "0.1.1+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "0.2.0+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "0.2.3+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "0.4.1+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "20.0.0+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "99999.0.0+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "99999.0.0-alpha.1+my-package") + .alternative(alt) + .publish(); + } + + cargo_test_support::registry::Package::new("prerelease_only", "0.2.0-alpha.1") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new("test_breaking", "0.2.0") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new("test_nonbreaking", "0.1.1") + .alternative(alt) + .publish(); + + // Normalization + cargo_test_support::registry::Package::new("linked-hash-map", "0.5.4") + .alternative(alt) + .feature("clippy", &[]) + .feature("heapsize", &[]) + .feature("heapsize_impl", &[]) + .feature("nightly", &[]) + .feature("serde", &[]) + .feature("serde_impl", &[]) + .feature("serde_test", &[]) + .publish(); + cargo_test_support::registry::Package::new("inflector", "0.11.4") + .alternative(alt) + .feature("default", &["heavyweight", "lazy_static", "regex"]) + .feature("heavyweight", &[]) + .feature("lazy_static", &[]) + .feature("regex", &[]) + .feature("unstable", &[]) + .publish(); + + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .alternative(alt) + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); +} diff --git a/tests/testsuite/cargo_add/multiple_conflicts_with_features/in b/tests/testsuite/cargo_add/multiple_conflicts_with_features/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/multiple_conflicts_with_features/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs b/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs new file mode 100644 index 0000000..10f8244 --- /dev/null +++ b/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 your-face --features nose") + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/multiple_conflicts_with_features/out/Cargo.toml b/tests/testsuite/cargo_add/multiple_conflicts_with_features/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/multiple_conflicts_with_features/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/multiple_conflicts_with_features/stderr.log b/tests/testsuite/cargo_add/multiple_conflicts_with_features/stderr.log new file mode 100644 index 0000000..72fd9fc --- /dev/null +++ b/tests/testsuite/cargo_add/multiple_conflicts_with_features/stderr.log @@ -0,0 +1 @@ +error: feature `nose` must be qualified by the dependency it's being activated for, like `my-package1/nose`, `your-face/nose` diff --git a/tests/testsuite/cargo_add/multiple_conflicts_with_features/stdout.log b/tests/testsuite/cargo_add/multiple_conflicts_with_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/multiple_conflicts_with_rename/in b/tests/testsuite/cargo_add/multiple_conflicts_with_rename/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/multiple_conflicts_with_rename/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs b/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs new file mode 100644 index 0000000..293ed3e --- /dev/null +++ b/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2 --rename renamed") + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/multiple_conflicts_with_rename/out/Cargo.toml b/tests/testsuite/cargo_add/multiple_conflicts_with_rename/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/multiple_conflicts_with_rename/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/multiple_conflicts_with_rename/stderr.log b/tests/testsuite/cargo_add/multiple_conflicts_with_rename/stderr.log new file mode 100644 index 0000000..e83250e --- /dev/null +++ b/tests/testsuite/cargo_add/multiple_conflicts_with_rename/stderr.log @@ -0,0 +1 @@ +error: cannot specify multiple crates with `--rename` diff --git a/tests/testsuite/cargo_add/multiple_conflicts_with_rename/stdout.log b/tests/testsuite/cargo_add/multiple_conflicts_with_rename/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/namever/in b/tests/testsuite/cargo_add/namever/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/namever/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/namever/mod.rs b/tests/testsuite/cargo_add/namever/mod.rs new file mode 100644 index 0000000..90fda1a --- /dev/null +++ b/tests/testsuite/cargo_add/namever/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1@>=0.1.1 my-package2@0.2.3 my-package") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/namever/out/Cargo.toml b/tests/testsuite/cargo_add/namever/out/Cargo.toml new file mode 100644 index 0000000..1704d34 --- /dev/null +++ b/tests/testsuite/cargo_add/namever/out/Cargo.toml @@ -0,0 +1,10 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package = "99999.0.0" +my-package1 = ">=0.1.1" +my-package2 = "0.2.3" diff --git a/tests/testsuite/cargo_add/namever/stderr.log b/tests/testsuite/cargo_add/namever/stderr.log new file mode 100644 index 0000000..17be8f9 --- /dev/null +++ b/tests/testsuite/cargo_add/namever/stderr.log @@ -0,0 +1,4 @@ + Updating `dummy-registry` index + Adding my-package1 >=0.1.1 to dependencies. + Adding my-package2 v0.2.3 to dependencies. + Adding my-package v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/namever/stdout.log b/tests/testsuite/cargo_add/namever/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/no_args/in b/tests/testsuite/cargo_add/no_args/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/no_args/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/no_args/mod.rs b/tests/testsuite/cargo_add/no_args/mod.rs new file mode 100644 index 0000000..7eca17b --- /dev/null +++ b/tests/testsuite/cargo_add/no_args/mod.rs @@ -0,0 +1,24 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .current_dir(cwd) + .assert() + .code(1) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/no_args/out/Cargo.toml b/tests/testsuite/cargo_add/no_args/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/no_args/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/no_args/stderr.log b/tests/testsuite/cargo_add/no_args/stderr.log new file mode 100644 index 0000000..0274950 --- /dev/null +++ b/tests/testsuite/cargo_add/no_args/stderr.log @@ -0,0 +1,8 @@ +error: the following required arguments were not provided: + |--git > + +Usage: cargo add [OPTIONS] [@] ... + cargo add [OPTIONS] --path ... + cargo add [OPTIONS] --git ... + +For more information, try '--help'. diff --git a/tests/testsuite/cargo_add/no_args/stdout.log b/tests/testsuite/cargo_add/no_args/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/no_default_features/in b/tests/testsuite/cargo_add/no_default_features/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/no_default_features/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/no_default_features/mod.rs b/tests/testsuite/cargo_add/no_default_features/mod.rs new file mode 100644 index 0000000..e72ca3b --- /dev/null +++ b/tests/testsuite/cargo_add/no_default_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --no-default-features") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/no_default_features/out/Cargo.toml b/tests/testsuite/cargo_add/no_default_features/out/Cargo.toml new file mode 100644 index 0000000..ddd02b1 --- /dev/null +++ b/tests/testsuite/cargo_add/no_default_features/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", default-features = false } +my-package2 = { version = "0.4.1", default-features = false } diff --git a/tests/testsuite/cargo_add/no_default_features/stderr.log b/tests/testsuite/cargo_add/no_default_features/stderr.log new file mode 100644 index 0000000..fb8d490 --- /dev/null +++ b/tests/testsuite/cargo_add/no_default_features/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies. + Adding my-package2 v0.4.1 to dependencies. diff --git a/tests/testsuite/cargo_add/no_default_features/stdout.log b/tests/testsuite/cargo_add/no_default_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/no_optional/in b/tests/testsuite/cargo_add/no_optional/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/no_optional/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/no_optional/mod.rs b/tests/testsuite/cargo_add/no_optional/mod.rs new file mode 100644 index 0000000..fdb983b --- /dev/null +++ b/tests/testsuite/cargo_add/no_optional/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --no-optional") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/no_optional/out/Cargo.toml b/tests/testsuite/cargo_add/no_optional/out/Cargo.toml new file mode 100644 index 0000000..c5e0178 --- /dev/null +++ b/tests/testsuite/cargo_add/no_optional/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = "99999.0.0" +my-package2 = "0.4.1" diff --git a/tests/testsuite/cargo_add/no_optional/stderr.log b/tests/testsuite/cargo_add/no_optional/stderr.log new file mode 100644 index 0000000..fb8d490 --- /dev/null +++ b/tests/testsuite/cargo_add/no_optional/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies. + Adding my-package2 v0.4.1 to dependencies. diff --git a/tests/testsuite/cargo_add/no_optional/stdout.log b/tests/testsuite/cargo_add/no_optional/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/offline_empty_cache/in b/tests/testsuite/cargo_add/offline_empty_cache/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/offline_empty_cache/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/offline_empty_cache/mod.rs b/tests/testsuite/cargo_add/offline_empty_cache/mod.rs new file mode 100644 index 0000000..ae74859 --- /dev/null +++ b/tests/testsuite/cargo_add/offline_empty_cache/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("--offline my-package") + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/offline_empty_cache/out/Cargo.toml b/tests/testsuite/cargo_add/offline_empty_cache/out/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/offline_empty_cache/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/offline_empty_cache/stderr.log b/tests/testsuite/cargo_add/offline_empty_cache/stderr.log new file mode 100644 index 0000000..e0260b7 --- /dev/null +++ b/tests/testsuite/cargo_add/offline_empty_cache/stderr.log @@ -0,0 +1 @@ +error: the crate `my-package` could not be found in registry index. diff --git a/tests/testsuite/cargo_add/offline_empty_cache/stdout.log b/tests/testsuite/cargo_add/offline_empty_cache/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/optional/in b/tests/testsuite/cargo_add/optional/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/optional/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/optional/mod.rs b/tests/testsuite/cargo_add/optional/mod.rs new file mode 100644 index 0000000..94d1cbf --- /dev/null +++ b/tests/testsuite/cargo_add/optional/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --optional") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/optional/out/Cargo.toml b/tests/testsuite/cargo_add/optional/out/Cargo.toml new file mode 100644 index 0000000..eda5445 --- /dev/null +++ b/tests/testsuite/cargo_add/optional/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", optional = true } +my-package2 = { version = "0.4.1", optional = true } diff --git a/tests/testsuite/cargo_add/optional/stderr.log b/tests/testsuite/cargo_add/optional/stderr.log new file mode 100644 index 0000000..8cf4812 --- /dev/null +++ b/tests/testsuite/cargo_add/optional/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to optional dependencies. + Adding my-package2 v0.4.1 to optional dependencies. diff --git a/tests/testsuite/cargo_add/optional/stdout.log b/tests/testsuite/cargo_add/optional/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_default_features/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_default_features/in/Cargo.toml new file mode 100644 index 0000000..c5e0178 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_default_features/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = "99999.0.0" +my-package2 = "0.4.1" diff --git a/tests/testsuite/cargo_add/overwrite_default_features/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_default_features/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_default_features/mod.rs b/tests/testsuite/cargo_add/overwrite_default_features/mod.rs new file mode 100644 index 0000000..88bdd80 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_default_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --default-features") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_default_features/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_default_features/out/Cargo.toml new file mode 100644 index 0000000..c5e0178 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_default_features/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = "99999.0.0" +my-package2 = "0.4.1" diff --git a/tests/testsuite/cargo_add/overwrite_default_features/stderr.log b/tests/testsuite/cargo_add/overwrite_default_features/stderr.log new file mode 100644 index 0000000..fb8d490 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_default_features/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies. + Adding my-package2 v0.4.1 to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_default_features/stdout.log b/tests/testsuite/cargo_add/overwrite_default_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/in/Cargo.toml new file mode 100644 index 0000000..73f56a7 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", default-features = true } +my-package2 = { version = "0.4.1", default-features = true } diff --git a/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs b/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs new file mode 100644 index 0000000..e72ca3b --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --no-default-features") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/out/Cargo.toml new file mode 100644 index 0000000..ddd02b1 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", default-features = false } +my-package2 = { version = "0.4.1", default-features = false } diff --git a/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/stderr.log b/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/stderr.log new file mode 100644 index 0000000..fb8d490 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies. + Adding my-package2 v0.4.1 to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/stdout.log b/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_features/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_features/in/Cargo.toml new file mode 100644 index 0000000..11419b2 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_features/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "99999.0.0", features = ["eyes"] } diff --git a/tests/testsuite/cargo_add/overwrite_features/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_features/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_features/mod.rs b/tests/testsuite/cargo_add/overwrite_features/mod.rs new file mode 100644 index 0000000..0b2ab18 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --features nose") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_features/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_features/out/Cargo.toml new file mode 100644 index 0000000..0060d24 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_features/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "99999.0.0", features = ["eyes", "nose"] } diff --git a/tests/testsuite/cargo_add/overwrite_features/stderr.log b/tests/testsuite/cargo_add/overwrite_features/stderr.log new file mode 100644 index 0000000..6154590 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_features/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + + eyes + + nose + - ears + - mouth diff --git a/tests/testsuite/cargo_add/overwrite_features/stdout.log b/tests/testsuite/cargo_add/overwrite_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_git_with_path/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_git_with_path/in/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_git_with_path/in/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_git_with_path/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_git_with_path/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_git_with_path/in/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_git_with_path/in/primary/Cargo.toml new file mode 100644 index 0000000..6cb4d6a --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_git_with_path/in/primary/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { git = "git://git.git", branch = "main", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_git_with_path/in/primary/src/lib.rs b/tests/testsuite/cargo_add/overwrite_git_with_path/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs b/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs new file mode 100644 index 0000000..ab89e3a --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency --path ../dependency") + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_git_with_path/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_git_with_path/out/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_git_with_path/out/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml new file mode 100644 index 0000000..ad12054 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { optional = true, path = "../dependency", version = "0.0.0" } diff --git a/tests/testsuite/cargo_add/overwrite_git_with_path/stderr.log b/tests/testsuite/cargo_add/overwrite_git_with_path/stderr.log new file mode 100644 index 0000000..98abcfc --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_git_with_path/stderr.log @@ -0,0 +1 @@ + Adding cargo-list-test-fixture-dependency (local) to optional dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_git_with_path/stdout.log b/tests/testsuite/cargo_add/overwrite_git_with_path/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/Cargo.toml new file mode 100644 index 0000000..24c5055 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/dependency/Cargo.toml new file mode 100644 index 0000000..bed9320 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/dependency/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "foo" +version = "0.0.0" + +[features] +test = [] \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/primary/Cargo.toml new file mode 100644 index 0000000..a131c94 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, features = ["test"] } \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/primary/src/lib.rs b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs new file mode 100644 index 0000000..1617832 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs @@ -0,0 +1,23 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "-p", "bar"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/Cargo.toml new file mode 100644 index 0000000..24c5055 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/dependency/Cargo.toml new file mode 100644 index 0000000..bed9320 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/dependency/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "foo" +version = "0.0.0" + +[features] +test = [] \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/primary/Cargo.toml new file mode 100644 index 0000000..fb4a126 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, features = ["test"] } diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/stderr.log b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/stderr.log new file mode 100644 index 0000000..3c7133b --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/stderr.log @@ -0,0 +1,3 @@ + Adding foo (workspace) to dependencies. + Features as of v0.0.0: + + test diff --git a/tests/testsuite/cargo_add/overwrite_inherit_features_noop/stdout.log b/tests/testsuite/cargo_add/overwrite_inherit_features_noop/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_noop/in/Cargo.toml new file mode 100644 index 0000000..24c5055 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_noop/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_noop/in/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_noop/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_inherit_noop/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/in/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_noop/in/primary/Cargo.toml new file mode 100644 index 0000000..2ac789d --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_noop/in/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo.workspace = true \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/in/primary/src/lib.rs b/tests/testsuite/cargo_add/overwrite_inherit_noop/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs b/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs new file mode 100644 index 0000000..065fb4f --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "-p", "bar"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_noop/out/Cargo.toml new file mode 100644 index 0000000..24c5055 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_noop/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_noop/out/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_noop/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_noop/out/primary/Cargo.toml new file mode 100644 index 0000000..a574094 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_noop/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo.workspace = true diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/stderr.log b/tests/testsuite/cargo_add/overwrite_inherit_noop/stderr.log new file mode 100644 index 0000000..d2efcc0 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_noop/stderr.log @@ -0,0 +1 @@ + Adding foo (workspace) to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_inherit_noop/stdout.log b/tests/testsuite/cargo_add/overwrite_inherit_noop/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/Cargo.toml new file mode 100644 index 0000000..24c5055 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/primary/Cargo.toml new file mode 100644 index 0000000..228aef6 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, optional = true } \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/primary/src/lib.rs b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs new file mode 100644 index 0000000..065fb4f --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "-p", "bar"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/Cargo.toml new file mode 100644 index 0000000..24c5055 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml new file mode 100644 index 0000000..6dd7fb6 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/stderr.log b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/stderr.log new file mode 100644 index 0000000..da03b11 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/stderr.log @@ -0,0 +1 @@ + Adding foo (workspace) to optional dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/stdout.log b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inline_features/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inline_features/in/Cargo.toml new file mode 100644 index 0000000..11419b2 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inline_features/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "99999.0.0", features = ["eyes"] } diff --git a/tests/testsuite/cargo_add/overwrite_inline_features/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_inline_features/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs b/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs new file mode 100644 index 0000000..356b4d7 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs @@ -0,0 +1,27 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line( + "unrelateed-crate your-face --features your-face/nose,your-face/mouth -Fyour-face/ears", + ) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_inline_features/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inline_features/out/Cargo.toml new file mode 100644 index 0000000..8e9579d --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inline_features/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +unrelateed-crate = "99999.0.0" +your-face = { version = "99999.0.0", features = ["eyes", "nose", "mouth", "ears"] } diff --git a/tests/testsuite/cargo_add/overwrite_inline_features/stderr.log b/tests/testsuite/cargo_add/overwrite_inline_features/stderr.log new file mode 100644 index 0000000..a686cba --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_inline_features/stderr.log @@ -0,0 +1,8 @@ + Updating `dummy-registry` index + Adding unrelateed-crate v99999.0.0 to dependencies. + Adding your-face v99999.0.0 to dependencies. + Features: + + ears + + eyes + + mouth + + nose diff --git a/tests/testsuite/cargo_add/overwrite_inline_features/stdout.log b/tests/testsuite/cargo_add/overwrite_inline_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_name_dev_noop/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_name_dev_noop/in/Cargo.toml new file mode 100644 index 0000000..b69b5d3 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_dev_noop/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +exclude = ["dependency"] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dev-dependencies] +your-face = { version = "0.0.0", path = "dependency", default-features = false, features = ["nose", "mouth"], registry = "alternative" } diff --git a/tests/testsuite/cargo_add/overwrite_name_dev_noop/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_name_dev_noop/in/dependency/Cargo.toml new file mode 100644 index 0000000..8243797 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_dev_noop/in/dependency/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "your-face" +version = "0.0.0" + +[features] +mouth = [] +nose = [] diff --git a/tests/testsuite/cargo_add/overwrite_name_dev_noop/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_name_dev_noop/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_name_dev_noop/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_name_dev_noop/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs b/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs new file mode 100644 index 0000000..b418c78 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_alt_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_alt_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --dev") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_name_dev_noop/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_name_dev_noop/out/Cargo.toml new file mode 100644 index 0000000..b69b5d3 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_dev_noop/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +exclude = ["dependency"] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dev-dependencies] +your-face = { version = "0.0.0", path = "dependency", default-features = false, features = ["nose", "mouth"], registry = "alternative" } diff --git a/tests/testsuite/cargo_add/overwrite_name_dev_noop/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_name_dev_noop/out/dependency/Cargo.toml new file mode 100644 index 0000000..8243797 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_dev_noop/out/dependency/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "your-face" +version = "0.0.0" + +[features] +mouth = [] +nose = [] diff --git a/tests/testsuite/cargo_add/overwrite_name_dev_noop/stderr.log b/tests/testsuite/cargo_add/overwrite_name_dev_noop/stderr.log new file mode 100644 index 0000000..2fe3f6a --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_dev_noop/stderr.log @@ -0,0 +1,4 @@ + Adding your-face (local) to dev-dependencies. + Features: + + mouth + + nose diff --git a/tests/testsuite/cargo_add/overwrite_name_dev_noop/stdout.log b/tests/testsuite/cargo_add/overwrite_name_dev_noop/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_name_noop/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_name_noop/in/Cargo.toml new file mode 100644 index 0000000..bbaf4f5 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_noop/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +exclude = ["dependency"] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" } diff --git a/tests/testsuite/cargo_add/overwrite_name_noop/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_name_noop/in/dependency/Cargo.toml new file mode 100644 index 0000000..8243797 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_noop/in/dependency/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "your-face" +version = "0.0.0" + +[features] +mouth = [] +nose = [] diff --git a/tests/testsuite/cargo_add/overwrite_name_noop/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_name_noop/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_name_noop/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_name_noop/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs b/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs new file mode 100644 index 0000000..193c588 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_alt_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_alt_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml new file mode 100644 index 0000000..bbaf4f5 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +exclude = ["dependency"] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" } diff --git a/tests/testsuite/cargo_add/overwrite_name_noop/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_name_noop/out/dependency/Cargo.toml new file mode 100644 index 0000000..8243797 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_noop/out/dependency/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "your-face" +version = "0.0.0" + +[features] +mouth = [] +nose = [] diff --git a/tests/testsuite/cargo_add/overwrite_name_noop/stderr.log b/tests/testsuite/cargo_add/overwrite_name_noop/stderr.log new file mode 100644 index 0000000..2f0b90d --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_name_noop/stderr.log @@ -0,0 +1,4 @@ + Adding your-face (local) to optional dependencies. + Features: + + mouth + + nose diff --git a/tests/testsuite/cargo_add/overwrite_name_noop/stdout.log b/tests/testsuite/cargo_add/overwrite_name_noop/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_no_default_features/in/Cargo.toml new file mode 100644 index 0000000..c5e0178 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_default_features/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = "99999.0.0" +my-package2 = "0.4.1" diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_no_default_features/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs b/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs new file mode 100644 index 0000000..e72ca3b --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --no-default-features") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_no_default_features/out/Cargo.toml new file mode 100644 index 0000000..ddd02b1 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_default_features/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", default-features = false } +my-package2 = { version = "0.4.1", default-features = false } diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features/stderr.log b/tests/testsuite/cargo_add/overwrite_no_default_features/stderr.log new file mode 100644 index 0000000..fb8d490 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_default_features/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies. + Adding my-package2 v0.4.1 to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features/stdout.log b/tests/testsuite/cargo_add/overwrite_no_default_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/in/Cargo.toml new file mode 100644 index 0000000..ddd02b1 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", default-features = false } +my-package2 = { version = "0.4.1", default-features = false } diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs b/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs new file mode 100644 index 0000000..88bdd80 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --default-features") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/out/Cargo.toml new file mode 100644 index 0000000..b9e8985 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0" } +my-package2 = { version = "0.4.1" } diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/stderr.log b/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/stderr.log new file mode 100644 index 0000000..fb8d490 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies. + Adding my-package2 v0.4.1 to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/stdout.log b/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_no_optional/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_no_optional/in/Cargo.toml new file mode 100644 index 0000000..c5e0178 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_optional/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = "99999.0.0" +my-package2 = "0.4.1" diff --git a/tests/testsuite/cargo_add/overwrite_no_optional/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_no_optional/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs b/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs new file mode 100644 index 0000000..fdb983b --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --no-optional") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_no_optional/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_no_optional/out/Cargo.toml new file mode 100644 index 0000000..c5e0178 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_optional/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = "99999.0.0" +my-package2 = "0.4.1" diff --git a/tests/testsuite/cargo_add/overwrite_no_optional/stderr.log b/tests/testsuite/cargo_add/overwrite_no_optional/stderr.log new file mode 100644 index 0000000..fb8d490 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_optional/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies. + Adding my-package2 v0.4.1 to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_no_optional/stdout.log b/tests/testsuite/cargo_add/overwrite_no_optional/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/Cargo.toml new file mode 100644 index 0000000..8cd2616 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", optional = false } +my-package2 = { version = "0.4.1", optional = false } diff --git a/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs new file mode 100644 index 0000000..94d1cbf --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --optional") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml new file mode 100644 index 0000000..eda5445 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", optional = true } +my-package2 = { version = "0.4.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stderr.log b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stderr.log new file mode 100644 index 0000000..8cf4812 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to optional dependencies. + Adding my-package2 v0.4.1 to optional dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stdout.log b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_optional/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_optional/in/Cargo.toml new file mode 100644 index 0000000..c5e0178 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = "99999.0.0" +my-package2 = "0.4.1" diff --git a/tests/testsuite/cargo_add/overwrite_optional/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_optional/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_optional/mod.rs b/tests/testsuite/cargo_add/overwrite_optional/mod.rs new file mode 100644 index 0000000..94d1cbf --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2@0.4.1 --optional") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml new file mode 100644 index 0000000..eda5445 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", optional = true } +my-package2 = { version = "0.4.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_optional/stderr.log b/tests/testsuite/cargo_add/overwrite_optional/stderr.log new file mode 100644 index 0000000..8cf4812 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to optional dependencies. + Adding my-package2 v0.4.1 to optional dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_optional/stdout.log b/tests/testsuite/cargo_add/overwrite_optional/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/Cargo.toml new file mode 100644 index 0000000..5ef9532 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/Cargo.toml @@ -0,0 +1,13 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[features] +default = ["your-face"] +other = ["your-face/nose"] + +[dependencies] +your-face = { version = "99999.0.0", optional = true } +my-package2 = { version = "0.4.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs b/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs new file mode 100644 index 0000000..c34c293 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face my-package2@0.4.1 --no-optional") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/out/Cargo.toml new file mode 100644 index 0000000..bf6c529 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/out/Cargo.toml @@ -0,0 +1,13 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[features] +default = [] +other = ["your-face/nose"] + +[dependencies] +your-face = { version = "99999.0.0" } +my-package2 = { version = "0.4.1" } diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stderr.log b/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stderr.log new file mode 100644 index 0000000..5fe113e --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stderr.log @@ -0,0 +1,8 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + - ears + - eyes + - mouth + - nose + Adding my-package2 v0.4.1 to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stdout.log b/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_path_noop/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_path_noop/in/Cargo.toml new file mode 100644 index 0000000..bbaf4f5 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_noop/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +exclude = ["dependency"] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" } diff --git a/tests/testsuite/cargo_add/overwrite_path_noop/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_path_noop/in/dependency/Cargo.toml new file mode 100644 index 0000000..8243797 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_noop/in/dependency/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "your-face" +version = "0.0.0" + +[features] +mouth = [] +nose = [] diff --git a/tests/testsuite/cargo_add/overwrite_path_noop/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_path_noop/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_path_noop/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_path_noop/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs b/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs new file mode 100644 index 0000000..f04405a --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_alt_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_alt_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --path ./dependency") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml new file mode 100644 index 0000000..bbaf4f5 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +exclude = ["dependency"] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" } diff --git a/tests/testsuite/cargo_add/overwrite_path_noop/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_path_noop/out/dependency/Cargo.toml new file mode 100644 index 0000000..8243797 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_noop/out/dependency/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "your-face" +version = "0.0.0" + +[features] +mouth = [] +nose = [] diff --git a/tests/testsuite/cargo_add/overwrite_path_noop/stderr.log b/tests/testsuite/cargo_add/overwrite_path_noop/stderr.log new file mode 100644 index 0000000..2f0b90d --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_noop/stderr.log @@ -0,0 +1,4 @@ + Adding your-face (local) to optional dependencies. + Features: + + mouth + + nose diff --git a/tests/testsuite/cargo_add/overwrite_path_noop/stdout.log b/tests/testsuite/cargo_add/overwrite_path_noop/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_path_with_version/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_path_with_version/in/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_with_version/in/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_path_with_version/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_path_with_version/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_path_with_version/in/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_path_with_version/in/primary/Cargo.toml new file mode 100644 index 0000000..9d20b22 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_with_version/in/primary/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { optional = true, path = "../dependency" } diff --git a/tests/testsuite/cargo_add/overwrite_path_with_version/in/primary/src/lib.rs b/tests/testsuite/cargo_add/overwrite_path_with_version/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs b/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs new file mode 100644 index 0000000..32674e2 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency@20.0") + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_path_with_version/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_path_with_version/out/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_with_version/out/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml new file mode 100644 index 0000000..a20f209 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { optional = true, version = "20.0" } diff --git a/tests/testsuite/cargo_add/overwrite_path_with_version/stderr.log b/tests/testsuite/cargo_add/overwrite_path_with_version/stderr.log new file mode 100644 index 0000000..d0b3a4c --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_path_with_version/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding cargo-list-test-fixture-dependency v20.0 to optional dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_path_with_version/stdout.log b/tests/testsuite/cargo_add/overwrite_path_with_version/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_preserves_inline_table/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_preserves_inline_table/in/Cargo.toml new file mode 100644 index 0000000..3dddbbd --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_preserves_inline_table/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face={version="99999.0.0",features=["eyes"]} # Hello world diff --git a/tests/testsuite/cargo_add/overwrite_preserves_inline_table/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_preserves_inline_table/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs b/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs new file mode 100644 index 0000000..0b2ab18 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --features nose") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_preserves_inline_table/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_preserves_inline_table/out/Cargo.toml new file mode 100644 index 0000000..f204a89 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_preserves_inline_table/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face={ version = "99999.0.0", features = ["eyes", "nose"] } # Hello world diff --git a/tests/testsuite/cargo_add/overwrite_preserves_inline_table/stderr.log b/tests/testsuite/cargo_add/overwrite_preserves_inline_table/stderr.log new file mode 100644 index 0000000..6154590 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_preserves_inline_table/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + + eyes + + nose + - ears + - mouth diff --git a/tests/testsuite/cargo_add/overwrite_preserves_inline_table/stdout.log b/tests/testsuite/cargo_add/overwrite_preserves_inline_table/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/in/Cargo.toml new file mode 100644 index 0000000..4502292 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +a1 = { package = "versioned-package", version = "0.1.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs b/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs new file mode 100644 index 0000000..a006c95 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("versioned-package") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/out/Cargo.toml new file mode 100644 index 0000000..9951492 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +a1 = { package = "versioned-package", version = "0.1.1", optional = true } +versioned-package = "99999.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/stderr.log b/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/stderr.log new file mode 100644 index 0000000..305b89f --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding versioned-package v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/stdout.log b/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_rename_with_rename/in/Cargo.toml new file mode 100644 index 0000000..4502292 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_rename/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +a1 = { package = "versioned-package", version = "0.1.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_rename_with_rename/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs b/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs new file mode 100644 index 0000000..e14282b --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("versioned-package --rename a2") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_rename_with_rename/out/Cargo.toml new file mode 100644 index 0000000..790f654 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_rename/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +a1 = { package = "versioned-package", version = "0.1.1", optional = true } +a2 = { version = "99999.0.0", package = "versioned-package" } diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename/stderr.log b/tests/testsuite/cargo_add/overwrite_rename_with_rename/stderr.log new file mode 100644 index 0000000..305b89f --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_rename/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding versioned-package v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename/stdout.log b/tests/testsuite/cargo_add/overwrite_rename_with_rename/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/in/Cargo.toml new file mode 100644 index 0000000..4502292 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +a1 = { package = "versioned-package", version = "0.1.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs new file mode 100644 index 0000000..c0ca2e5 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("versioned-package --rename a1") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml new file mode 100644 index 0000000..4502292 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +a1 = { package = "versioned-package", version = "0.1.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/stderr.log b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/stderr.log new file mode 100644 index 0000000..d69dc92 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding versioned-package v0.1.1 to optional dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/stdout.log b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_version_with_git/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_version_with_git/in/Cargo.toml new file mode 100644 index 0000000..fe41f2a --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_version_with_git/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +versioned-package = { version = "0.1.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_version_with_git/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_version_with_git/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs b/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs new file mode 100644 index 0000000..ce7a0ac --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs @@ -0,0 +1,34 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + let git_dep = cargo_test_support::git::new("versioned-package", |project| { + project + .file( + "Cargo.toml", + &cargo_test_support::basic_manifest("versioned-package", "0.3.0+versioned-package"), + ) + .file("src/lib.rs", "") + }); + let git_url = git_dep.url().to_string(); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["versioned-package", "--git", &git_url]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml new file mode 100644 index 0000000..2600140 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +versioned-package = { version = "0.3.0", optional = true, git = "[ROOTURL]/versioned-package" } diff --git a/tests/testsuite/cargo_add/overwrite_version_with_git/stderr.log b/tests/testsuite/cargo_add/overwrite_version_with_git/stderr.log new file mode 100644 index 0000000..1b77cbe --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_version_with_git/stderr.log @@ -0,0 +1,3 @@ + Updating git repository `[ROOTURL]/versioned-package` + Adding versioned-package (git) to optional dependencies. + Updating git repository `[ROOTURL]/versioned-package` diff --git a/tests/testsuite/cargo_add/overwrite_version_with_git/stdout.log b/tests/testsuite/cargo_add/overwrite_version_with_git/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_version_with_path/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_version_with_path/in/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_version_with_path/in/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_version_with_path/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_version_with_path/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_version_with_path/in/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_version_with_path/in/primary/Cargo.toml new file mode 100644 index 0000000..063b891 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_version_with_path/in/primary/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.1.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_version_with_path/in/primary/src/lib.rs b/tests/testsuite/cargo_add/overwrite_version_with_path/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs b/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs new file mode 100644 index 0000000..ab89e3a --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency --path ../dependency") + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_version_with_path/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_version_with_path/out/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_version_with_path/out/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml new file mode 100644 index 0000000..0725367 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", optional = true, path = "../dependency" } diff --git a/tests/testsuite/cargo_add/overwrite_version_with_path/stderr.log b/tests/testsuite/cargo_add/overwrite_version_with_path/stderr.log new file mode 100644 index 0000000..98abcfc --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_version_with_path/stderr.log @@ -0,0 +1 @@ + Adding cargo-list-test-fixture-dependency (local) to optional dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_version_with_path/stdout.log b/tests/testsuite/cargo_add/overwrite_version_with_path/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_with_rename/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_with_rename/in/Cargo.toml new file mode 100644 index 0000000..fe41f2a --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_with_rename/in/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +versioned-package = { version = "0.1.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_with_rename/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_with_rename/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs b/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs new file mode 100644 index 0000000..05cc2d1 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("versioned-package --rename renamed") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_with_rename/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_with_rename/out/Cargo.toml new file mode 100644 index 0000000..4b74851 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_with_rename/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +renamed = { version = "99999.0.0", package = "versioned-package" } +versioned-package = { version = "0.1.1", optional = true } diff --git a/tests/testsuite/cargo_add/overwrite_with_rename/stderr.log b/tests/testsuite/cargo_add/overwrite_with_rename/stderr.log new file mode 100644 index 0000000..305b89f --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_with_rename/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding versioned-package v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_with_rename/stdout.log b/tests/testsuite/cargo_add/overwrite_with_rename/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep/in/Cargo.toml new file mode 100644 index 0000000..a80d499 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency" } \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep/in/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_workspace_dep/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/in/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep/in/primary/Cargo.toml new file mode 100644 index 0000000..2ac789d --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep/in/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo.workspace = true \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/in/primary/src/lib.rs b/tests/testsuite/cargo_add/overwrite_workspace_dep/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs b/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs new file mode 100644 index 0000000..87ed58f --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "--path", "./dependency", "-p", "bar"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep/out/Cargo.toml new file mode 100644 index 0000000..a80d499 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency" } \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep/out/dependency/Cargo.toml new file mode 100644 index 0000000..2d247d4 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "foo" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep/out/primary/Cargo.toml new file mode 100644 index 0000000..da32f4e --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { version = "0.0.0", path = "../dependency" } diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/stderr.log b/tests/testsuite/cargo_add/overwrite_workspace_dep/stderr.log new file mode 100644 index 0000000..d1bc507 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep/stderr.log @@ -0,0 +1 @@ + Adding foo (local) to dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep/stdout.log b/tests/testsuite/cargo_add/overwrite_workspace_dep/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/Cargo.toml new file mode 100644 index 0000000..a80d499 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency" } \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/dependency/Cargo.toml new file mode 100644 index 0000000..ef9ec77 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/dependency/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "foo" +version = "0.0.0" + +[features] +default-base = [] +default-test-base = [] +default-merge-base = [] +default = ["default-base", "default-test-base", "default-merge-base"] +test-base = [] +test = ["test-base", "default-test-base"] +merge-base = [] +merge = ["merge-base", "default-merge-base"] +unrelated = [] diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/primary/Cargo.toml new file mode 100644 index 0000000..a131c94 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, features = ["test"] } \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/primary/src/lib.rs b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs new file mode 100644 index 0000000..87ed58f --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "--path", "./dependency", "-p", "bar"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/Cargo.toml new file mode 100644 index 0000000..a80d499 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency" } \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/dependency/Cargo.toml new file mode 100644 index 0000000..ef9ec77 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/dependency/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "foo" +version = "0.0.0" + +[features] +default-base = [] +default-test-base = [] +default-merge-base = [] +default = ["default-base", "default-test-base", "default-merge-base"] +test-base = [] +test = ["test-base", "default-test-base"] +merge-base = [] +merge = ["merge-base", "default-merge-base"] +unrelated = [] diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/primary/Cargo.toml new file mode 100644 index 0000000..6f95ded --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { features = ["test"], path = "../dependency", version = "0.0.0" } diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/stderr.log b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/stderr.log new file mode 100644 index 0000000..18ed7c2 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/stderr.log @@ -0,0 +1,10 @@ + Adding foo (local) to dependencies. + Features: + + default-base + + default-merge-base + + default-test-base + + test + + test-base + - merge + - merge-base + - unrelated diff --git a/tests/testsuite/cargo_add/overwrite_workspace_dep_features/stdout.log b/tests/testsuite/cargo_add/overwrite_workspace_dep_features/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/path/in/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/path/in/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/path/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path/in/primary/Cargo.toml b/tests/testsuite/cargo_add/path/in/primary/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/path/in/primary/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path/in/primary/src/lib.rs b/tests/testsuite/cargo_add/path/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path/mod.rs b/tests/testsuite/cargo_add/path/mod.rs new file mode 100644 index 0000000..ab89e3a --- /dev/null +++ b/tests/testsuite/cargo_add/path/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency --path ../dependency") + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/path/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/path/out/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/path/out/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path/out/primary/Cargo.toml b/tests/testsuite/cargo_add/path/out/primary/Cargo.toml new file mode 100644 index 0000000..93476d7 --- /dev/null +++ b/tests/testsuite/cargo_add/path/out/primary/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", path = "../dependency" } diff --git a/tests/testsuite/cargo_add/path/stderr.log b/tests/testsuite/cargo_add/path/stderr.log new file mode 100644 index 0000000..8109d3c --- /dev/null +++ b/tests/testsuite/cargo_add/path/stderr.log @@ -0,0 +1 @@ + Adding cargo-list-test-fixture-dependency (local) to dependencies. diff --git a/tests/testsuite/cargo_add/path/stdout.log b/tests/testsuite/cargo_add/path/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_dev/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/path_dev/in/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/path_dev/in/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_dev/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/path_dev/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_dev/in/primary/Cargo.toml b/tests/testsuite/cargo_add/path_dev/in/primary/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/path_dev/in/primary/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_dev/in/primary/src/lib.rs b/tests/testsuite/cargo_add/path_dev/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_dev/mod.rs b/tests/testsuite/cargo_add/path_dev/mod.rs new file mode 100644 index 0000000..4ae04c7 --- /dev/null +++ b/tests/testsuite/cargo_add/path_dev/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency --path ../dependency --dev") + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/path_dev/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/path_dev/out/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/path_dev/out/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_dev/out/primary/Cargo.toml b/tests/testsuite/cargo_add/path_dev/out/primary/Cargo.toml new file mode 100644 index 0000000..92be59d --- /dev/null +++ b/tests/testsuite/cargo_add/path_dev/out/primary/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dev-dependencies] +cargo-list-test-fixture-dependency = { path = "../dependency" } diff --git a/tests/testsuite/cargo_add/path_dev/stderr.log b/tests/testsuite/cargo_add/path_dev/stderr.log new file mode 100644 index 0000000..d8093d6 --- /dev/null +++ b/tests/testsuite/cargo_add/path_dev/stderr.log @@ -0,0 +1 @@ + Adding cargo-list-test-fixture-dependency (local) to dev-dependencies. diff --git a/tests/testsuite/cargo_add/path_dev/stdout.log b/tests/testsuite/cargo_add/path_dev/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_inferred_name/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name/in/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name/in/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_inferred_name/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/path_inferred_name/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_inferred_name/in/primary/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name/in/primary/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name/in/primary/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_inferred_name/in/primary/src/lib.rs b/tests/testsuite/cargo_add/path_inferred_name/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_inferred_name/mod.rs b/tests/testsuite/cargo_add/path_inferred_name/mod.rs new file mode 100644 index 0000000..ab89e3a --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency --path ../dependency") + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/path_inferred_name/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name/out/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name/out/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_inferred_name/out/primary/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name/out/primary/Cargo.toml new file mode 100644 index 0000000..93476d7 --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name/out/primary/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", path = "../dependency" } diff --git a/tests/testsuite/cargo_add/path_inferred_name/stderr.log b/tests/testsuite/cargo_add/path_inferred_name/stderr.log new file mode 100644 index 0000000..8109d3c --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name/stderr.log @@ -0,0 +1 @@ + Adding cargo-list-test-fixture-dependency (local) to dependencies. diff --git a/tests/testsuite/cargo_add/path_inferred_name/stdout.log b/tests/testsuite/cargo_add/path_inferred_name/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/Cargo.toml new file mode 100644 index 0000000..299859e --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency", "optional"] diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/dependency/Cargo.toml new file mode 100644 index 0000000..34157f4 --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/dependency/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "your-face" +version = "0.1.3" + +[dependencies] +toml_edit = "0.1.5" +atty = "0.2.13" +optional-dependency = { path = "../optional", optional = true } + +[features] +default = ["mouth"] +nose = [] +mouth = ["nose"] +eyes = [] diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/optional/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/optional/Cargo.toml new file mode 100644 index 0000000..0216dba --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/optional/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "optional-dep" +version = "0.1.3" + +[dependencies] +toml_edit = "0.1.5" +atty = "0.2.13" diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/optional/src/lib.rs b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/optional/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/primary/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/primary/Cargo.toml new file mode 100644 index 0000000..5e20016 --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/primary/src/lib.rs b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs new file mode 100644 index 0000000..eadd096 --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("--path ../dependency --features your-face/nose") + .current_dir(&cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/Cargo.toml new file mode 100644 index 0000000..299859e --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency", "optional"] diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/dependency/Cargo.toml new file mode 100644 index 0000000..34157f4 --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/dependency/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "your-face" +version = "0.1.3" + +[dependencies] +toml_edit = "0.1.5" +atty = "0.2.13" +optional-dependency = { path = "../optional", optional = true } + +[features] +default = ["mouth"] +nose = [] +mouth = ["nose"] +eyes = [] diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/primary/Cargo.toml b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/primary/Cargo.toml new file mode 100644 index 0000000..5e20016 --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/out/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/stderr.log b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/stderr.log new file mode 100644 index 0000000..791ca60 --- /dev/null +++ b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/stderr.log @@ -0,0 +1 @@ +error: `your-face/nose` is unsupported when inferring the crate name, use `nose` diff --git a/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/stdout.log b/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_normalized_name/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/path_normalized_name/in/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/path_normalized_name/in/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_normalized_name/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/path_normalized_name/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_normalized_name/in/primary/Cargo.toml b/tests/testsuite/cargo_add/path_normalized_name/in/primary/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/path_normalized_name/in/primary/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_normalized_name/in/primary/src/lib.rs b/tests/testsuite/cargo_add/path_normalized_name/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/path_normalized_name/mod.rs b/tests/testsuite/cargo_add/path_normalized_name/mod.rs new file mode 100644 index 0000000..754f278 --- /dev/null +++ b/tests/testsuite/cargo_add/path_normalized_name/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo_list_test_fixture_dependency --path ../dependency") + .current_dir(&cwd) + .assert() + .failure() // Fuzzy searching for paths isn't supported at this time + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/path_normalized_name/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/path_normalized_name/out/dependency/Cargo.toml new file mode 100644 index 0000000..cbe2441 --- /dev/null +++ b/tests/testsuite/cargo_add/path_normalized_name/out/dependency/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_normalized_name/out/primary/Cargo.toml b/tests/testsuite/cargo_add/path_normalized_name/out/primary/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/path_normalized_name/out/primary/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/path_normalized_name/stderr.log b/tests/testsuite/cargo_add/path_normalized_name/stderr.log new file mode 100644 index 0000000..59b35e3 --- /dev/null +++ b/tests/testsuite/cargo_add/path_normalized_name/stderr.log @@ -0,0 +1 @@ +error: the crate `cargo_list_test_fixture_dependency@[ROOT]/case/dependency` could not be found at `[ROOT]/case/dependency` diff --git a/tests/testsuite/cargo_add/path_normalized_name/stdout.log b/tests/testsuite/cargo_add/path_normalized_name/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/preserve_sorted/in/Cargo.toml b/tests/testsuite/cargo_add/preserve_sorted/in/Cargo.toml new file mode 100644 index 0000000..550e41b --- /dev/null +++ b/tests/testsuite/cargo_add/preserve_sorted/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package = "0.1.1" +versioned-package = "0.1.1" diff --git a/tests/testsuite/cargo_add/preserve_sorted/in/src/lib.rs b/tests/testsuite/cargo_add/preserve_sorted/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/preserve_sorted/mod.rs b/tests/testsuite/cargo_add/preserve_sorted/mod.rs new file mode 100644 index 0000000..4dfb06e --- /dev/null +++ b/tests/testsuite/cargo_add/preserve_sorted/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("toml") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/preserve_sorted/out/Cargo.toml b/tests/testsuite/cargo_add/preserve_sorted/out/Cargo.toml new file mode 100644 index 0000000..cacd510 --- /dev/null +++ b/tests/testsuite/cargo_add/preserve_sorted/out/Cargo.toml @@ -0,0 +1,10 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package = "0.1.1" +toml = "99999.0.0" +versioned-package = "0.1.1" diff --git a/tests/testsuite/cargo_add/preserve_sorted/stderr.log b/tests/testsuite/cargo_add/preserve_sorted/stderr.log new file mode 100644 index 0000000..7c83976 --- /dev/null +++ b/tests/testsuite/cargo_add/preserve_sorted/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding toml v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/preserve_sorted/stdout.log b/tests/testsuite/cargo_add/preserve_sorted/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/preserve_unsorted/in/Cargo.toml b/tests/testsuite/cargo_add/preserve_unsorted/in/Cargo.toml new file mode 100644 index 0000000..f803120 --- /dev/null +++ b/tests/testsuite/cargo_add/preserve_unsorted/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +versioned-package = "0.1.1" +my-package = "0.1.1" diff --git a/tests/testsuite/cargo_add/preserve_unsorted/in/src/lib.rs b/tests/testsuite/cargo_add/preserve_unsorted/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/preserve_unsorted/mod.rs b/tests/testsuite/cargo_add/preserve_unsorted/mod.rs new file mode 100644 index 0000000..4dfb06e --- /dev/null +++ b/tests/testsuite/cargo_add/preserve_unsorted/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("toml") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/preserve_unsorted/out/Cargo.toml b/tests/testsuite/cargo_add/preserve_unsorted/out/Cargo.toml new file mode 100644 index 0000000..244a06a --- /dev/null +++ b/tests/testsuite/cargo_add/preserve_unsorted/out/Cargo.toml @@ -0,0 +1,10 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +versioned-package = "0.1.1" +my-package = "0.1.1" +toml = "99999.0.0" diff --git a/tests/testsuite/cargo_add/preserve_unsorted/stderr.log b/tests/testsuite/cargo_add/preserve_unsorted/stderr.log new file mode 100644 index 0000000..7c83976 --- /dev/null +++ b/tests/testsuite/cargo_add/preserve_unsorted/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding toml v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/preserve_unsorted/stdout.log b/tests/testsuite/cargo_add/preserve_unsorted/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/quiet/in b/tests/testsuite/cargo_add/quiet/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/quiet/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/quiet/mod.rs b/tests/testsuite/cargo_add/quiet/mod.rs new file mode 100644 index 0000000..3578439 --- /dev/null +++ b/tests/testsuite/cargo_add/quiet/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("--quiet your-face") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/quiet/out/Cargo.toml b/tests/testsuite/cargo_add/quiet/out/Cargo.toml new file mode 100644 index 0000000..79d735a --- /dev/null +++ b/tests/testsuite/cargo_add/quiet/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +your-face = "99999.0.0" diff --git a/tests/testsuite/cargo_add/quiet/stderr.log b/tests/testsuite/cargo_add/quiet/stderr.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/quiet/stdout.log b/tests/testsuite/cargo_add/quiet/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/registry/in/Cargo.toml b/tests/testsuite/cargo_add/registry/in/Cargo.toml new file mode 100644 index 0000000..3ecdb66 --- /dev/null +++ b/tests/testsuite/cargo_add/registry/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/registry/in/src/lib.rs b/tests/testsuite/cargo_add/registry/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/registry/mod.rs b/tests/testsuite/cargo_add/registry/mod.rs new file mode 100644 index 0000000..d5ba9ef --- /dev/null +++ b/tests/testsuite/cargo_add/registry/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_alt_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_alt_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2 --registry alternative") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/registry/out/Cargo.toml b/tests/testsuite/cargo_add/registry/out/Cargo.toml new file mode 100644 index 0000000..e856bee --- /dev/null +++ b/tests/testsuite/cargo_add/registry/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", registry = "alternative" } +my-package2 = { version = "99999.0.0", registry = "alternative" } diff --git a/tests/testsuite/cargo_add/registry/stderr.log b/tests/testsuite/cargo_add/registry/stderr.log new file mode 100644 index 0000000..437e780 --- /dev/null +++ b/tests/testsuite/cargo_add/registry/stderr.log @@ -0,0 +1,3 @@ + Updating `alternative` index + Adding my-package1 v99999.0.0 to dependencies. + Adding my-package2 v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/registry/stdout.log b/tests/testsuite/cargo_add/registry/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/rename/in b/tests/testsuite/cargo_add/rename/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/rename/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/rename/mod.rs b/tests/testsuite/cargo_add/rename/mod.rs new file mode 100644 index 0000000..3fefccc --- /dev/null +++ b/tests/testsuite/cargo_add/rename/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package --rename renamed") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/rename/out/Cargo.toml b/tests/testsuite/cargo_add/rename/out/Cargo.toml new file mode 100644 index 0000000..ebcfbbd --- /dev/null +++ b/tests/testsuite/cargo_add/rename/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +renamed = { version = "99999.0.0", package = "my-package" } diff --git a/tests/testsuite/cargo_add/rename/stderr.log b/tests/testsuite/cargo_add/rename/stderr.log new file mode 100644 index 0000000..fd6b711 --- /dev/null +++ b/tests/testsuite/cargo_add/rename/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding my-package v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/rename/stdout.log b/tests/testsuite/cargo_add/rename/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/require_weak/in/Cargo.toml b/tests/testsuite/cargo_add/require_weak/in/Cargo.toml new file mode 100644 index 0000000..54faf17 --- /dev/null +++ b/tests/testsuite/cargo_add/require_weak/in/Cargo.toml @@ -0,0 +1,11 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[features] +eyes = ["your-face?/eyes"] + +[dependencies] +your-face = { version = "99999.0.0", optional = true } diff --git a/tests/testsuite/cargo_add/require_weak/in/src/lib.rs b/tests/testsuite/cargo_add/require_weak/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/require_weak/mod.rs b/tests/testsuite/cargo_add/require_weak/mod.rs new file mode 100644 index 0000000..d99e448 --- /dev/null +++ b/tests/testsuite/cargo_add/require_weak/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --no-optional") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/require_weak/out/Cargo.toml b/tests/testsuite/cargo_add/require_weak/out/Cargo.toml new file mode 100644 index 0000000..a0e4b97 --- /dev/null +++ b/tests/testsuite/cargo_add/require_weak/out/Cargo.toml @@ -0,0 +1,11 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[features] +eyes = ["your-face/eyes"] + +[dependencies] +your-face = { version = "99999.0.0" } diff --git a/tests/testsuite/cargo_add/require_weak/stderr.log b/tests/testsuite/cargo_add/require_weak/stderr.log new file mode 100644 index 0000000..796b960 --- /dev/null +++ b/tests/testsuite/cargo_add/require_weak/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + - ears + - eyes + - mouth + - nose diff --git a/tests/testsuite/cargo_add/require_weak/stdout.log b/tests/testsuite/cargo_add/require_weak/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/sorted_table_with_dotted_item/in/Cargo.toml b/tests/testsuite/cargo_add/sorted_table_with_dotted_item/in/Cargo.toml new file mode 100644 index 0000000..19aa939 --- /dev/null +++ b/tests/testsuite/cargo_add/sorted_table_with_dotted_item/in/Cargo.toml @@ -0,0 +1,13 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +toml = "0.1.1" +versioned-package = "0.1.1" + +[dependencies.my-build-package1] +version = "0.1.1" + diff --git a/tests/testsuite/cargo_add/sorted_table_with_dotted_item/in/src/lib.rs b/tests/testsuite/cargo_add/sorted_table_with_dotted_item/in/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs b/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs new file mode 100644 index 0000000..55e4c22 --- /dev/null +++ b/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("unrelateed-crate") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/sorted_table_with_dotted_item/out/Cargo.toml b/tests/testsuite/cargo_add/sorted_table_with_dotted_item/out/Cargo.toml new file mode 100644 index 0000000..008ff4f --- /dev/null +++ b/tests/testsuite/cargo_add/sorted_table_with_dotted_item/out/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +toml = "0.1.1" +unrelateed-crate = "99999.0.0" +versioned-package = "0.1.1" + +[dependencies.my-build-package1] +version = "0.1.1" + diff --git a/tests/testsuite/cargo_add/sorted_table_with_dotted_item/stderr.log b/tests/testsuite/cargo_add/sorted_table_with_dotted_item/stderr.log new file mode 100644 index 0000000..be1db1c --- /dev/null +++ b/tests/testsuite/cargo_add/sorted_table_with_dotted_item/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding unrelateed-crate v99999.0.0 to dependencies. diff --git a/tests/testsuite/cargo_add/sorted_table_with_dotted_item/stdout.log b/tests/testsuite/cargo_add/sorted_table_with_dotted_item/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/target/in b/tests/testsuite/cargo_add/target/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/target/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/target/mod.rs b/tests/testsuite/cargo_add/target/mod.rs new file mode 100644 index 0000000..e263bad --- /dev/null +++ b/tests/testsuite/cargo_add/target/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2 --target i686-unknown-linux-gnu") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/target/out/Cargo.toml b/tests/testsuite/cargo_add/target/out/Cargo.toml new file mode 100644 index 0000000..9c96ede --- /dev/null +++ b/tests/testsuite/cargo_add/target/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[target.i686-unknown-linux-gnu.dependencies] +my-package1 = "99999.0.0" +my-package2 = "99999.0.0" diff --git a/tests/testsuite/cargo_add/target/stderr.log b/tests/testsuite/cargo_add/target/stderr.log new file mode 100644 index 0000000..3413bcc --- /dev/null +++ b/tests/testsuite/cargo_add/target/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies for target `i686-unknown-linux-gnu`. + Adding my-package2 v99999.0.0 to dependencies for target `i686-unknown-linux-gnu`. diff --git a/tests/testsuite/cargo_add/target/stdout.log b/tests/testsuite/cargo_add/target/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/target_cfg/in b/tests/testsuite/cargo_add/target_cfg/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/target_cfg/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/target_cfg/mod.rs b/tests/testsuite/cargo_add/target_cfg/mod.rs new file mode 100644 index 0000000..43efe8e --- /dev/null +++ b/tests/testsuite/cargo_add/target_cfg/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 my-package2 --target cfg(unix)") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/target_cfg/out/Cargo.toml b/tests/testsuite/cargo_add/target_cfg/out/Cargo.toml new file mode 100644 index 0000000..212ec57 --- /dev/null +++ b/tests/testsuite/cargo_add/target_cfg/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[target."cfg(unix)".dependencies] +my-package1 = "99999.0.0" +my-package2 = "99999.0.0" diff --git a/tests/testsuite/cargo_add/target_cfg/stderr.log b/tests/testsuite/cargo_add/target_cfg/stderr.log new file mode 100644 index 0000000..e405c8d --- /dev/null +++ b/tests/testsuite/cargo_add/target_cfg/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to dependencies for target `cfg(unix)`. + Adding my-package2 v99999.0.0 to dependencies for target `cfg(unix)`. diff --git a/tests/testsuite/cargo_add/target_cfg/stdout.log b/tests/testsuite/cargo_add/target_cfg/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/in/Cargo.toml b/tests/testsuite/cargo_add/unknown_inherited_feature/in/Cargo.toml new file mode 100644 index 0000000..b2a34c9 --- /dev/null +++ b/tests/testsuite/cargo_add/unknown_inherited_feature/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency", features = ["not_recognized"] } diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/unknown_inherited_feature/in/dependency/Cargo.toml new file mode 100644 index 0000000..9a7bc7f --- /dev/null +++ b/tests/testsuite/cargo_add/unknown_inherited_feature/in/dependency/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "foo" +version = "0.0.0" + +[features] +default-base = [] +default-test-base = [] +default-merge-base = [] +long-feature-name-because-of-formatting-reasons = [] +default = [ + "default-base", + "default-test-base", + "default-merge-base", + "long-feature-name-because-of-formatting-reasons", +] +test-base = [] +test = ["test-base", "default-test-base"] +merge-base = [] +merge = ["merge-base", "default-merge-base"] +unrelated = [] diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/unknown_inherited_feature/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/in/primary/Cargo.toml b/tests/testsuite/cargo_add/unknown_inherited_feature/in/primary/Cargo.toml new file mode 100644 index 0000000..fb4a126 --- /dev/null +++ b/tests/testsuite/cargo_add/unknown_inherited_feature/in/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, features = ["test"] } diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/in/primary/src/lib.rs b/tests/testsuite/cargo_add/unknown_inherited_feature/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/mod.rs b/tests/testsuite/cargo_add/unknown_inherited_feature/mod.rs new file mode 100644 index 0000000..8184dac --- /dev/null +++ b/tests/testsuite/cargo_add/unknown_inherited_feature/mod.rs @@ -0,0 +1,23 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .args(["foo", "-p", "bar"]) + .current_dir(cwd) + .assert() + .failure() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/out/Cargo.toml b/tests/testsuite/cargo_add/unknown_inherited_feature/out/Cargo.toml new file mode 100644 index 0000000..b2a34c9 --- /dev/null +++ b/tests/testsuite/cargo_add/unknown_inherited_feature/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["primary", "dependency"] + +[workspace.dependencies] +foo = { version = "0.0.0", path = "./dependency", features = ["not_recognized"] } diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/unknown_inherited_feature/out/dependency/Cargo.toml new file mode 100644 index 0000000..9a7bc7f --- /dev/null +++ b/tests/testsuite/cargo_add/unknown_inherited_feature/out/dependency/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "foo" +version = "0.0.0" + +[features] +default-base = [] +default-test-base = [] +default-merge-base = [] +long-feature-name-because-of-formatting-reasons = [] +default = [ + "default-base", + "default-test-base", + "default-merge-base", + "long-feature-name-because-of-formatting-reasons", +] +test-base = [] +test = ["test-base", "default-test-base"] +merge-base = [] +merge = ["merge-base", "default-merge-base"] +unrelated = [] diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/out/primary/Cargo.toml b/tests/testsuite/cargo_add/unknown_inherited_feature/out/primary/Cargo.toml new file mode 100644 index 0000000..fb4a126 --- /dev/null +++ b/tests/testsuite/cargo_add/unknown_inherited_feature/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bar" +version = "0.0.0" + +[dependencies] +foo = { workspace = true, features = ["test"] } diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/stderr.log b/tests/testsuite/cargo_add/unknown_inherited_feature/stderr.log new file mode 100644 index 0000000..c5aee4d --- /dev/null +++ b/tests/testsuite/cargo_add/unknown_inherited_feature/stderr.log @@ -0,0 +1,7 @@ + Adding foo (workspace) to dependencies. +error: unrecognized feature for crate foo: not_recognized +disabled features: + merge, merge-base, unrelated +enabled features: + default-base, default-merge-base, default-test-base + long-feature-name-because-of-formatting-reasons, test, test-base diff --git a/tests/testsuite/cargo_add/unknown_inherited_feature/stdout.log b/tests/testsuite/cargo_add/unknown_inherited_feature/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/vers/in b/tests/testsuite/cargo_add/vers/in new file mode 120000 index 0000000..6c6a27f --- /dev/null +++ b/tests/testsuite/cargo_add/vers/in @@ -0,0 +1 @@ +../add-basic.in \ No newline at end of file diff --git a/tests/testsuite/cargo_add/vers/mod.rs b/tests/testsuite/cargo_add/vers/mod.rs new file mode 100644 index 0000000..fb78739 --- /dev/null +++ b/tests/testsuite/cargo_add/vers/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package@>=0.1.1") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/vers/out/Cargo.toml b/tests/testsuite/cargo_add/vers/out/Cargo.toml new file mode 100644 index 0000000..c6ca3d6 --- /dev/null +++ b/tests/testsuite/cargo_add/vers/out/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package = ">=0.1.1" diff --git a/tests/testsuite/cargo_add/vers/stderr.log b/tests/testsuite/cargo_add/vers/stderr.log new file mode 100644 index 0000000..7ef92d2 --- /dev/null +++ b/tests/testsuite/cargo_add/vers/stderr.log @@ -0,0 +1,2 @@ + Updating `dummy-registry` index + Adding my-package >=0.1.1 to dependencies. diff --git a/tests/testsuite/cargo_add/vers/stdout.log b/tests/testsuite/cargo_add/vers/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/workspace_name/in/Cargo.toml b/tests/testsuite/cargo_add/workspace_name/in/Cargo.toml new file mode 100644 index 0000000..57e1f30 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_name/in/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency"] diff --git a/tests/testsuite/cargo_add/workspace_name/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/workspace_name/in/dependency/Cargo.toml new file mode 100644 index 0000000..ca4f36d --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_name/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/workspace_name/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/workspace_name/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/workspace_name/in/primary/Cargo.toml b/tests/testsuite/cargo_add/workspace_name/in/primary/Cargo.toml new file mode 100644 index 0000000..5e20016 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_name/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/workspace_name/in/primary/src/lib.rs b/tests/testsuite/cargo_add/workspace_name/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/workspace_name/mod.rs b/tests/testsuite/cargo_add/workspace_name/mod.rs new file mode 100644 index 0000000..ccaf850 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_name/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency") + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/workspace_name/out/Cargo.toml b/tests/testsuite/cargo_add/workspace_name/out/Cargo.toml new file mode 100644 index 0000000..57e1f30 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_name/out/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency"] diff --git a/tests/testsuite/cargo_add/workspace_name/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/workspace_name/out/dependency/Cargo.toml new file mode 100644 index 0000000..ca4f36d --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_name/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/workspace_name/out/primary/Cargo.toml b/tests/testsuite/cargo_add/workspace_name/out/primary/Cargo.toml new file mode 100644 index 0000000..a693df5 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_name/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", path = "../dependency" } diff --git a/tests/testsuite/cargo_add/workspace_name/stderr.log b/tests/testsuite/cargo_add/workspace_name/stderr.log new file mode 100644 index 0000000..8109d3c --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_name/stderr.log @@ -0,0 +1 @@ + Adding cargo-list-test-fixture-dependency (local) to dependencies. diff --git a/tests/testsuite/cargo_add/workspace_name/stdout.log b/tests/testsuite/cargo_add/workspace_name/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/workspace_path/in/Cargo.toml b/tests/testsuite/cargo_add/workspace_path/in/Cargo.toml new file mode 100644 index 0000000..57e1f30 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path/in/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency"] diff --git a/tests/testsuite/cargo_add/workspace_path/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/workspace_path/in/dependency/Cargo.toml new file mode 100644 index 0000000..ca4f36d --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/workspace_path/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/workspace_path/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/workspace_path/in/primary/Cargo.toml b/tests/testsuite/cargo_add/workspace_path/in/primary/Cargo.toml new file mode 100644 index 0000000..5e20016 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/workspace_path/in/primary/src/lib.rs b/tests/testsuite/cargo_add/workspace_path/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/workspace_path/mod.rs b/tests/testsuite/cargo_add/workspace_path/mod.rs new file mode 100644 index 0000000..ab89e3a --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency --path ../dependency") + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/workspace_path/out/Cargo.toml b/tests/testsuite/cargo_add/workspace_path/out/Cargo.toml new file mode 100644 index 0000000..57e1f30 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path/out/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency"] diff --git a/tests/testsuite/cargo_add/workspace_path/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/workspace_path/out/dependency/Cargo.toml new file mode 100644 index 0000000..ca4f36d --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/workspace_path/out/primary/Cargo.toml b/tests/testsuite/cargo_add/workspace_path/out/primary/Cargo.toml new file mode 100644 index 0000000..a693df5 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +cargo-list-test-fixture-dependency = { version = "0.0.0", path = "../dependency" } diff --git a/tests/testsuite/cargo_add/workspace_path/stderr.log b/tests/testsuite/cargo_add/workspace_path/stderr.log new file mode 100644 index 0000000..8109d3c --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path/stderr.log @@ -0,0 +1 @@ + Adding cargo-list-test-fixture-dependency (local) to dependencies. diff --git a/tests/testsuite/cargo_add/workspace_path/stdout.log b/tests/testsuite/cargo_add/workspace_path/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/workspace_path_dev/in/Cargo.toml b/tests/testsuite/cargo_add/workspace_path_dev/in/Cargo.toml new file mode 100644 index 0000000..57e1f30 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path_dev/in/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency"] diff --git a/tests/testsuite/cargo_add/workspace_path_dev/in/dependency/Cargo.toml b/tests/testsuite/cargo_add/workspace_path_dev/in/dependency/Cargo.toml new file mode 100644 index 0000000..ca4f36d --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path_dev/in/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/workspace_path_dev/in/dependency/src/lib.rs b/tests/testsuite/cargo_add/workspace_path_dev/in/dependency/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/workspace_path_dev/in/primary/Cargo.toml b/tests/testsuite/cargo_add/workspace_path_dev/in/primary/Cargo.toml new file mode 100644 index 0000000..5e20016 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path_dev/in/primary/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/workspace_path_dev/in/primary/src/lib.rs b/tests/testsuite/cargo_add/workspace_path_dev/in/primary/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_add/workspace_path_dev/mod.rs b/tests/testsuite/cargo_add/workspace_path_dev/mod.rs new file mode 100644 index 0000000..4ae04c7 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path_dev/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = project_root.join("primary"); + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("cargo-list-test-fixture-dependency --path ../dependency --dev") + .current_dir(&cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/workspace_path_dev/out/Cargo.toml b/tests/testsuite/cargo_add/workspace_path_dev/out/Cargo.toml new file mode 100644 index 0000000..57e1f30 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path_dev/out/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["primary", "dependency"] diff --git a/tests/testsuite/cargo_add/workspace_path_dev/out/dependency/Cargo.toml b/tests/testsuite/cargo_add/workspace_path_dev/out/dependency/Cargo.toml new file mode 100644 index 0000000..ca4f36d --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path_dev/out/dependency/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo-list-test-fixture-dependency" +version = "0.0.0" diff --git a/tests/testsuite/cargo_add/workspace_path_dev/out/primary/Cargo.toml b/tests/testsuite/cargo_add/workspace_path_dev/out/primary/Cargo.toml new file mode 100644 index 0000000..8dfa5c2 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path_dev/out/primary/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dev-dependencies] +cargo-list-test-fixture-dependency = { path = "../dependency" } diff --git a/tests/testsuite/cargo_add/workspace_path_dev/stderr.log b/tests/testsuite/cargo_add/workspace_path_dev/stderr.log new file mode 100644 index 0000000..d8093d6 --- /dev/null +++ b/tests/testsuite/cargo_add/workspace_path_dev/stderr.log @@ -0,0 +1 @@ + Adding cargo-list-test-fixture-dependency (local) to dev-dependencies. diff --git a/tests/testsuite/cargo_add/workspace_path_dev/stdout.log b/tests/testsuite/cargo_add/workspace_path_dev/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_alias_config.rs b/tests/testsuite/cargo_alias_config.rs new file mode 100644 index 0000000..fd4aec9 --- /dev/null +++ b/tests/testsuite/cargo_alias_config.rs @@ -0,0 +1,434 @@ +//! Tests for `[alias]` config command aliases. + +use std::env; + +use cargo_test_support::tools::echo_subcommand; +use cargo_test_support::{basic_bin_manifest, project}; + +#[cargo_test] +fn alias_incorrect_config_type() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + b-cargo-test = 5 + "#, + ) + .build(); + + p.cargo("b-cargo-test -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] invalid configuration for key `alias.b-cargo-test` +expected a list, but found a integer for [..]", + ) + .run(); +} + +#[cargo_test] +fn alias_malformed_config_string() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + b-cargo-test = ` + "#, + ) + .build(); + + p.cargo("b-cargo-test -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] could not load Cargo configuration + +Caused by: + could not parse TOML configuration in `[..]/config` + +Caused by: + [..] + +Caused by: + TOML parse error at line [..] + | + 3 | b-cargo-test = ` + | ^ + invalid string + expected `\"`, `'` +", + ) + .run(); +} + +#[cargo_test] +fn alias_malformed_config_list() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + b-cargo-test = [1, 2] + "#, + ) + .build(); + + p.cargo("b-cargo-test -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] could not load Cargo configuration + +Caused by: + failed to load TOML configuration from `[..]/config` + +Caused by: + [..] `alias` + +Caused by: + [..] `b-cargo-test` + +Caused by: + expected string but found integer in list +", + ) + .run(); +} + +#[cargo_test] +fn alias_config() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + b-cargo-test = "build" + "#, + ) + .build(); + + p.cargo("b-cargo-test -v") + .with_stderr_contains( + "\ +[COMPILING] foo v0.5.0 [..] +[RUNNING] `rustc --crate-name foo [..]", + ) + .run(); +} + +#[cargo_test] +fn dependent_alias() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + b-cargo-test = "build" + a-cargo-test = ["b-cargo-test", "-v"] + "#, + ) + .build(); + + p.cargo("a-cargo-test") + .with_stderr_contains( + "\ +[COMPILING] foo v0.5.0 [..] +[RUNNING] `rustc --crate-name foo [..]", + ) + .run(); +} + +#[cargo_test] +fn builtin_alias_shadowing_external_subcommand() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .executable("cargo-t", "") + .build(); + + let mut paths: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); + paths.push(p.root()); + let path = env::join_paths(paths).unwrap(); + + p.cargo("t") + .env("PATH", &path) + .with_stderr( + "\ +[COMPILING] foo v0.5.0 [..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] unittests src/main.rs [..] +", + ) + .run(); +} + +#[cargo_test] +fn alias_shadowing_external_subcommand() { + let echo = echo_subcommand(); + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + echo = "build" + "#, + ) + .build(); + + let mut paths: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); + paths.push(echo.target_debug_dir()); + let path = env::join_paths(paths).unwrap(); + + p.cargo("echo") + .env("PATH", &path) + .with_stderr("\ +[WARNING] user-defined alias `echo` is shadowing an external subcommand found at: `[ROOT]/cargo-echo/target/debug/cargo-echo[EXE]` +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #10049 . +[COMPILING] foo v0.5.0 [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn default_args_alias() { + let echo = echo_subcommand(); + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + echo = "echo --flag1 --flag2" + test-1 = "echo" + build = "build --verbose" + "#, + ) + .build(); + + let mut paths: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); + paths.push(echo.target_debug_dir()); + let path = env::join_paths(paths).unwrap(); + + p.cargo("echo") + .env("PATH", &path) + .with_status(101) + .with_stderr("\ +[WARNING] user-defined alias `echo` is shadowing an external subcommand found at: `[ROOT]/cargo-echo/target/debug/cargo-echo[EXE]` +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #10049 . +error: alias echo has unresolvable recursive definition: echo -> echo +", + ) + .run(); + + p.cargo("test-1") + .env("PATH", &path) + .with_status(101) + .with_stderr("\ +[WARNING] user-defined alias `echo` is shadowing an external subcommand found at: `[ROOT]/cargo-echo/target/debug/cargo-echo[EXE]` +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #10049 . +error: alias test-1 has unresolvable recursive definition: test-1 -> echo -> echo +", + ) + .run(); + + // Builtins are not expanded by rule + p.cargo("build") + .with_stderr( + "\ +[WARNING] user-defined alias `build` is ignored, because it is shadowed by a built-in command +[COMPILING] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn corecursive_alias() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + test-1 = "test-2 --flag1" + test-2 = "test-3 --flag2" + test-3 = "test-1 --flag3" + "#, + ) + .build(); + + p.cargo("test-1") + .with_status(101) + .with_stderr( + "error: alias test-1 has unresolvable recursive definition: test-1 -> test-2 -> test-3 -> test-1", + ) + .run(); + + p.cargo("test-2") + .with_status(101) + .with_stderr( + "error: alias test-2 has unresolvable recursive definition: test-2 -> test-3 -> test-1 -> test-2", + ) + .run(); +} + +#[cargo_test] +fn alias_list_test() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + b-cargo-test = ["build", "--release"] + "#, + ) + .build(); + + p.cargo("b-cargo-test -v") + .with_stderr_contains("[COMPILING] foo v0.5.0 [..]") + .with_stderr_contains("[RUNNING] `rustc --crate-name [..]") + .run(); +} + +#[cargo_test] +fn alias_with_flags_config() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + b-cargo-test = "build --release" + "#, + ) + .build(); + + p.cargo("b-cargo-test -v") + .with_stderr_contains("[COMPILING] foo v0.5.0 [..]") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]") + .run(); +} + +#[cargo_test] +fn alias_cannot_shadow_builtin_command() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + build = "fetch" + "#, + ) + .build(); + + p.cargo("build") + .with_stderr( + "\ +[WARNING] user-defined alias `build` is ignored, because it is shadowed by a built-in command +[COMPILING] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn alias_override_builtin_alias() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [alias] + b = "run" + "#, + ) + .build(); + + p.cargo("b") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn builtin_alias_takes_options() { + // #6381 + let p = project() + .file("src/lib.rs", "") + .file( + "examples/ex1.rs", + r#"fn main() { println!("{}", std::env::args().skip(1).next().unwrap()) }"#, + ) + .build(); + + p.cargo("r --example ex1 -- asdf").with_stdout("asdf").run(); +} + +#[cargo_test] +fn global_options_with_alias() { + // Check that global options are passed through. + let p = project().file("src/lib.rs", "").build(); + + p.cargo("-v c") + .with_stderr( + "\ +[CHECKING] foo [..] +[RUNNING] `rustc [..] +[FINISHED] dev [..] +", + ) + .run(); +} + +#[cargo_test] +fn weird_check() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("-- check --invalid_argument -some-other-argument") + .with_status(101) + .with_stderr( + "\ +[ERROR] trailing arguments after built-in command `check` are unsupported: `--invalid_argument -some-other-argument` + +To pass the arguments to the subcommand, remove `--` +", + ) + .run(); +} diff --git a/tests/testsuite/cargo_command.rs b/tests/testsuite/cargo_command.rs new file mode 100644 index 0000000..6286938 --- /dev/null +++ b/tests/testsuite/cargo_command.rs @@ -0,0 +1,535 @@ +//! Tests for custom cargo commands and other global command features. + +use std::env; +use std::fs; +use std::io::Read; +use std::path::{Path, PathBuf}; +use std::process::Stdio; +use std::str; + +use cargo_test_support::basic_manifest; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::Package; +use cargo_test_support::tools::echo_subcommand; +use cargo_test_support::{ + basic_bin_manifest, cargo_exe, cargo_process, paths, project, project_in_home, +}; +use cargo_util::paths::join_paths; + +fn path() -> Vec { + env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect() +} + +#[cargo_test] +fn list_commands_with_descriptions() { + let p = project().build(); + p.cargo("--list") + .with_stdout_contains( + " build Compile a local package and all of its dependencies", + ) + // Assert that `read-manifest` prints the right one-line description followed by another + // command, indented. + .with_stdout_contains( + " read-manifest Print a JSON representation of a Cargo.toml manifest.", + ) + .run(); +} + +#[cargo_test] +fn list_builtin_aliases_with_descriptions() { + let p = project().build(); + p.cargo("--list") + .with_stdout_contains(" b alias: build") + .with_stdout_contains(" c alias: check") + .with_stdout_contains(" r alias: run") + .with_stdout_contains(" t alias: test") + .run(); +} + +#[cargo_test] +fn list_custom_aliases_with_descriptions() { + let p = project_in_home("proj") + .file( + &paths::home().join(".cargo").join("config"), + r#" + [alias] + myaliasstr = "foo --bar" + myaliasvec = ["foo", "--bar"] + "#, + ) + .build(); + + p.cargo("--list") + .with_stdout_contains(" myaliasstr alias: foo --bar") + .with_stdout_contains(" myaliasvec alias: foo --bar") + .run(); +} + +#[cargo_test] +fn list_dedupe() { + let p = project() + .executable(Path::new("path-test-1").join("cargo-dupe"), "") + .executable(Path::new("path-test-2").join("cargo-dupe"), "") + .build(); + + let mut path = path(); + path.push(p.root().join("path-test-1")); + path.push(p.root().join("path-test-2")); + let path = env::join_paths(path.iter()).unwrap(); + + p.cargo("--list") + .env("PATH", &path) + .with_stdout_contains_n(" dupe", 1) + .run(); +} + +#[cargo_test] +fn list_command_looks_at_path() { + let proj = project() + .executable(Path::new("path-test").join("cargo-1"), "") + .build(); + + let mut path = path(); + path.push(proj.root().join("path-test")); + let path = env::join_paths(path.iter()).unwrap(); + let output = cargo_process("-v --list") + .env("PATH", &path) + .exec_with_output() + .unwrap(); + let output = str::from_utf8(&output.stdout).unwrap(); + assert!( + output.contains("\n 1 "), + "missing 1: {}", + output + ); +} + +#[cfg(windows)] +#[cargo_test] +fn list_command_looks_at_path_case_mismatch() { + let proj = project() + .executable(Path::new("path-test").join("cargo-1"), "") + .build(); + + let mut path = path(); + path.push(proj.root().join("path-test")); + let path = env::join_paths(path.iter()).unwrap(); + + // See issue #11814: Environment variable names are case-insensitive on Windows. + // We need to check that having "Path" instead of "PATH" is okay. + let output = cargo_process("-v --list") + .env("Path", &path) + .env_remove("PATH") + .exec_with_output() + .unwrap(); + let output = str::from_utf8(&output.stdout).unwrap(); + assert!( + output.contains("\n 1 "), + "missing 1: {}", + output + ); +} + +#[cargo_test] +fn list_command_handles_known_external_commands() { + let p = project() + .executable(Path::new("path-test").join("cargo-fmt"), "") + .build(); + + let fmt_desc = " fmt Formats all bin and lib files of the current crate using rustfmt."; + + // Without path - fmt isn't there + p.cargo("--list") + .env("PATH", "") + .with_stdout_does_not_contain(fmt_desc) + .run(); + + // With path - fmt is there with known description + let mut path = path(); + path.push(p.root().join("path-test")); + let path = env::join_paths(path.iter()).unwrap(); + + p.cargo("--list") + .env("PATH", &path) + .with_stdout_contains(fmt_desc) + .run(); +} + +#[cargo_test] +fn list_command_resolves_symlinks() { + let proj = project() + .symlink(cargo_exe(), Path::new("path-test").join("cargo-2")) + .build(); + + let mut path = path(); + path.push(proj.root().join("path-test")); + let path = env::join_paths(path.iter()).unwrap(); + let output = cargo_process("-v --list") + .env("PATH", &path) + .exec_with_output() + .unwrap(); + let output = str::from_utf8(&output.stdout).unwrap(); + assert!( + output.contains("\n 2 "), + "missing 2: {}", + output + ); +} + +#[cargo_test] +fn find_closest_capital_c_to_c() { + cargo_process("C") + .with_status(101) + .with_stderr_contains( + "\ +error: no such command: `C` + +Did you mean `c`? +", + ) + .run(); +} + +#[cargo_test] +fn find_closest_capital_b_to_b() { + cargo_process("B") + .with_status(101) + .with_stderr_contains( + "\ +error: no such command: `B` + +Did you mean `b`? +", + ) + .run(); +} + +#[cargo_test] +fn find_closest_biuld_to_build() { + cargo_process("biuld") + .with_status(101) + .with_stderr_contains( + "\ +error: no such command: `biuld` + +Did you mean `build`? +", + ) + .run(); + + // But, if we actually have `biuld`, it must work! + // https://github.com/rust-lang/cargo/issues/5201 + Package::new("cargo-biuld", "1.0.0") + .file( + "src/main.rs", + r#" + fn main() { + println!("Similar, but not identical to, build"); + } + "#, + ) + .publish(); + + cargo_process("install cargo-biuld").run(); + cargo_process("biuld") + .with_stdout("Similar, but not identical to, build\n") + .run(); + cargo_process("--list") + .with_stdout_contains( + " build Compile a local package and all of its dependencies\n", + ) + .with_stdout_contains(" biuld\n") + .run(); +} + +#[cargo_test] +fn find_closest_alias() { + let root = paths::root(); + let my_home = root.join("my_home"); + fs::create_dir(&my_home).unwrap(); + fs::write( + &my_home.join("config"), + r#" + [alias] + myalias = "build" + "#, + ) + .unwrap(); + + cargo_process("myalais") + .env("CARGO_HOME", &my_home) + .with_status(101) + .with_stderr_contains( + "\ +error: no such command: `myalais` + +Did you mean `myalias`? +", + ) + .run(); + + // But, if no alias is defined, it must not suggest one! + cargo_process("myalais") + .with_status(101) + .with_stderr_contains( + "\ +error: no such command: `myalais` +", + ) + .with_stderr_does_not_contain( + "\ +Did you mean `myalias`? +", + ) + .run(); +} + +// If a subcommand is more than an edit distance of 3 away, we don't make a suggestion. +#[cargo_test] +fn find_closest_dont_correct_nonsense() { + cargo_process("there-is-no-way-that-there-is-a-command-close-to-this") + .cwd(&paths::root()) + .with_status(101) + .with_stderr( + "\ +[ERROR] no such command: `there-is-no-way-that-there-is-a-command-close-to-this` + +View all installed commands with `cargo --list`", + ) + .run(); +} + +#[cargo_test] +fn displays_subcommand_on_error() { + cargo_process("invalid-command") + .with_status(101) + .with_stderr( + "\ +[ERROR] no such command: `invalid-command` + +View all installed commands with `cargo --list`", + ) + .run(); +} + +#[cargo_test] +fn override_cargo_home() { + let root = paths::root(); + let my_home = root.join("my_home"); + fs::create_dir(&my_home).unwrap(); + fs::write( + &my_home.join("config"), + r#" + [cargo-new] + vcs = "none" + "#, + ) + .unwrap(); + + cargo_process("new foo").env("CARGO_HOME", &my_home).run(); + + assert!(!paths::root().join("foo/.git").is_dir()); + + cargo_process("new foo2").run(); + + assert!(paths::root().join("foo2/.git").is_dir()); +} + +#[cargo_test] +fn cargo_subcommand_env() { + let src = format!( + r#" + use std::env; + + fn main() {{ + println!("{{}}", env::var("{}").unwrap()); + }} + "#, + cargo::CARGO_ENV + ); + + let p = project() + .at("cargo-envtest") + .file("Cargo.toml", &basic_bin_manifest("cargo-envtest")) + .file("src/main.rs", &src) + .build(); + + let target_dir = p.target_debug_dir(); + + p.cargo("build").run(); + assert!(p.bin("cargo-envtest").is_file()); + + let cargo = cargo_exe().canonicalize().unwrap(); + let mut path = path(); + path.push(target_dir.clone()); + let path = env::join_paths(path.iter()).unwrap(); + + cargo_process("envtest") + .env("PATH", &path) + .with_stdout(cargo.to_str().unwrap()) + .run(); + + // Check that subcommands inherit an overridden $CARGO + let envtest_bin = target_dir + .join("cargo-envtest") + .with_extension(std::env::consts::EXE_EXTENSION) + .canonicalize() + .unwrap(); + let envtest_bin = envtest_bin.to_str().unwrap(); + cargo_process("envtest") + .env("PATH", &path) + .env(cargo::CARGO_ENV, &envtest_bin) + .with_stdout(envtest_bin) + .run(); +} + +#[cargo_test] +fn cargo_cmd_bins_vs_explicit_path() { + // Set up `cargo-foo` binary in two places: inside `$HOME/.cargo/bin` and outside of it + // + // Return paths to both places + fn set_up_cargo_foo() -> (PathBuf, PathBuf) { + let p = project() + .at("cargo-foo") + .file("Cargo.toml", &basic_manifest("cargo-foo", "1.0.0")) + .file( + "src/bin/cargo-foo.rs", + r#"fn main() { println!("INSIDE"); }"#, + ) + .file( + "src/bin/cargo-foo2.rs", + r#"fn main() { println!("OUTSIDE"); }"#, + ) + .build(); + p.cargo("build").run(); + let cargo_bin_dir = paths::home().join(".cargo/bin"); + cargo_bin_dir.mkdir_p(); + let root_bin_dir = paths::root().join("bin"); + root_bin_dir.mkdir_p(); + let exe_name = format!("cargo-foo{}", env::consts::EXE_SUFFIX); + fs::rename(p.bin("cargo-foo"), cargo_bin_dir.join(&exe_name)).unwrap(); + fs::rename(p.bin("cargo-foo2"), root_bin_dir.join(&exe_name)).unwrap(); + + (root_bin_dir, cargo_bin_dir) + } + + let (outside_dir, inside_dir) = set_up_cargo_foo(); + + // If `$CARGO_HOME/bin` is not in a path, prefer it over anything in `$PATH`. + // + // This is the historical behavior we don't want to break. + cargo_process("foo").with_stdout_contains("INSIDE").run(); + + // When `$CARGO_HOME/bin` is in the `$PATH` + // use only `$PATH` so the user-defined ordering is respected. + { + cargo_process("foo") + .env( + "PATH", + join_paths(&[&inside_dir, &outside_dir], "PATH").unwrap(), + ) + .with_stdout_contains("INSIDE") + .run(); + + cargo_process("foo") + // Note: trailing slash + .env( + "PATH", + join_paths(&[inside_dir.join(""), outside_dir.join("")], "PATH").unwrap(), + ) + .with_stdout_contains("INSIDE") + .run(); + + cargo_process("foo") + .env( + "PATH", + join_paths(&[&outside_dir, &inside_dir], "PATH").unwrap(), + ) + .with_stdout_contains("OUTSIDE") + .run(); + + cargo_process("foo") + // Note: trailing slash + .env( + "PATH", + join_paths(&[outside_dir.join(""), inside_dir.join("")], "PATH").unwrap(), + ) + .with_stdout_contains("OUTSIDE") + .run(); + } +} + +#[cargo_test] +fn cargo_subcommand_args() { + let p = echo_subcommand(); + let cargo_foo_bin = p.bin("cargo-echo"); + assert!(cargo_foo_bin.is_file()); + + let mut path = path(); + path.push(p.target_debug_dir()); + let path = env::join_paths(path.iter()).unwrap(); + + cargo_process("echo bar -v --help") + .env("PATH", &path) + .with_stdout("echo bar -v --help") + .run(); +} + +#[cargo_test] +fn explain() { + cargo_process("--explain E0001") + .with_stdout_contains( + "This error suggests that the expression arm corresponding to the noted pattern", + ) + .run(); +} + +#[cargo_test] +fn closed_output_ok() { + // Checks that closed output doesn't cause an error. + let mut p = cargo_process("--list").build_command(); + p.stdout(Stdio::piped()).stderr(Stdio::piped()); + let mut child = p.spawn().unwrap(); + // Close stdout + drop(child.stdout.take()); + // Read stderr + let mut s = String::new(); + child + .stderr + .as_mut() + .unwrap() + .read_to_string(&mut s) + .unwrap(); + let status = child.wait().unwrap(); + assert!(status.success()); + assert!(s.is_empty(), "{}", s); +} + +#[cargo_test] +fn subcommand_leading_plus_output_contains() { + cargo_process("+nightly") + .with_status(101) + .with_stderr( + "\ +error: no such command: `+nightly` + +Cargo does not handle `+toolchain` directives. +Did you mean to invoke `cargo` through `rustup` instead?", + ) + .run(); +} + +#[cargo_test] +fn full_did_you_mean() { + cargo_process("bluid") + .with_status(101) + .with_stderr( + "\ +error: no such command: `bluid` + +Did you mean `build`? + +View all installed commands with `cargo --list`", + ) + .run(); +} diff --git a/tests/testsuite/cargo_config.rs b/tests/testsuite/cargo_config.rs new file mode 100644 index 0000000..e367f8e --- /dev/null +++ b/tests/testsuite/cargo_config.rs @@ -0,0 +1,520 @@ +//! Tests for the `cargo config` command. + +use super::config::write_config_at; +use cargo_test_support::paths; +use std::fs; +use std::path::PathBuf; + +fn cargo_process(s: &str) -> cargo_test_support::Execs { + let mut p = cargo_test_support::cargo_process(s); + // Clear out some of the environment added by the default cargo_process so + // the tests don't need to deal with it. + p.env_remove("CARGO_PROFILE_DEV_SPLIT_DEBUGINFO") + .env_remove("CARGO_PROFILE_TEST_SPLIT_DEBUGINFO") + .env_remove("CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO") + .env_remove("CARGO_PROFILE_BENCH_SPLIT_DEBUGINFO") + .env_remove("CARGO_INCREMENTAL"); + p +} + +#[cargo_test] +fn gated() { + cargo_process("config get") + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_status(101) + .with_stderr("\ +error: the `cargo config` command is unstable, pass `-Z unstable-options` to enable it +See https://github.com/rust-lang/cargo/issues/9301 for more information about the `cargo config` command. +") + .run(); +} + +fn common_setup() -> PathBuf { + write_config_at( + paths::home().join(".cargo/config.toml"), + " + [alias] + foo = \"abc --xyz\" + [build] + jobs = 99 + rustflags = [\"--flag-global\"] + [profile.dev] + opt-level = 3 + [profile.dev.package.foo] + opt-level = 1 + [target.'cfg(target_os = \"linux\")'] + runner = \"runme\" + + # How unknown keys are handled. + [extra-table] + somekey = \"somevalue\" + ", + ); + let sub_folder = paths::root().join("foo/.cargo"); + write_config_at( + sub_folder.join("config.toml"), + " + [alias] + sub-example = [\"sub\", \"example\"] + [build] + rustflags = [\"--flag-directory\"] + ", + ); + sub_folder +} + +#[cargo_test] +fn get_toml() { + // Notes: + // - The "extra-table" is shown without a warning. I'm not sure how that + // should be handled, since displaying warnings could cause problems + // with ingesting the output. + // - Environment variables aren't loaded. :( + let sub_folder = common_setup(); + cargo_process("config get -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .env("CARGO_ALIAS_BAR", "cat dog") + .env("CARGO_BUILD_JOBS", "100") + // The weird forward slash in the linux line is due to testsuite normalization. + .with_stdout( + "\ +alias.foo = \"abc --xyz\" +alias.sub-example = [\"sub\", \"example\"] +build.jobs = 99 +build.rustflags = [\"--flag-directory\", \"--flag-global\"] +extra-table.somekey = \"somevalue\" +profile.dev.opt-level = 3 +profile.dev.package.foo.opt-level = 1 +target.\"cfg(target_os = \\\"linux\\\")\".runner = \"runme\" +# The following environment variables may affect the loaded values. +# CARGO_ALIAS_BAR=[..]cat dog[..] +# CARGO_BUILD_JOBS=100 +# CARGO_HOME=[ROOT]/home/.cargo +", + ) + .with_stderr("") + .run(); + + // Env keys work if they are specific. + cargo_process("config get build.jobs -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .env("CARGO_BUILD_JOBS", "100") + .with_stdout("build.jobs = 100") + .with_stderr("") + .run(); + + // Array value. + cargo_process("config get build.rustflags -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_stdout("build.rustflags = [\"--flag-directory\", \"--flag-global\"]") + .with_stderr("") + .run(); + + // Sub-table + cargo_process("config get profile -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_stdout( + "\ +profile.dev.opt-level = 3 +profile.dev.package.foo.opt-level = 1 +", + ) + .with_stderr("") + .run(); + + // Specific profile entry. + cargo_process("config get profile.dev.opt-level -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_stdout("profile.dev.opt-level = 3") + .with_stderr("") + .run(); + + // A key that isn't set. + cargo_process("config get build.rustc -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_status(101) + .with_stdout("") + .with_stderr("error: config value `build.rustc` is not set") + .run(); + + // A key that is not part of Cargo's config schema. + cargo_process("config get not.set -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_status(101) + .with_stdout("") + .with_stderr("error: config value `not.set` is not set") + .run(); +} + +#[cargo_test] +fn get_json() { + // Notes: + // - This does not show env vars at all. :( + let all_json = r#" + { + "alias": { + "foo": "abc --xyz", + "sub-example": [ + "sub", + "example" + ] + }, + "build": { + "jobs": 99, + "rustflags": [ + "--flag-directory", + "--flag-global" + ] + }, + "extra-table": { + "somekey": "somevalue" + }, + "profile": { + "dev": { + "opt-level": 3, + "package": { + "foo": { + "opt-level": 1 + } + } + } + }, + "target": { + "cfg(target_os = \"linux\")": { + "runner": "runme" + } + } + } + "#; + let sub_folder = common_setup(); + cargo_process("config get --format=json -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .env("CARGO_ALIAS_BAR", "cat dog") + .env("CARGO_BUILD_JOBS", "100") + .with_json(all_json) + .with_stderr( + "\ +note: The following environment variables may affect the loaded values. +CARGO_ALIAS_BAR=[..]cat dog[..] +CARGO_BUILD_JOBS=100 +CARGO_HOME=[ROOT]/home/.cargo +", + ) + .run(); + + // json-value is the same for the entire root table + cargo_process("config get --format=json-value -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_json(all_json) + .with_stderr( + "\ +note: The following environment variables may affect the loaded values. +CARGO_HOME=[ROOT]/home/.cargo +", + ) + .run(); + + cargo_process("config get --format=json build.jobs -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_json( + r#" + {"build": {"jobs": 99}} + "#, + ) + .with_stderr("") + .run(); + + cargo_process("config get --format=json-value build.jobs -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_stdout("99") + .with_stderr("") + .run(); +} + +#[cargo_test] +fn show_origin_toml() { + let sub_folder = common_setup(); + cargo_process("config get --show-origin -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_stdout( + "\ +alias.foo = \"abc --xyz\" # [ROOT]/home/.cargo/config.toml +alias.sub-example = [ + \"sub\", # [ROOT]/foo/.cargo/config.toml + \"example\", # [ROOT]/foo/.cargo/config.toml +] +build.jobs = 99 # [ROOT]/home/.cargo/config.toml +build.rustflags = [ + \"--flag-directory\", # [ROOT]/foo/.cargo/config.toml + \"--flag-global\", # [ROOT]/home/.cargo/config.toml +] +extra-table.somekey = \"somevalue\" # [ROOT]/home/.cargo/config.toml +profile.dev.opt-level = 3 # [ROOT]/home/.cargo/config.toml +profile.dev.package.foo.opt-level = 1 # [ROOT]/home/.cargo/config.toml +target.\"cfg(target_os = \\\"linux\\\")\".runner = \"runme\" # [ROOT]/home/.cargo/config.toml +# The following environment variables may affect the loaded values. +# CARGO_HOME=[ROOT]/home/.cargo +", + ) + .with_stderr("") + .run(); + + cargo_process("config get --show-origin build.rustflags -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .env("CARGO_BUILD_RUSTFLAGS", "env1 env2") + .with_stdout( + "\ +build.rustflags = [ + \"--flag-directory\", # [ROOT]/foo/.cargo/config.toml + \"--flag-global\", # [ROOT]/home/.cargo/config.toml + \"env1\", # environment variable `CARGO_BUILD_RUSTFLAGS` + \"env2\", # environment variable `CARGO_BUILD_RUSTFLAGS` +] +", + ) + .with_stderr("") + .run(); +} + +#[cargo_test] +fn show_origin_toml_cli() { + let sub_folder = common_setup(); + cargo_process("config get --show-origin build.jobs -Zunstable-options --config build.jobs=123") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .env("CARGO_BUILD_JOBS", "1") + .with_stdout("build.jobs = 123 # --config cli option") + .with_stderr("") + .run(); + + cargo_process("config get --show-origin build.rustflags -Zunstable-options --config") + .arg("build.rustflags=[\"cli1\",\"cli2\"]") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .env("CARGO_BUILD_RUSTFLAGS", "env1 env2") + .with_stdout( + "\ +build.rustflags = [ + \"--flag-directory\", # [ROOT]/foo/.cargo/config.toml + \"--flag-global\", # [ROOT]/home/.cargo/config.toml + \"cli1\", # --config cli option + \"cli2\", # --config cli option + \"env1\", # environment variable `CARGO_BUILD_RUSTFLAGS` + \"env2\", # environment variable `CARGO_BUILD_RUSTFLAGS` +] +", + ) + .with_stderr("") + .run(); +} + +#[cargo_test] +fn show_origin_json() { + let sub_folder = common_setup(); + cargo_process("config get --show-origin --format=json -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_status(101) + .with_stderr("error: the `json` format does not support --show-origin, try the `toml` format instead") + .run(); +} + +#[cargo_test] +fn unmerged_toml() { + let sub_folder = common_setup(); + cargo_process("config get --merged=no -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .env("CARGO_ALIAS_BAR", "cat dog") + .env("CARGO_BUILD_JOBS", "100") + .with_stdout( + "\ +# Environment variables +# CARGO=[..] +# CARGO_ALIAS_BAR=[..]cat dog[..] +# CARGO_BUILD_JOBS=100 +# CARGO_HOME=[ROOT]/home/.cargo + +# [ROOT]/foo/.cargo/config.toml +alias.sub-example = [\"sub\", \"example\"] +build.rustflags = [\"--flag-directory\"] + +# [ROOT]/home/.cargo/config.toml +alias.foo = \"abc --xyz\" +build.jobs = 99 +build.rustflags = [\"--flag-global\"] +extra-table.somekey = \"somevalue\" +profile.dev.opt-level = 3 +profile.dev.package.foo.opt-level = 1 +target.\"cfg(target_os = \\\"linux\\\")\".runner = \"runme\" + +", + ) + .with_stderr("") + .run(); + + cargo_process("config get --merged=no build.rustflags -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .env("CARGO_BUILD_RUSTFLAGS", "env1 env2") + .with_stdout( + "\ +# Environment variables +# CARGO_BUILD_RUSTFLAGS=[..]env1 env2[..] + +# [ROOT]/foo/.cargo/config.toml +build.rustflags = [\"--flag-directory\"] + +# [ROOT]/home/.cargo/config.toml +build.rustflags = [\"--flag-global\"] + +", + ) + .with_stderr("") + .run(); + + cargo_process("config get --merged=no does.not.exist -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_stderr("") + .with_stderr("") + .run(); + + cargo_process("config get --merged=no build.rustflags.extra -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_status(101) + .with_stderr( + "error: expected table for configuration key `build.rustflags`, \ + but found array in [ROOT]/foo/.cargo/config.toml", + ) + .run(); +} + +#[cargo_test] +fn unmerged_toml_cli() { + let sub_folder = common_setup(); + cargo_process("config get --merged=no build.rustflags -Zunstable-options --config") + .arg("build.rustflags=[\"cli1\",\"cli2\"]") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .env("CARGO_BUILD_RUSTFLAGS", "env1 env2") + .with_stdout( + "\ +# --config cli option +build.rustflags = [\"cli1\", \"cli2\"] + +# Environment variables +# CARGO_BUILD_RUSTFLAGS=[..]env1 env2[..] + +# [ROOT]/foo/.cargo/config.toml +build.rustflags = [\"--flag-directory\"] + +# [ROOT]/home/.cargo/config.toml +build.rustflags = [\"--flag-global\"] + +", + ) + .with_stderr("") + .run(); +} + +#[cargo_test] +fn unmerged_json() { + let sub_folder = common_setup(); + cargo_process("config get --merged=no --format=json -Zunstable-options") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config"]) + .with_status(101) + .with_stderr( + "error: the `json` format does not support --merged=no, try the `toml` format instead", + ) + .run(); +} + +#[cargo_test] +fn includes() { + let sub_folder = common_setup(); + fs::write( + sub_folder.join("config.toml"), + " + include = 'other.toml' + [build] + rustflags = [\"--flag-directory\"] + ", + ) + .unwrap(); + fs::write( + sub_folder.join("other.toml"), + " + [build] + rustflags = [\"--flag-other\"] + ", + ) + .unwrap(); + + cargo_process("config get build.rustflags -Zunstable-options -Zconfig-include") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config", "config-include"]) + .with_stdout(r#"build.rustflags = ["--flag-other", "--flag-directory", "--flag-global"]"#) + .with_stderr("") + .run(); + + cargo_process("config get build.rustflags --show-origin -Zunstable-options -Zconfig-include") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config", "config-include"]) + .with_stdout( + "\ +build.rustflags = [ + \"--flag-other\", # [ROOT]/foo/.cargo/other.toml + \"--flag-directory\", # [ROOT]/foo/.cargo/config.toml + \"--flag-global\", # [ROOT]/home/.cargo/config.toml +] +", + ) + .with_stderr("") + .run(); + + cargo_process("config get --merged=no -Zunstable-options -Zconfig-include") + .cwd(&sub_folder.parent().unwrap()) + .masquerade_as_nightly_cargo(&["cargo-config", "config-include"]) + .with_stdout( + "\ +# Environment variables +# CARGO=[..] +# CARGO_HOME=[ROOT]/home/.cargo + +# [ROOT]/foo/.cargo/other.toml +build.rustflags = [\"--flag-other\"] + +# [ROOT]/foo/.cargo/config.toml +build.rustflags = [\"--flag-directory\"] +include = \"other.toml\" + +# [ROOT]/home/.cargo/config.toml +alias.foo = \"abc --xyz\" +build.jobs = 99 +build.rustflags = [\"--flag-global\"] +extra-table.somekey = \"somevalue\" +profile.dev.opt-level = 3 +profile.dev.package.foo.opt-level = 1 +target.\"cfg(target_os = \\\"linux\\\")\".runner = \"runme\" + +", + ) + .with_stderr("") + .run(); +} diff --git a/tests/testsuite/cargo_env_config.rs b/tests/testsuite/cargo_env_config.rs new file mode 100644 index 0000000..d80c38d --- /dev/null +++ b/tests/testsuite/cargo_env_config.rs @@ -0,0 +1,181 @@ +//! Tests for `[env]` config. + +use cargo_test_support::{basic_bin_manifest, project}; + +#[cargo_test] +fn env_basic() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + use std::env; + fn main() { + println!( "compile-time:{}", env!("ENV_TEST_1233") ); + println!( "run-time:{}", env::var("ENV_TEST_1233").unwrap()); + } + "#, + ) + .file( + ".cargo/config", + r#" + [env] + ENV_TEST_1233 = "Hello" + "#, + ) + .build(); + + p.cargo("run") + .with_stdout_contains("compile-time:Hello") + .with_stdout_contains("run-time:Hello") + .run(); +} + +#[cargo_test] +fn env_invalid() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + fn main() { + } + "#, + ) + .file( + ".cargo/config", + r#" + [env] + ENV_TEST_BOOL = false + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]could not load config key `env.ENV_TEST_BOOL`") + .run(); +} + +#[cargo_test] +fn env_no_cargo_home() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + fn main() { + } + "#, + ) + .file( + ".cargo/config", + r#" + [env] + CARGO_HOME = "/" + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]setting the `CARGO_HOME` environment variable is not supported in the `[env]` configuration table") + .run(); +} + +#[cargo_test] +fn env_force() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + use std::env; + fn main() { + println!( "ENV_TEST_FORCED:{}", env!("ENV_TEST_FORCED") ); + println!( "ENV_TEST_UNFORCED:{}", env!("ENV_TEST_UNFORCED") ); + println!( "ENV_TEST_UNFORCED_DEFAULT:{}", env!("ENV_TEST_UNFORCED_DEFAULT") ); + } + "#, + ) + .file( + ".cargo/config", + r#" + [env] + ENV_TEST_UNFORCED_DEFAULT = "from-config" + ENV_TEST_UNFORCED = { value = "from-config", force = false } + ENV_TEST_FORCED = { value = "from-config", force = true } + "#, + ) + .build(); + + p.cargo("run") + .env("ENV_TEST_FORCED", "from-env") + .env("ENV_TEST_UNFORCED", "from-env") + .env("ENV_TEST_UNFORCED_DEFAULT", "from-env") + .with_stdout_contains("ENV_TEST_FORCED:from-config") + .with_stdout_contains("ENV_TEST_UNFORCED:from-env") + .with_stdout_contains("ENV_TEST_UNFORCED_DEFAULT:from-env") + .run(); +} + +#[cargo_test] +fn env_relative() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo2")) + .file( + "src/main.rs", + r#" + use std::env; + use std::path::Path; + fn main() { + println!( "ENV_TEST_REGULAR:{}", env!("ENV_TEST_REGULAR") ); + println!( "ENV_TEST_REGULAR_DEFAULT:{}", env!("ENV_TEST_REGULAR_DEFAULT") ); + println!( "ENV_TEST_RELATIVE:{}", env!("ENV_TEST_RELATIVE") ); + + assert!( Path::new(env!("ENV_TEST_RELATIVE")).is_absolute() ); + assert!( !Path::new(env!("ENV_TEST_REGULAR")).is_absolute() ); + assert!( !Path::new(env!("ENV_TEST_REGULAR_DEFAULT")).is_absolute() ); + } + "#, + ) + .file( + ".cargo/config", + r#" + [env] + ENV_TEST_REGULAR = { value = "Cargo.toml", relative = false } + ENV_TEST_REGULAR_DEFAULT = "Cargo.toml" + ENV_TEST_RELATIVE = { value = "Cargo.toml", relative = true } + "#, + ) + .build(); + + p.cargo("run").run(); +} + +#[cargo_test] +fn env_no_override() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("unchanged")) + .file( + "src/main.rs", + r#" + use std::env; + fn main() { + println!( "CARGO_PKG_NAME:{}", env!("CARGO_PKG_NAME") ); + } + "#, + ) + .file( + ".cargo/config", + r#" + [env] + CARGO_PKG_NAME = { value = "from-config", force = true } + "#, + ) + .build(); + + p.cargo("run") + .with_stdout_contains("CARGO_PKG_NAME:unchanged") + .run(); +} diff --git a/tests/testsuite/cargo_features.rs b/tests/testsuite/cargo_features.rs new file mode 100644 index 0000000..720d221 --- /dev/null +++ b/tests/testsuite/cargo_features.rs @@ -0,0 +1,711 @@ +//! Tests for `cargo-features` definitions. + +use cargo_test_support::registry::Package; +use cargo_test_support::{project, registry}; + +#[cargo_test] +fn feature_required() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + im-a-teapot = true + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check") + .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + the `im-a-teapot` manifest key is unstable and may not work properly in England + +Caused by: + feature `test-dummy-unstable` is required + + The package requires the Cargo feature called `test-dummy-unstable`, \ + but that feature is not stabilized in this version of Cargo (1.[..]). + Consider adding `cargo-features = [\"test-dummy-unstable\"]` to the top of Cargo.toml \ + (above the [package] table) to tell Cargo you are opting in to use this unstable feature. + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information \ + about the status of this feature. +", + ) + .run(); + + // Same, but stable. + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + the `im-a-teapot` manifest key is unstable and may not work properly in England + +Caused by: + feature `test-dummy-unstable` is required + + The package requires the Cargo feature called `test-dummy-unstable`, \ + but that feature is not stabilized in this version of Cargo (1.[..]). + Consider trying a newer version of Cargo (this may require the nightly release). + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \ + for more information about the status of this feature. +", + ) + .run(); +} + +#[cargo_test] +fn feature_required_dependency() { + // The feature has been stabilized by a future version of Cargo, and + // someone published something uses it, but this version of Cargo has not + // yet stabilized it. Don't suggest editing Cargo.toml, since published + // packages shouldn't be edited. + Package::new("bar", "1.0.0") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + im-a-teapot = true + "#, + ) + .file("src/lib.rs", "") + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] [..] +[DOWNLOADED] bar v1.0.0 [..] +error: failed to download replaced source registry `crates-io` + +Caused by: + failed to parse manifest at `[..]/bar-1.0.0/Cargo.toml` + +Caused by: + the `im-a-teapot` manifest key is unstable and may not work properly in England + +Caused by: + feature `test-dummy-unstable` is required + + The package requires the Cargo feature called `test-dummy-unstable`, \ + but that feature is not stabilized in this version of Cargo (1.[..]). + Consider trying a more recent nightly release. + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \ + for more information about the status of this feature. +", + ) + .run(); + + // Same, but stable. + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to download `bar v1.0.0` + +Caused by: + unable to get packages from source + +Caused by: + failed to download replaced source registry `crates-io` + +Caused by: + failed to parse manifest at `[..]/bar-1.0.0/Cargo.toml` + +Caused by: + the `im-a-teapot` manifest key is unstable and may not work properly in England + +Caused by: + feature `test-dummy-unstable` is required + + The package requires the Cargo feature called `test-dummy-unstable`, \ + but that feature is not stabilized in this version of Cargo (1.[..]). + Consider trying a newer version of Cargo (this may require the nightly release). + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \ + for more information about the status of this feature. +", + ) + .run(); +} + +#[cargo_test] +fn unknown_feature() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["foo"] + + [package] + name = "a" + version = "0.0.1" + authors = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + unknown cargo feature `foo` +", + ) + .run(); +} + +#[cargo_test] +fn stable_feature_warns() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["test-dummy-stable"] + + [package] + name = "a" + version = "0.0.1" + authors = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check") + .with_stderr( + "\ +warning: the cargo feature `test-dummy-stable` has been stabilized in the 1.0 \ +release and is no longer necessary to be listed in the manifest + See https://doc.rust-lang.org/[..]cargo/ for more information about using this feature. +[CHECKING] a [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "-Zallow-features is unstable")] +fn allow_features() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["test-dummy-unstable"] + + [package] + name = "a" + version = "0.0.1" + authors = [] + im-a-teapot = true + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("-Zallow-features=test-dummy-unstable check") + .masquerade_as_nightly_cargo(&["allow-features", "test-dummy-unstable"]) + .with_stderr( + "\ +[CHECKING] a [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("-Zallow-features=test-dummy-unstable,print-im-a-teapot -Zprint-im-a-teapot check") + .masquerade_as_nightly_cargo(&[ + "allow-features", + "test-dummy-unstable", + "print-im-a-teapot", + ]) + .with_stdout("im-a-teapot = true") + .run(); + + p.cargo("-Zallow-features=test-dummy-unstable -Zprint-im-a-teapot check") + .masquerade_as_nightly_cargo(&[ + "allow-features", + "test-dummy-unstable", + "print-im-a-teapot", + ]) + .with_status(101) + .with_stderr( + "\ +error: the feature `print-im-a-teapot` is not in the list of allowed features: [test-dummy-unstable] +", + ) + .run(); + + p.cargo("-Zallow-features= check") + .masquerade_as_nightly_cargo(&["allow-features", "test-dummy-unstable"]) + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + the feature `test-dummy-unstable` is not in the list of allowed features: [] +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "-Zallow-features is unstable")] +fn allow_features_to_rustc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(test_2018_feature)] + "#, + ) + .build(); + + p.cargo("-Zallow-features= check") + .masquerade_as_nightly_cargo(&["allow-features"]) + .with_status(101) + .with_stderr_contains("[..]E0725[..]") + .run(); + + p.cargo("-Zallow-features=test_2018_feature check") + .masquerade_as_nightly_cargo(&["allow-features"]) + .with_stderr( + "\ +[CHECKING] a [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "-Zallow-features is unstable")] +fn allow_features_in_cfg() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["test-dummy-unstable"] + + [package] + name = "a" + version = "0.0.1" + authors = [] + im-a-teapot = true + "#, + ) + .file( + ".cargo/config.toml", + r#" + [unstable] + allow-features = ["test-dummy-unstable", "print-im-a-teapot"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .masquerade_as_nightly_cargo(&[ + "allow-features", + "test-dummy-unstable", + "print-im-a-teapot", + ]) + .with_stderr( + "\ +[CHECKING] a [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("-Zprint-im-a-teapot check") + .masquerade_as_nightly_cargo(&[ + "allow-features", + "test-dummy-unstable", + "print-im-a-teapot", + ]) + .with_stdout("im-a-teapot = true") + .with_stderr("[FINISHED] [..]") + .run(); + + p.cargo("-Zunstable-options check") + .masquerade_as_nightly_cargo(&["allow-features", "test-dummy-unstable", "print-im-a-teapot"]) + .with_status(101) + .with_stderr( + "\ +error: the feature `unstable-options` is not in the list of allowed features: [print-im-a-teapot, test-dummy-unstable] +", + ) + .run(); + + // -Zallow-features overrides .cargo/config + p.cargo("-Zallow-features=test-dummy-unstable -Zprint-im-a-teapot check") + .masquerade_as_nightly_cargo(&[ + "allow-features", + "test-dummy-unstable", + "print-im-a-teapot", + ]) + .with_status(101) + .with_stderr( + "\ +error: the feature `print-im-a-teapot` is not in the list of allowed features: [test-dummy-unstable] +", + ) + .run(); + + p.cargo("-Zallow-features= check") + .masquerade_as_nightly_cargo(&[ + "allow-features", + "test-dummy-unstable", + "print-im-a-teapot", + ]) + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + the feature `test-dummy-unstable` is not in the list of allowed features: [] +", + ) + .run(); +} + +#[cargo_test] +fn nightly_feature_requires_nightly() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["test-dummy-unstable"] + + [package] + name = "a" + version = "0.0.1" + authors = [] + im-a-teapot = true + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check") + .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) + .with_stderr( + "\ +[CHECKING] a [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + the cargo feature `test-dummy-unstable` requires a nightly version of Cargo, \ + but this is the `stable` channel + See [..] + See https://doc.rust-lang.org/[..]cargo/reference/unstable.html for more \ + information about using this feature. +", + ) + .run(); +} + +#[cargo_test] +fn nightly_feature_requires_nightly_in_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "b" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + cargo-features = ["test-dummy-unstable"] + + [package] + name = "a" + version = "0.0.1" + authors = [] + im-a-teapot = true + "#, + ) + .file("a/src/lib.rs", "") + .build(); + p.cargo("check") + .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) + .with_stderr( + "\ +[CHECKING] a [..] +[CHECKING] b [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to get `a` as a dependency of package `b v0.0.1 ([..])` + +Caused by: + failed to load source for dependency `a` + +Caused by: + Unable to update [..] + +Caused by: + failed to parse manifest at `[..]` + +Caused by: + the cargo feature `test-dummy-unstable` requires a nightly version of Cargo, \ + but this is the `stable` channel + See [..] + See https://doc.rust-lang.org/[..]cargo/reference/unstable.html for more \ + information about using this feature. +", + ) + .run(); +} + +#[cargo_test] +fn cant_publish() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["test-dummy-unstable"] + + [package] + name = "a" + version = "0.0.1" + authors = [] + im-a-teapot = true + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check") + .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) + .with_stderr( + "\ +[CHECKING] a [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + the cargo feature `test-dummy-unstable` requires a nightly version of Cargo, \ + but this is the `stable` channel + See [..] + See https://doc.rust-lang.org/[..]cargo/reference/unstable.html for more \ + information about using this feature. +", + ) + .run(); +} + +#[cargo_test] +fn z_flags_rejected() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["test-dummy-unstable"] + + [package] + name = "a" + version = "0.0.1" + authors = [] + im-a-teapot = true + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check -Zprint-im-a-teapot") + .with_status(101) + .with_stderr( + "error: the `-Z` flag is only accepted on the nightly \ + channel of Cargo, but this is the `stable` channel\n\ + See [..]", + ) + .run(); + + p.cargo("check -Zarg") + .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) + .with_status(101) + .with_stderr("error: unknown `-Z` flag specified: arg") + .run(); + + p.cargo("check -Zprint-im-a-teapot") + .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) + .with_stdout("im-a-teapot = true\n") + .with_stderr( + "\ +[CHECKING] a [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn publish_allowed() { + let registry = registry::RegistryBuilder::new() + .http_api() + .http_index() + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["test-dummy-unstable"] + + [package] + name = "a" + version = "0.0.1" + authors = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) + .with_stderr( + "\ +[UPDATING] [..] +[WARNING] [..] +[..] +[PACKAGING] a v0.0.1 [..] +[VERIFYING] a v0.0.1 [..] +[COMPILING] a v0.0.1 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] a v0.0.1 [..] +[UPDATING] [..] +", + ) + .run(); +} + +#[cargo_test] +fn wrong_position() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + cargo-features = ["test-dummy-unstable"] + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check") + .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at [..] + +Caused by: + cargo-features = [\"test-dummy-unstable\"] was found in the wrong location: it \ + should be set at the top of Cargo.toml before any tables +", + ) + .run(); +} + +#[cargo_test] +fn z_stabilized() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("check -Z cache-messages") + .masquerade_as_nightly_cargo(&["always_nightly"]) + .with_stderr( + "\ +warning: flag `-Z cache-messages` has been stabilized in the 1.40 release, \ + and is no longer necessary + Message caching is now always enabled. + +[CHECKING] foo [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("check -Z offline") + .masquerade_as_nightly_cargo(&["always_nightly"]) + .with_status(101) + .with_stderr( + "\ +error: flag `-Z offline` has been stabilized in the 1.36 release + Offline mode is now available via the --offline CLI option + +", + ) + .run(); +} diff --git a/tests/testsuite/cargo_remove/avoid_empty_tables/in b/tests/testsuite/cargo_remove/avoid_empty_tables/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/avoid_empty_tables/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs b/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs new file mode 100644 index 0000000..59a2333 --- /dev/null +++ b/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["clippy"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/avoid_empty_tables/out/Cargo.toml b/tests/testsuite/cargo_remove/avoid_empty_tables/out/Cargo.toml new file mode 100644 index 0000000..09a9ee8 --- /dev/null +++ b/tests/testsuite/cargo_remove/avoid_empty_tables/out/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/avoid_empty_tables/stderr.log b/tests/testsuite/cargo_remove/avoid_empty_tables/stderr.log new file mode 100644 index 0000000..dd71023 --- /dev/null +++ b/tests/testsuite/cargo_remove/avoid_empty_tables/stderr.log @@ -0,0 +1,2 @@ + Removing clippy from dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/avoid_empty_tables/stdout.log b/tests/testsuite/cargo_remove/avoid_empty_tables/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/build/in b/tests/testsuite/cargo_remove/build/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/build/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/build/mod.rs b/tests/testsuite/cargo_remove/build/mod.rs new file mode 100644 index 0000000..f4c9dcb --- /dev/null +++ b/tests/testsuite/cargo_remove/build/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--build", "semver"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/build/out/Cargo.toml b/tests/testsuite/cargo_remove/build/out/Cargo.toml new file mode 100644 index 0000000..babdc0a --- /dev/null +++ b/tests/testsuite/cargo_remove/build/out/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/build/stderr.log b/tests/testsuite/cargo_remove/build/stderr.log new file mode 100644 index 0000000..f037ebe --- /dev/null +++ b/tests/testsuite/cargo_remove/build/stderr.log @@ -0,0 +1,2 @@ + Removing semver from build-dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/build/stdout.log b/tests/testsuite/cargo_remove/build/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/dev/in b/tests/testsuite/cargo_remove/dev/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/dev/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/dev/mod.rs b/tests/testsuite/cargo_remove/dev/mod.rs new file mode 100644 index 0000000..7d61fa9 --- /dev/null +++ b/tests/testsuite/cargo_remove/dev/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--dev", "regex"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/dev/out/Cargo.toml b/tests/testsuite/cargo_remove/dev/out/Cargo.toml new file mode 100644 index 0000000..40744a5 --- /dev/null +++ b/tests/testsuite/cargo_remove/dev/out/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/dev/stderr.log b/tests/testsuite/cargo_remove/dev/stderr.log new file mode 100644 index 0000000..c629b26 --- /dev/null +++ b/tests/testsuite/cargo_remove/dev/stderr.log @@ -0,0 +1,2 @@ + Removing regex from dev-dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/dev/stdout.log b/tests/testsuite/cargo_remove/dev/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/dry_run/in b/tests/testsuite/cargo_remove/dry_run/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/dry_run/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/dry_run/mod.rs b/tests/testsuite/cargo_remove/dry_run/mod.rs new file mode 100644 index 0000000..dca1893 --- /dev/null +++ b/tests/testsuite/cargo_remove/dry_run/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["semver", "--dry-run"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/dry_run/out/Cargo.toml b/tests/testsuite/cargo_remove/dry_run/out/Cargo.toml new file mode 100644 index 0000000..340f06c --- /dev/null +++ b/tests/testsuite/cargo_remove/dry_run/out/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/dry_run/out/src/lib.rs b/tests/testsuite/cargo_remove/dry_run/out/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/dry_run/out/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/dry_run/stderr.log b/tests/testsuite/cargo_remove/dry_run/stderr.log new file mode 100644 index 0000000..8b11891 --- /dev/null +++ b/tests/testsuite/cargo_remove/dry_run/stderr.log @@ -0,0 +1,2 @@ + Removing semver from dependencies +warning: aborting remove due to dry run diff --git a/tests/testsuite/cargo_remove/dry_run/stdout.log b/tests/testsuite/cargo_remove/dry_run/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/gc_patch/mod.rs b/tests/testsuite/cargo_remove/gc_patch/mod.rs new file mode 100644 index 0000000..2c1d592 --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_patch/mod.rs @@ -0,0 +1,72 @@ +use cargo_test_support::basic_manifest; +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::git; +use cargo_test_support::project; +use cargo_test_support::CargoCommand; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + + let git_project1 = git::new("bar1", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + }) + .url(); + + let git_project2 = git::new("bar2", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + }) + .url(); + + let in_project = project() + .file( + "Cargo.toml", + &format!( + "[workspace]\n\ + members = [ \"my-member\" ]\n\ + \n\ + [package]\n\ + name = \"my-project\"\n\ + version = \"0.1.0\"\n\ + \n\ + [dependencies]\n\ + bar = {{ git = \"{git_project1}\" }}\n\ + \n\ + [patch.\"{git_project1}\"]\n\ + bar = {{ git = \"{git_project2}\" }}\n\ + \n\ + [patch.crates-io]\n\ + bar = {{ git = \"{git_project2}\" }}\n", + ), + ) + .file("src/lib.rs", "") + .file( + "my-member/Cargo.toml", + "[package]\n\ + name = \"my-member\"\n\ + version = \"0.1.0\"\n\ + \n\ + [dependencies]\n\ + bar = \"0.1.0\"\n", + ) + .file("my-member/src/lib.rs", "") + .build(); + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["bar"]) + .current_dir(&in_project.root()) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &in_project.root()); +} diff --git a/tests/testsuite/cargo_remove/gc_patch/out/Cargo.toml b/tests/testsuite/cargo_remove/gc_patch/out/Cargo.toml new file mode 100644 index 0000000..2d8c221 --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_patch/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +members = [ "my-member" ] + +[package] +name = "my-project" +version = "0.1.0" + +[patch.crates-io] +bar = { git = "[ROOTURL]/bar2" } diff --git a/tests/testsuite/cargo_remove/gc_patch/out/src/lib.rs b/tests/testsuite/cargo_remove/gc_patch/out/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/gc_patch/stderr.log b/tests/testsuite/cargo_remove/gc_patch/stderr.log new file mode 100644 index 0000000..1dd2e77 --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_patch/stderr.log @@ -0,0 +1,3 @@ + Removing bar from dependencies + Updating git repository `[ROOTURL]/bar2` + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/gc_patch/stdout.log b/tests/testsuite/cargo_remove/gc_patch/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/gc_profile/in/Cargo.toml b/tests/testsuite/cargo_remove/gc_profile/in/Cargo.toml new file mode 100644 index 0000000..d781ad5 --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_profile/in/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" +toml = "0.2.3" +docopt = "0.6" + +[features] +std = ["serde/std", "semver/std"] + +[profile.dev.package.docopt] +opt-level = 3 + +[profile.dev.package."toml@0.1.0"] +opt-level = 3 + +[profile.release.package.toml] +opt-level = 1 +overflow-checks = false diff --git a/tests/testsuite/cargo_remove/gc_profile/in/src/lib.rs b/tests/testsuite/cargo_remove/gc_profile/in/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_profile/in/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/gc_profile/mod.rs b/tests/testsuite/cargo_remove/gc_profile/mod.rs new file mode 100644 index 0000000..7047c92 --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_profile/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["toml"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/gc_profile/out/Cargo.toml b/tests/testsuite/cargo_remove/gc_profile/out/Cargo.toml new file mode 100644 index 0000000..21b43fe --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_profile/out/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" +toml = "0.2.3" +docopt = "0.6" + +[features] +std = ["serde/std", "semver/std"] + +[profile.dev.package.docopt] +opt-level = 3 + +[profile.release.package.toml] +opt-level = 1 +overflow-checks = false diff --git a/tests/testsuite/cargo_remove/gc_profile/stderr.log b/tests/testsuite/cargo_remove/gc_profile/stderr.log new file mode 100644 index 0000000..0e2e38f --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_profile/stderr.log @@ -0,0 +1,2 @@ + Removing toml from dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/gc_profile/stdout.log b/tests/testsuite/cargo_remove/gc_profile/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/gc_replace/in/Cargo.toml b/tests/testsuite/cargo_remove/gc_replace/in/Cargo.toml new file mode 100644 index 0000000..48242c2 --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_replace/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ "my-package" ] + +[replace] +"toml:0.1.0" = { path = "../toml" } diff --git a/tests/testsuite/cargo_remove/gc_replace/in/my-package/Cargo.toml b/tests/testsuite/cargo_remove/gc_replace/in/my-package/Cargo.toml new file mode 100644 index 0000000..bee343a --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_replace/in/my-package/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" +toml = "0.2.3" +docopt = "0.6" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/gc_replace/in/my-package/src/main.rs b/tests/testsuite/cargo_remove/gc_replace/in/my-package/src/main.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_replace/in/my-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/gc_replace/mod.rs b/tests/testsuite/cargo_remove/gc_replace/mod.rs new file mode 100644 index 0000000..717adef --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_replace/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--package", "my-package", "toml"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/gc_replace/out/Cargo.toml b/tests/testsuite/cargo_remove/gc_replace/out/Cargo.toml new file mode 100644 index 0000000..83a6a04 --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_replace/out/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = [ "my-package" ] diff --git a/tests/testsuite/cargo_remove/gc_replace/out/my-package/Cargo.toml b/tests/testsuite/cargo_remove/gc_replace/out/my-package/Cargo.toml new file mode 100644 index 0000000..36ddf7a --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_replace/out/my-package/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" +toml = "0.2.3" +docopt = "0.6" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/gc_replace/out/my-package/src/main.rs b/tests/testsuite/cargo_remove/gc_replace/out/my-package/src/main.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_replace/out/my-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/gc_replace/stderr.log b/tests/testsuite/cargo_remove/gc_replace/stderr.log new file mode 100644 index 0000000..0e2e38f --- /dev/null +++ b/tests/testsuite/cargo_remove/gc_replace/stderr.log @@ -0,0 +1,2 @@ + Removing toml from dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/gc_replace/stdout.log b/tests/testsuite/cargo_remove/gc_replace/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/invalid_arg/in b/tests/testsuite/cargo_remove/invalid_arg/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_arg/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/invalid_arg/mod.rs b/tests/testsuite/cargo_remove/invalid_arg/mod.rs new file mode 100644 index 0000000..d7db4b0 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_arg/mod.rs @@ -0,0 +1,26 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +#[ignore = "temporarily disabled for beta due to clap update"] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["foo", "--flag"]) + .current_dir(cwd) + .assert() + .code(1) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/invalid_arg/out/Cargo.toml b/tests/testsuite/cargo_remove/invalid_arg/out/Cargo.toml new file mode 100644 index 0000000..340f06c --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_arg/out/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_arg/stderr.log b/tests/testsuite/cargo_remove/invalid_arg/stderr.log new file mode 100644 index 0000000..82a1004 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_arg/stderr.log @@ -0,0 +1,7 @@ +error: unexpected argument '--flag' found + + note: to pass '--flag' as a value, use '-- --flag' + +Usage: cargo[EXE] remove ... + +For more information, try '--help'. diff --git a/tests/testsuite/cargo_remove/invalid_arg/stdout.log b/tests/testsuite/cargo_remove/invalid_arg/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/invalid_dep/in b/tests/testsuite/cargo_remove/invalid_dep/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_dep/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/invalid_dep/mod.rs b/tests/testsuite/cargo_remove/invalid_dep/mod.rs new file mode 100644 index 0000000..c4dbeae --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_dep/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["invalid_dependency_name"]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml b/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml new file mode 100644 index 0000000..340f06c --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_dep/stderr.log b/tests/testsuite/cargo_remove/invalid_dep/stderr.log new file mode 100644 index 0000000..eea124d --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_dep/stderr.log @@ -0,0 +1,2 @@ + Removing invalid_dependency_name from dependencies +error: the dependency `invalid_dependency_name` could not be found in `dependencies`. diff --git a/tests/testsuite/cargo_remove/invalid_dep/stdout.log b/tests/testsuite/cargo_remove/invalid_dep/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/invalid_package/in b/tests/testsuite/cargo_remove/invalid_package/in new file mode 120000 index 0000000..e2165e8 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package/in @@ -0,0 +1 @@ +../remove-package.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/invalid_package/mod.rs b/tests/testsuite/cargo_remove/invalid_package/mod.rs new file mode 100644 index 0000000..bff0988 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["docopt", "--package", "dep-c"]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/invalid_package/out/Cargo.toml b/tests/testsuite/cargo_remove/invalid_package/out/Cargo.toml new file mode 100644 index 0000000..7338571 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "dep-a", + "dep-b" +] diff --git a/tests/testsuite/cargo_remove/invalid_package/out/dep-a/Cargo.toml b/tests/testsuite/cargo_remove/invalid_package/out/dep-a/Cargo.toml new file mode 100644 index 0000000..7e87ce3 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package/out/dep-a/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "dep-a" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_package/out/dep-a/src/lib.rs b/tests/testsuite/cargo_remove/invalid_package/out/dep-a/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package/out/dep-a/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/invalid_package/out/dep-b/Cargo.toml b/tests/testsuite/cargo_remove/invalid_package/out/dep-b/Cargo.toml new file mode 100644 index 0000000..37d2d3d --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package/out/dep-b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "dep-b" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_package/out/dep-b/src/lib.rs b/tests/testsuite/cargo_remove/invalid_package/out/dep-b/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package/out/dep-b/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/invalid_package/stderr.log b/tests/testsuite/cargo_remove/invalid_package/stderr.log new file mode 100644 index 0000000..683512c --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package/stderr.log @@ -0,0 +1 @@ +error: package(s) `dep-c` not found in workspace `[ROOT]/case` diff --git a/tests/testsuite/cargo_remove/invalid_package/stdout.log b/tests/testsuite/cargo_remove/invalid_package/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/invalid_package_multiple/in b/tests/testsuite/cargo_remove/invalid_package_multiple/in new file mode 120000 index 0000000..e2165e8 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package_multiple/in @@ -0,0 +1 @@ +../remove-package.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs b/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs new file mode 100644 index 0000000..5093d5d --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["docopt"]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/invalid_package_multiple/out/Cargo.toml b/tests/testsuite/cargo_remove/invalid_package_multiple/out/Cargo.toml new file mode 100644 index 0000000..7338571 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package_multiple/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "dep-a", + "dep-b" +] diff --git a/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-a/Cargo.toml b/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-a/Cargo.toml new file mode 100644 index 0000000..7e87ce3 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-a/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "dep-a" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-a/src/lib.rs b/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-a/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-a/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-b/Cargo.toml b/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-b/Cargo.toml new file mode 100644 index 0000000..37d2d3d --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "dep-b" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-b/src/lib.rs b/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-b/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package_multiple/out/dep-b/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/invalid_package_multiple/stderr.log b/tests/testsuite/cargo_remove/invalid_package_multiple/stderr.log new file mode 100644 index 0000000..8a03c9e --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_package_multiple/stderr.log @@ -0,0 +1,2 @@ +error: `cargo remove` could not determine which package to modify. Use the `--package` option to specify a package. +available packages: dep-a, dep-b diff --git a/tests/testsuite/cargo_remove/invalid_package_multiple/stdout.log b/tests/testsuite/cargo_remove/invalid_package_multiple/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/invalid_section/in b/tests/testsuite/cargo_remove/invalid_section/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_section/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/invalid_section/mod.rs b/tests/testsuite/cargo_remove/invalid_section/mod.rs new file mode 100644 index 0000000..80d42be --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_section/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--build", "docopt"]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/invalid_section/out/Cargo.toml b/tests/testsuite/cargo_remove/invalid_section/out/Cargo.toml new file mode 100644 index 0000000..340f06c --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_section/out/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_section/stderr.log b/tests/testsuite/cargo_remove/invalid_section/stderr.log new file mode 100644 index 0000000..fff5ff0 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_section/stderr.log @@ -0,0 +1,2 @@ + Removing docopt from build-dependencies +error: the dependency `docopt` could not be found in `build-dependencies`. diff --git a/tests/testsuite/cargo_remove/invalid_section/stdout.log b/tests/testsuite/cargo_remove/invalid_section/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/invalid_section_dep/in b/tests/testsuite/cargo_remove/invalid_section_dep/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_section_dep/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs b/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs new file mode 100644 index 0000000..7be8fd6 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--dev", "semver", "regex"]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/invalid_section_dep/out/Cargo.toml b/tests/testsuite/cargo_remove/invalid_section_dep/out/Cargo.toml new file mode 100644 index 0000000..340f06c --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_section_dep/out/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log b/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log new file mode 100644 index 0000000..1926f95 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log @@ -0,0 +1,2 @@ + Removing semver from dev-dependencies +error: the dependency `semver` could not be found in `dev-dependencies`. diff --git a/tests/testsuite/cargo_remove/invalid_section_dep/stdout.log b/tests/testsuite/cargo_remove/invalid_section_dep/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/invalid_target/in b/tests/testsuite/cargo_remove/invalid_target/in new file mode 120000 index 0000000..d5742d0 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_target/in @@ -0,0 +1 @@ +../remove-target.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/invalid_target/mod.rs b/tests/testsuite/cargo_remove/invalid_target/mod.rs new file mode 100644 index 0000000..34deb6c --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_target/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--target", "powerpc-unknown-linux-gnu", "dbus"]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/invalid_target/out/Cargo.toml b/tests/testsuite/cargo_remove/invalid_target/out/Cargo.toml new file mode 100644 index 0000000..14747c7 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_target/out/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "cargo-remove-target-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[target.x86_64-unknown-freebsd.build-dependencies] +semver = "0.1.0" + +[target.x86_64-unknown-linux-gnu.build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[target.x86_64-unknown-linux-gnu.dependencies] +dbus = "0.6.2" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[target.x86_64-unknown-linux-gnu.dev-dependencies] +ncurses = "20.0" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_target/stderr.log b/tests/testsuite/cargo_remove/invalid_target/stderr.log new file mode 100644 index 0000000..5075b80 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_target/stderr.log @@ -0,0 +1,2 @@ + Removing dbus from dependencies for target `powerpc-unknown-linux-gnu` +error: the dependency `dbus` could not be found in `target.powerpc-unknown-linux-gnu.dependencies`. diff --git a/tests/testsuite/cargo_remove/invalid_target/stdout.log b/tests/testsuite/cargo_remove/invalid_target/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/invalid_target_dep/in b/tests/testsuite/cargo_remove/invalid_target_dep/in new file mode 120000 index 0000000..d5742d0 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_target_dep/in @@ -0,0 +1 @@ +../remove-target.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs b/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs new file mode 100644 index 0000000..e04418f --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--target", "x86_64-unknown-linux-gnu", "toml"]) + .current_dir(cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/invalid_target_dep/out/Cargo.toml b/tests/testsuite/cargo_remove/invalid_target_dep/out/Cargo.toml new file mode 100644 index 0000000..14747c7 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_target_dep/out/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "cargo-remove-target-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[target.x86_64-unknown-freebsd.build-dependencies] +semver = "0.1.0" + +[target.x86_64-unknown-linux-gnu.build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[target.x86_64-unknown-linux-gnu.dependencies] +dbus = "0.6.2" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[target.x86_64-unknown-linux-gnu.dev-dependencies] +ncurses = "20.0" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log b/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log new file mode 100644 index 0000000..54bfe08 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log @@ -0,0 +1,2 @@ + Removing toml from dependencies for target `x86_64-unknown-linux-gnu` +error: the dependency `toml` could not be found in `target.x86_64-unknown-linux-gnu.dependencies`. diff --git a/tests/testsuite/cargo_remove/invalid_target_dep/stdout.log b/tests/testsuite/cargo_remove/invalid_target_dep/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/mod.rs b/tests/testsuite/cargo_remove/mod.rs new file mode 100644 index 0000000..fd8b4a2 --- /dev/null +++ b/tests/testsuite/cargo_remove/mod.rs @@ -0,0 +1,88 @@ +mod avoid_empty_tables; +mod build; +mod dev; +mod dry_run; +mod gc_patch; +mod gc_profile; +mod gc_replace; +mod invalid_arg; +mod invalid_dep; +mod invalid_package; +mod invalid_package_multiple; +mod invalid_section; +mod invalid_section_dep; +mod invalid_target; +mod invalid_target_dep; +mod multiple_deps; +mod multiple_dev; +mod no_arg; +mod offline; +mod optional_dep_feature; +mod optional_feature; +mod package; +mod remove_basic; +mod target; +mod target_build; +mod target_dev; +mod update_lock_file; +mod workspace; +mod workspace_non_virtual; +mod workspace_preserved; + +fn init_registry() { + cargo_test_support::registry::init(); + add_registry_packages(false); +} + +fn add_registry_packages(alt: bool) { + for name in [ + "clippy", + "dbus", + "docopt", + "ncurses", + "pad", + "regex", + "rustc-serialize", + "toml", + ] { + cargo_test_support::registry::Package::new(name, "0.1.1+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "0.2.0+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "0.2.3+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "0.4.1+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "0.6.2+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "0.9.9+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "1.0.90+my-package") + .alternative(alt) + .publish(); + cargo_test_support::registry::Package::new(name, "20.0.0+my-package") + .alternative(alt) + .publish(); + } + + for name in ["semver", "serde"] { + cargo_test_support::registry::Package::new(name, "0.1.1") + .alternative(alt) + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new(name, "0.9.0") + .alternative(alt) + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new(name, "1.0.90") + .alternative(alt) + .feature("std", &[]) + .publish(); + } +} diff --git a/tests/testsuite/cargo_remove/multiple_deps/in b/tests/testsuite/cargo_remove/multiple_deps/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/multiple_deps/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/multiple_deps/mod.rs b/tests/testsuite/cargo_remove/multiple_deps/mod.rs new file mode 100644 index 0000000..35922b7 --- /dev/null +++ b/tests/testsuite/cargo_remove/multiple_deps/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["docopt", "semver"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/multiple_deps/out/Cargo.toml b/tests/testsuite/cargo_remove/multiple_deps/out/Cargo.toml new file mode 100644 index 0000000..53cde08 --- /dev/null +++ b/tests/testsuite/cargo_remove/multiple_deps/out/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +rustc-serialize = "0.4" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/multiple_deps/stderr.log b/tests/testsuite/cargo_remove/multiple_deps/stderr.log new file mode 100644 index 0000000..1eb59ac --- /dev/null +++ b/tests/testsuite/cargo_remove/multiple_deps/stderr.log @@ -0,0 +1,3 @@ + Removing docopt from dependencies + Removing semver from dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/multiple_deps/stdout.log b/tests/testsuite/cargo_remove/multiple_deps/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/multiple_dev/in b/tests/testsuite/cargo_remove/multiple_dev/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/multiple_dev/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/multiple_dev/mod.rs b/tests/testsuite/cargo_remove/multiple_dev/mod.rs new file mode 100644 index 0000000..5eac7e2 --- /dev/null +++ b/tests/testsuite/cargo_remove/multiple_dev/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--dev", "regex", "serde"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml b/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml new file mode 100644 index 0000000..d961b2b --- /dev/null +++ b/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[features] +std = ["semver/std"] diff --git a/tests/testsuite/cargo_remove/multiple_dev/stderr.log b/tests/testsuite/cargo_remove/multiple_dev/stderr.log new file mode 100644 index 0000000..a3042dc --- /dev/null +++ b/tests/testsuite/cargo_remove/multiple_dev/stderr.log @@ -0,0 +1,3 @@ + Removing regex from dev-dependencies + Removing serde from dev-dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/multiple_dev/stdout.log b/tests/testsuite/cargo_remove/multiple_dev/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/no_arg/in b/tests/testsuite/cargo_remove/no_arg/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/no_arg/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/no_arg/mod.rs b/tests/testsuite/cargo_remove/no_arg/mod.rs new file mode 100644 index 0000000..d0c66f9 --- /dev/null +++ b/tests/testsuite/cargo_remove/no_arg/mod.rs @@ -0,0 +1,24 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .current_dir(cwd) + .assert() + .code(1) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/no_arg/out/Cargo.toml b/tests/testsuite/cargo_remove/no_arg/out/Cargo.toml new file mode 100644 index 0000000..340f06c --- /dev/null +++ b/tests/testsuite/cargo_remove/no_arg/out/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/no_arg/stderr.log b/tests/testsuite/cargo_remove/no_arg/stderr.log new file mode 100644 index 0000000..54fa9f4 --- /dev/null +++ b/tests/testsuite/cargo_remove/no_arg/stderr.log @@ -0,0 +1,6 @@ +error: the following required arguments were not provided: + ... + +Usage: cargo[EXE] remove ... + +For more information, try '--help'. diff --git a/tests/testsuite/cargo_remove/no_arg/stdout.log b/tests/testsuite/cargo_remove/no_arg/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/offline/in b/tests/testsuite/cargo_remove/offline/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/offline/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/offline/mod.rs b/tests/testsuite/cargo_remove/offline/mod.rs new file mode 100644 index 0000000..d034639 --- /dev/null +++ b/tests/testsuite/cargo_remove/offline/mod.rs @@ -0,0 +1,32 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + // run the metadata command to populate the cache + snapbox::cmd::Command::cargo_ui() + .arg("metadata") + .current_dir(cwd) + .assert() + .success(); + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["docopt", "--offline"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/offline/out/Cargo.toml b/tests/testsuite/cargo_remove/offline/out/Cargo.toml new file mode 100644 index 0000000..b8628ee --- /dev/null +++ b/tests/testsuite/cargo_remove/offline/out/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/offline/stderr.log b/tests/testsuite/cargo_remove/offline/stderr.log new file mode 100644 index 0000000..7083976 --- /dev/null +++ b/tests/testsuite/cargo_remove/offline/stderr.log @@ -0,0 +1 @@ + Removing docopt from dependencies diff --git a/tests/testsuite/cargo_remove/offline/stdout.log b/tests/testsuite/cargo_remove/offline/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/optional_dep_feature/in b/tests/testsuite/cargo_remove/optional_dep_feature/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/optional_dep_feature/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs b/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs new file mode 100644 index 0000000..cae736b --- /dev/null +++ b/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--dev", "serde"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml b/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml new file mode 100644 index 0000000..63112d3 --- /dev/null +++ b/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" + +[features] +std = ["semver/std"] diff --git a/tests/testsuite/cargo_remove/optional_dep_feature/stderr.log b/tests/testsuite/cargo_remove/optional_dep_feature/stderr.log new file mode 100644 index 0000000..72c9f92 --- /dev/null +++ b/tests/testsuite/cargo_remove/optional_dep_feature/stderr.log @@ -0,0 +1,2 @@ + Removing serde from dev-dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/optional_dep_feature/stdout.log b/tests/testsuite/cargo_remove/optional_dep_feature/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/optional_feature/in b/tests/testsuite/cargo_remove/optional_feature/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/optional_feature/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/optional_feature/mod.rs b/tests/testsuite/cargo_remove/optional_feature/mod.rs new file mode 100644 index 0000000..af54226 --- /dev/null +++ b/tests/testsuite/cargo_remove/optional_feature/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["semver"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/optional_feature/out/Cargo.toml b/tests/testsuite/cargo_remove/optional_feature/out/Cargo.toml new file mode 100644 index 0000000..9ac0b1b --- /dev/null +++ b/tests/testsuite/cargo_remove/optional_feature/out/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/optional_feature/stderr.log b/tests/testsuite/cargo_remove/optional_feature/stderr.log new file mode 100644 index 0000000..2dc546f --- /dev/null +++ b/tests/testsuite/cargo_remove/optional_feature/stderr.log @@ -0,0 +1,2 @@ + Removing semver from dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/optional_feature/stdout.log b/tests/testsuite/cargo_remove/optional_feature/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/package/in b/tests/testsuite/cargo_remove/package/in new file mode 120000 index 0000000..e2165e8 --- /dev/null +++ b/tests/testsuite/cargo_remove/package/in @@ -0,0 +1 @@ +../remove-package.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/package/mod.rs b/tests/testsuite/cargo_remove/package/mod.rs new file mode 100644 index 0000000..2714f31 --- /dev/null +++ b/tests/testsuite/cargo_remove/package/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["docopt", "--package", "dep-a"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/package/out/Cargo.toml b/tests/testsuite/cargo_remove/package/out/Cargo.toml new file mode 100644 index 0000000..7338571 --- /dev/null +++ b/tests/testsuite/cargo_remove/package/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "dep-a", + "dep-b" +] diff --git a/tests/testsuite/cargo_remove/package/out/dep-a/Cargo.toml b/tests/testsuite/cargo_remove/package/out/dep-a/Cargo.toml new file mode 100644 index 0000000..5f2bfe6 --- /dev/null +++ b/tests/testsuite/cargo_remove/package/out/dep-a/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "dep-a" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/package/out/dep-a/src/lib.rs b/tests/testsuite/cargo_remove/package/out/dep-a/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/package/out/dep-a/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/package/out/dep-b/Cargo.toml b/tests/testsuite/cargo_remove/package/out/dep-b/Cargo.toml new file mode 100644 index 0000000..37d2d3d --- /dev/null +++ b/tests/testsuite/cargo_remove/package/out/dep-b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "dep-b" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/package/out/dep-b/src/lib.rs b/tests/testsuite/cargo_remove/package/out/dep-b/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/package/out/dep-b/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/package/stderr.log b/tests/testsuite/cargo_remove/package/stderr.log new file mode 100644 index 0000000..231026f --- /dev/null +++ b/tests/testsuite/cargo_remove/package/stderr.log @@ -0,0 +1,2 @@ + Removing docopt from dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/package/stdout.log b/tests/testsuite/cargo_remove/package/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/remove-basic.in/Cargo.toml b/tests/testsuite/cargo_remove/remove-basic.in/Cargo.toml new file mode 100644 index 0000000..340f06c --- /dev/null +++ b/tests/testsuite/cargo_remove/remove-basic.in/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/remove-basic.in/src/lib.rs b/tests/testsuite/cargo_remove/remove-basic.in/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/remove-basic.in/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/remove-package.in/Cargo.toml b/tests/testsuite/cargo_remove/remove-package.in/Cargo.toml new file mode 100644 index 0000000..7338571 --- /dev/null +++ b/tests/testsuite/cargo_remove/remove-package.in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "dep-a", + "dep-b" +] diff --git a/tests/testsuite/cargo_remove/remove-package.in/dep-a/Cargo.toml b/tests/testsuite/cargo_remove/remove-package.in/dep-a/Cargo.toml new file mode 100644 index 0000000..7e87ce3 --- /dev/null +++ b/tests/testsuite/cargo_remove/remove-package.in/dep-a/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "dep-a" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/remove-package.in/dep-a/src/lib.rs b/tests/testsuite/cargo_remove/remove-package.in/dep-a/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/remove-package.in/dep-a/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/remove-package.in/dep-b/Cargo.toml b/tests/testsuite/cargo_remove/remove-package.in/dep-b/Cargo.toml new file mode 100644 index 0000000..37d2d3d --- /dev/null +++ b/tests/testsuite/cargo_remove/remove-package.in/dep-b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "dep-b" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/remove-package.in/dep-b/src/lib.rs b/tests/testsuite/cargo_remove/remove-package.in/dep-b/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/remove-package.in/dep-b/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/remove-target.in/Cargo.toml b/tests/testsuite/cargo_remove/remove-target.in/Cargo.toml new file mode 100644 index 0000000..14747c7 --- /dev/null +++ b/tests/testsuite/cargo_remove/remove-target.in/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "cargo-remove-target-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[target.x86_64-unknown-freebsd.build-dependencies] +semver = "0.1.0" + +[target.x86_64-unknown-linux-gnu.build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[target.x86_64-unknown-linux-gnu.dependencies] +dbus = "0.6.2" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[target.x86_64-unknown-linux-gnu.dev-dependencies] +ncurses = "20.0" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/remove_basic/in b/tests/testsuite/cargo_remove/remove_basic/in new file mode 120000 index 0000000..7fd0ba5 --- /dev/null +++ b/tests/testsuite/cargo_remove/remove_basic/in @@ -0,0 +1 @@ +../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/remove_basic/mod.rs b/tests/testsuite/cargo_remove/remove_basic/mod.rs new file mode 100644 index 0000000..53381e6 --- /dev/null +++ b/tests/testsuite/cargo_remove/remove_basic/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["docopt"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/remove_basic/out/Cargo.toml b/tests/testsuite/cargo_remove/remove_basic/out/Cargo.toml new file mode 100644 index 0000000..b8628ee --- /dev/null +++ b/tests/testsuite/cargo_remove/remove_basic/out/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/remove_basic/stderr.log b/tests/testsuite/cargo_remove/remove_basic/stderr.log new file mode 100644 index 0000000..231026f --- /dev/null +++ b/tests/testsuite/cargo_remove/remove_basic/stderr.log @@ -0,0 +1,2 @@ + Removing docopt from dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/remove_basic/stdout.log b/tests/testsuite/cargo_remove/remove_basic/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/target/in b/tests/testsuite/cargo_remove/target/in new file mode 120000 index 0000000..d5742d0 --- /dev/null +++ b/tests/testsuite/cargo_remove/target/in @@ -0,0 +1 @@ +../remove-target.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/target/mod.rs b/tests/testsuite/cargo_remove/target/mod.rs new file mode 100644 index 0000000..1447c75 --- /dev/null +++ b/tests/testsuite/cargo_remove/target/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--target", "x86_64-unknown-linux-gnu", "dbus"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/target/out/Cargo.toml b/tests/testsuite/cargo_remove/target/out/Cargo.toml new file mode 100644 index 0000000..e29fbbd --- /dev/null +++ b/tests/testsuite/cargo_remove/target/out/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "cargo-remove-target-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[target.x86_64-unknown-freebsd.build-dependencies] +semver = "0.1.0" + +[target.x86_64-unknown-linux-gnu.build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[target.x86_64-unknown-linux-gnu.dev-dependencies] +ncurses = "20.0" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/target/stderr.log b/tests/testsuite/cargo_remove/target/stderr.log new file mode 100644 index 0000000..810abd9 --- /dev/null +++ b/tests/testsuite/cargo_remove/target/stderr.log @@ -0,0 +1,2 @@ + Removing dbus from dependencies for target `x86_64-unknown-linux-gnu` + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/target/stdout.log b/tests/testsuite/cargo_remove/target/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/target_build/in b/tests/testsuite/cargo_remove/target_build/in new file mode 120000 index 0000000..d5742d0 --- /dev/null +++ b/tests/testsuite/cargo_remove/target_build/in @@ -0,0 +1 @@ +../remove-target.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/target_build/mod.rs b/tests/testsuite/cargo_remove/target_build/mod.rs new file mode 100644 index 0000000..11afbbf --- /dev/null +++ b/tests/testsuite/cargo_remove/target_build/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--build", "--target", "x86_64-unknown-linux-gnu", "semver"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/target_build/out/Cargo.toml b/tests/testsuite/cargo_remove/target_build/out/Cargo.toml new file mode 100644 index 0000000..7353c7a --- /dev/null +++ b/tests/testsuite/cargo_remove/target_build/out/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "cargo-remove-target-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[target.x86_64-unknown-freebsd.build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[target.x86_64-unknown-linux-gnu.dependencies] +dbus = "0.6.2" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[target.x86_64-unknown-linux-gnu.dev-dependencies] +ncurses = "20.0" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/target_build/stderr.log b/tests/testsuite/cargo_remove/target_build/stderr.log new file mode 100644 index 0000000..b06f8f3 --- /dev/null +++ b/tests/testsuite/cargo_remove/target_build/stderr.log @@ -0,0 +1,2 @@ + Removing semver from build-dependencies for target `x86_64-unknown-linux-gnu` + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/target_build/stdout.log b/tests/testsuite/cargo_remove/target_build/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/target_dev/in b/tests/testsuite/cargo_remove/target_dev/in new file mode 120000 index 0000000..d5742d0 --- /dev/null +++ b/tests/testsuite/cargo_remove/target_dev/in @@ -0,0 +1 @@ +../remove-target.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/target_dev/mod.rs b/tests/testsuite/cargo_remove/target_dev/mod.rs new file mode 100644 index 0000000..d303c2b --- /dev/null +++ b/tests/testsuite/cargo_remove/target_dev/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--dev", "--target", "x86_64-unknown-linux-gnu", "ncurses"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/target_dev/out/Cargo.toml b/tests/testsuite/cargo_remove/target_dev/out/Cargo.toml new file mode 100644 index 0000000..a477b3d --- /dev/null +++ b/tests/testsuite/cargo_remove/target_dev/out/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "cargo-remove-target-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[target.x86_64-unknown-freebsd.build-dependencies] +semver = "0.1.0" + +[target.x86_64-unknown-linux-gnu.build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[target.x86_64-unknown-linux-gnu.dependencies] +dbus = "0.6.2" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/target_dev/stderr.log b/tests/testsuite/cargo_remove/target_dev/stderr.log new file mode 100644 index 0000000..68553a3 --- /dev/null +++ b/tests/testsuite/cargo_remove/target_dev/stderr.log @@ -0,0 +1,2 @@ + Removing ncurses from dev-dependencies for target `x86_64-unknown-linux-gnu` + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/target_dev/stdout.log b/tests/testsuite/cargo_remove/target_dev/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.lock b/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.lock new file mode 100644 index 0000000..06c2052 --- /dev/null +++ b/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.lock @@ -0,0 +1,58 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cargo-remove-test-fixture" +version = "0.1.0" +dependencies = [ + "clippy", + "docopt", + "regex", + "rustc-serialize", + "semver", + "serde", + "toml", +] + +[[package]] +name = "clippy" +version = "0.4.1+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ced0eda54e9ddc6063f0e1d0164493cd16c84c6b6a0329a536967c44e205f7" + +[[package]] +name = "docopt" +version = "0.6.2+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b600540c4fafb27bf6e6961f0f1e6f547c9d6126ce581ab3a92f878c8e2c9a2c" + +[[package]] +name = "regex" +version = "0.1.1+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84949cb53285a6c481d0133065a7b669871acfd9e20f273f4ce1283c309775d5" + +[[package]] +name = "rustc-serialize" +version = "0.4.1+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31162e7d23a085553c42dee375787b451a481275473f7779c4a63bcc267a24fd" + +[[package]] +name = "semver" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3031434e07edc922bf1b8262f075fac1522694f17b1ee7ad314c4cabd5d2723f" + +[[package]] +name = "serde" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75d9264696ebbf5315a6b068e9910c4df9274365afac2d88abf66525df660218" + +[[package]] +name = "toml" +version = "0.1.1+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f6c7804525ce0a968ef270e55a516cf4bdcf1fea0b09d130e0aa34a66745b3" diff --git a/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.toml b/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.toml new file mode 100644 index 0000000..340f06c --- /dev/null +++ b/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/update_lock_file/in/src/main.rs b/tests/testsuite/cargo_remove/update_lock_file/in/src/main.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/testsuite/cargo_remove/update_lock_file/in/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/cargo_remove/update_lock_file/mod.rs b/tests/testsuite/cargo_remove/update_lock_file/mod.rs new file mode 100644 index 0000000..be5bc87 --- /dev/null +++ b/tests/testsuite/cargo_remove/update_lock_file/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["rustc-serialize"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.lock b/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.lock new file mode 100644 index 0000000..bd8c90f --- /dev/null +++ b/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.lock @@ -0,0 +1,51 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cargo-remove-test-fixture" +version = "0.1.0" +dependencies = [ + "clippy", + "docopt", + "regex", + "semver", + "serde", + "toml", +] + +[[package]] +name = "clippy" +version = "0.4.1+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ced0eda54e9ddc6063f0e1d0164493cd16c84c6b6a0329a536967c44e205f7" + +[[package]] +name = "docopt" +version = "0.6.2+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b600540c4fafb27bf6e6961f0f1e6f547c9d6126ce581ab3a92f878c8e2c9a2c" + +[[package]] +name = "regex" +version = "0.1.1+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84949cb53285a6c481d0133065a7b669871acfd9e20f273f4ce1283c309775d5" + +[[package]] +name = "semver" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3031434e07edc922bf1b8262f075fac1522694f17b1ee7ad314c4cabd5d2723f" + +[[package]] +name = "serde" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75d9264696ebbf5315a6b068e9910c4df9274365afac2d88abf66525df660218" + +[[package]] +name = "toml" +version = "0.1.1+my-package" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f6c7804525ce0a968ef270e55a516cf4bdcf1fea0b09d130e0aa34a66745b3" diff --git a/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.toml b/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.toml new file mode 100644 index 0000000..5e7d7f0 --- /dev/null +++ b/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/update_lock_file/out/src/main.rs b/tests/testsuite/cargo_remove/update_lock_file/out/src/main.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/testsuite/cargo_remove/update_lock_file/out/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/cargo_remove/update_lock_file/stderr.log b/tests/testsuite/cargo_remove/update_lock_file/stderr.log new file mode 100644 index 0000000..164f8f4 --- /dev/null +++ b/tests/testsuite/cargo_remove/update_lock_file/stderr.log @@ -0,0 +1,2 @@ + Removing rustc-serialize from dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/update_lock_file/stdout.log b/tests/testsuite/cargo_remove/update_lock_file/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/workspace/in/Cargo.toml b/tests/testsuite/cargo_remove/workspace/in/Cargo.toml new file mode 100644 index 0000000..fd5e80a --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ "my-package" ] + +[workspace.dependencies] +semver = "0.1.0" diff --git a/tests/testsuite/cargo_remove/workspace/in/my-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace/in/my-package/Cargo.toml new file mode 100644 index 0000000..6690d59 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/in/my-package/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = { workspace = true } + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace/in/my-package/src/main.rs b/tests/testsuite/cargo_remove/workspace/in/my-package/src/main.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/in/my-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace/mod.rs b/tests/testsuite/cargo_remove/workspace/mod.rs new file mode 100644 index 0000000..225fbec --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--package", "my-package", "--build", "semver"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/workspace/out/Cargo.toml b/tests/testsuite/cargo_remove/workspace/out/Cargo.toml new file mode 100644 index 0000000..83a6a04 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/out/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = [ "my-package" ] diff --git a/tests/testsuite/cargo_remove/workspace/out/my-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace/out/my-package/Cargo.toml new file mode 100644 index 0000000..4027805 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/out/my-package/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace/out/my-package/src/main.rs b/tests/testsuite/cargo_remove/workspace/out/my-package/src/main.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/out/my-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace/stderr.log b/tests/testsuite/cargo_remove/workspace/stderr.log new file mode 100644 index 0000000..f037ebe --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/stderr.log @@ -0,0 +1,2 @@ + Removing semver from build-dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/workspace/stdout.log b/tests/testsuite/cargo_remove/workspace/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/in/Cargo.toml b/tests/testsuite/cargo_remove/workspace_non_virtual/in/Cargo.toml new file mode 100644 index 0000000..dbac8ab --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/in/Cargo.toml @@ -0,0 +1,30 @@ +[workspace] +members = [ "my-member" ] + +[workspace.dependencies] +semver = "0.1.0" + +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = { workspace = true } + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/Cargo.toml b/tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/Cargo.toml new file mode 100644 index 0000000..bb78904 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "my-member" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/src/main.rs b/tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/src/main.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs b/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs new file mode 100644 index 0000000..225fbec --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--package", "my-package", "--build", "semver"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/out/Cargo.toml b/tests/testsuite/cargo_remove/workspace_non_virtual/out/Cargo.toml new file mode 100644 index 0000000..9a32614 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/out/Cargo.toml @@ -0,0 +1,24 @@ +[workspace] +members = [ "my-member" ] + +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/Cargo.toml b/tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/Cargo.toml new file mode 100644 index 0000000..bb78904 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "my-member" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/src/main.rs b/tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/src/main.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log b/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log new file mode 100644 index 0000000..f037ebe --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log @@ -0,0 +1,2 @@ + Removing semver from build-dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/stdout.log b/tests/testsuite/cargo_remove/workspace_non_virtual/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_remove/workspace_preserved/in/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/in/Cargo.toml new file mode 100644 index 0000000..f1992ac --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ "my-package", "my-other-package" ] + +[workspace.dependencies] +semver = "0.1.0" diff --git a/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/Cargo.toml new file mode 100644 index 0000000..d659728 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "my-other-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +semver = { workspace = true } +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/src/main.rs b/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/src/main.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/Cargo.toml new file mode 100644 index 0000000..6690d59 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = { workspace = true } + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/src/main.rs b/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/src/main.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace_preserved/mod.rs b/tests/testsuite/cargo_remove/workspace_preserved/mod.rs new file mode 100644 index 0000000..225fbec --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::Project; + +use crate::cargo_remove::init_registry; + +#[cargo_test] +fn case() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--package", "my-package", "--build", "semver"]) + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/workspace_preserved/out/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/out/Cargo.toml new file mode 100644 index 0000000..f1992ac --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ "my-package", "my-other-package" ] + +[workspace.dependencies] +semver = "0.1.0" diff --git a/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/Cargo.toml new file mode 100644 index 0000000..d659728 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "my-other-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +semver = { workspace = true } +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/src/main.rs b/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/src/main.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/Cargo.toml new file mode 100644 index 0000000..4027805 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/src/main.rs b/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/src/main.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace_preserved/stderr.log b/tests/testsuite/cargo_remove/workspace_preserved/stderr.log new file mode 100644 index 0000000..f037ebe --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/stderr.log @@ -0,0 +1,2 @@ + Removing semver from build-dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/workspace_preserved/stdout.log b/tests/testsuite/cargo_remove/workspace_preserved/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/cargo_targets.rs b/tests/testsuite/cargo_targets.rs new file mode 100644 index 0000000..fcf2930 --- /dev/null +++ b/tests/testsuite/cargo_targets.rs @@ -0,0 +1,68 @@ +//! Tests specifically related to target handling (lib, bins, examples, tests, benches). + +use cargo_test_support::project; + +#[cargo_test] +fn warn_unmatched_target_filters() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [lib] + test = false + bench = false + "#, + ) + .file("src/lib.rs", r#"fn main() {}"#) + .build(); + + p.cargo("check --tests --bins --examples --benches") + .with_stderr( + "\ +[WARNING] Target filters `bins`, `tests`, `examples`, `benches` specified, \ +but no targets matched. This is a no-op +[FINISHED][..] +", + ) + .run(); +} + +#[cargo_test] +fn reserved_windows_target_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [[bin]] + name = "con" + path = "src/main.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + if cfg!(windows) { + p.cargo("check") + .with_stderr( + "\ +[WARNING] binary target `con` is a reserved Windows filename, \ +this target will not work on Windows platforms +[CHECKING] foo[..] +[FINISHED][..] +", + ) + .run(); + } else { + p.cargo("check") + .with_stderr("[CHECKING] foo[..]\n[FINISHED][..]") + .run(); + } +} diff --git a/tests/testsuite/cfg.rs b/tests/testsuite/cfg.rs new file mode 100644 index 0000000..dcce654 --- /dev/null +++ b/tests/testsuite/cfg.rs @@ -0,0 +1,515 @@ +//! Tests for cfg() expressions. + +use cargo_test_support::registry::Package; +use cargo_test_support::rustc_host; +use cargo_test_support::{basic_manifest, project}; + +#[cargo_test] +fn cfg_easy() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [target.'cfg(unix)'.dependencies] + b = { path = 'b' } + [target."cfg(windows)".dependencies] + b = { path = 'b' } + "#, + ) + .file("src/lib.rs", "extern crate b;") + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "") + .build(); + p.cargo("check -v").run(); +} + +#[cargo_test] +fn dont_include() { + let other_family = if cfg!(unix) { "windows" } else { "unix" }; + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [target.'cfg({})'.dependencies] + b = {{ path = 'b' }} + "#, + other_family + ), + ) + .file("src/lib.rs", "") + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "") + .build(); + p.cargo("check") + .with_stderr( + "\ +[CHECKING] a v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn works_through_the_registry() { + Package::new("baz", "0.1.0").publish(); + Package::new("bar", "0.1.0") + .target_dep("baz", "0.1.0", "cfg(unix)") + .target_dep("baz", "0.1.0", "cfg(windows)") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file( + "src/lib.rs", + "#[allow(unused_extern_crates)] extern crate bar;", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] index +[DOWNLOADING] crates ... +[DOWNLOADED] [..] +[DOWNLOADED] [..] +[CHECKING] baz v0.1.0 +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn ignore_version_from_other_platform() { + let this_family = if cfg!(unix) { "unix" } else { "windows" }; + let other_family = if cfg!(unix) { "windows" } else { "unix" }; + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.2.0").publish(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [target.'cfg({})'.dependencies] + bar = "0.1.0" + + [target.'cfg({})'.dependencies] + bar = "0.2.0" + "#, + this_family, other_family + ), + ) + .file( + "src/lib.rs", + "#[allow(unused_extern_crates)] extern crate bar;", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] index +[DOWNLOADING] crates ... +[DOWNLOADED] [..] +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn bad_target_spec() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [target.'cfg(4)'.dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + failed to parse `4` as a cfg expression: unexpected character `4` in cfg, [..] +", + ) + .run(); +} + +#[cargo_test] +fn bad_target_spec2() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [target.'cfg(bar =)'.dependencies] + baz = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + failed to parse `bar =` as a cfg expression: expected a string, but cfg expression ended +", + ) + .run(); +} + +#[cargo_test] +fn multiple_match_ok() { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [target.'cfg(unix)'.dependencies] + b = {{ path = 'b' }} + [target.'cfg(target_family = "unix")'.dependencies] + b = {{ path = 'b' }} + [target."cfg(windows)".dependencies] + b = {{ path = 'b' }} + [target.'cfg(target_family = "windows")'.dependencies] + b = {{ path = 'b' }} + [target."cfg(any(windows, unix))".dependencies] + b = {{ path = 'b' }} + + [target.{}.dependencies] + b = {{ path = 'b' }} + "#, + rustc_host() + ), + ) + .file("src/lib.rs", "extern crate b;") + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "") + .build(); + p.cargo("check -v").run(); +} + +#[cargo_test] +fn any_ok() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [target."cfg(any(windows, unix))".dependencies] + b = { path = 'b' } + "#, + ) + .file("src/lib.rs", "extern crate b;") + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "") + .build(); + p.cargo("check -v").run(); +} + +// https://github.com/rust-lang/cargo/issues/5313 +#[cargo_test] +#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] +fn cfg_looks_at_rustflags_for_target() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [target.'cfg(with_b)'.dependencies] + b = { path = 'b' } + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(with_b)] + extern crate b; + + fn main() { b::foo(); } + "#, + ) + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("check --target x86_64-unknown-linux-gnu") + .env("RUSTFLAGS", "--cfg with_b") + .run(); +} + +#[cargo_test] +fn bad_cfg_discovery() { + // Check error messages when `rustc -v` and `rustc --print=*` parsing fails. + // + // This is a `rustc` replacement which behaves differently based on an + // environment variable. + let p = project() + .at("compiler") + .file("Cargo.toml", &basic_manifest("compiler", "0.1.0")) + .file( + "src/main.rs", + r#" + fn run_rustc() -> String { + let mut cmd = std::process::Command::new("rustc"); + for arg in std::env::args_os().skip(1) { + cmd.arg(arg); + } + String::from_utf8(cmd.output().unwrap().stdout).unwrap() + } + + fn main() { + let mode = std::env::var("FUNKY_MODE").unwrap(); + if mode == "bad-version" { + println!("foo"); + return; + } + if std::env::args_os().any(|a| a == "-vV") { + print!("{}", run_rustc()); + return; + } + if mode == "no-crate-types" { + return; + } + if mode == "bad-crate-type" { + println!("foo"); + return; + } + let output = run_rustc(); + let mut lines = output.lines(); + let sysroot = loop { + let line = lines.next().unwrap(); + if line.contains("___") { + println!("{}", line); + } else { + break line; + } + }; + if mode == "no-sysroot" { + return; + } + println!("{}", sysroot); + + if mode == "no-split-debuginfo" { + return; + } + loop { + let line = lines.next().unwrap(); + if line == "___" { + println!("\n{line}"); + break; + } else { + // As the number split-debuginfo options varies, + // concat them into one line. + print!("{line},"); + } + }; + + if mode != "bad-cfg" { + panic!("unexpected"); + } + println!("123"); + } + "#, + ) + .build(); + p.cargo("build").run(); + let funky_rustc = p.bin("compiler"); + + let p = project().file("src/lib.rs", "").build(); + + p.cargo("check") + .env("RUSTC", &funky_rustc) + .env("FUNKY_MODE", "bad-version") + .with_status(101) + .with_stderr( + "\ +[ERROR] `rustc -vV` didn't have a line for `host:`, got: +foo + +", + ) + .run(); + + p.cargo("check") + .env("RUSTC", &funky_rustc) + .env("FUNKY_MODE", "no-crate-types") + .with_status(101) + .with_stderr( + "\ +[ERROR] malformed output when learning about crate-type bin information +command was: `[..]compiler[..] --crate-name ___ [..]` +(no output received) +", + ) + .run(); + + p.cargo("check") + .env("RUSTC", &funky_rustc) + .env("FUNKY_MODE", "no-sysroot") + .with_status(101) + .with_stderr( + "\ +[ERROR] output of --print=sysroot missing when learning about target-specific information from rustc +command was: `[..]compiler[..]--crate-type [..]` + +--- stdout +[..]___[..] +[..]___[..] +[..]___[..] +[..]___[..] +[..]___[..] +[..]___[..] + +", + ) + .run(); + + p.cargo("check") + .env("RUSTC", &funky_rustc) + .env("FUNKY_MODE", "no-split-debuginfo") + .with_status(101) + .with_stderr( + "\ +[ERROR] output of --print=split-debuginfo missing when learning about target-specific information from rustc +command was: `[..]compiler[..]--crate-type [..]` + +--- stdout +[..]___[..] +[..]___[..] +[..]___[..] +[..]___[..] +[..]___[..] +[..]___[..] +[..] + +", + ) + .run(); + + p.cargo("check") + .env("RUSTC", &funky_rustc) + .env("FUNKY_MODE", "bad-cfg") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse the cfg from `rustc --print=cfg`, got: +[..]___[..] +[..]___[..] +[..]___[..] +[..]___[..] +[..]___[..] +[..]___[..] +[..] +[..],[..] +___ +123 + + +Caused by: + failed to parse `123` as a cfg expression: unexpected character `1` in cfg, \ + expected parens, a comma, an identifier, or a string +", + ) + .run(); +} + +#[cargo_test] +fn exclusive_dep_kinds() { + // Checks for a bug where the same package with different cfg expressions + // was not being filtered correctly. + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [target.'cfg(abc)'.dependencies] + bar = "1.0" + + [target.'cfg(not(abc))'.build-dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "extern crate bar; fn main() {}") + .build(); + + p.cargo("check").run(); + p.change_file("src/lib.rs", "extern crate bar;"); + p.cargo("check") + .with_status(101) + // can't find crate for `bar` + .with_stderr_contains("[..]E0463[..]") + .run(); +} diff --git a/tests/testsuite/check.rs b/tests/testsuite/check.rs new file mode 100644 index 0000000..9582480 --- /dev/null +++ b/tests/testsuite/check.rs @@ -0,0 +1,1521 @@ +//! Tests for the `cargo check` command. + +use std::fmt::{self, Write}; + +use crate::messages::raw_rustc_output; +use cargo_test_support::install::exe; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::Package; +use cargo_test_support::tools; +use cargo_test_support::{basic_bin_manifest, basic_manifest, git, project}; + +#[cargo_test] +fn check_success() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file( + "src/main.rs", + "extern crate bar; fn main() { ::bar::baz(); }", + ) + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn baz() {}") + .build(); + + foo.cargo("check").run(); +} + +#[cargo_test] +fn check_fail() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file( + "src/main.rs", + "extern crate bar; fn main() { ::bar::baz(42); }", + ) + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn baz() {}") + .build(); + + foo.cargo("check") + .with_status(101) + .with_stderr_contains("[..]this function takes 0[..]") + .run(); +} + +#[cargo_test] +fn custom_derive() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file( + "src/main.rs", + r#" + #[macro_use] + extern crate bar; + + trait B { + fn b(&self); + } + + #[derive(B)] + struct A; + + fn main() { + let a = A; + a.b(); + } + "#, + ) + .build(); + let _bar = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + [lib] + proc-macro = true + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate proc_macro; + + use proc_macro::TokenStream; + + #[proc_macro_derive(B)] + pub fn derive(_input: TokenStream) -> TokenStream { + format!("impl B for A {{ fn b(&self) {{}} }}").parse().unwrap() + } + "#, + ) + .build(); + + foo.cargo("check").run(); +} + +#[cargo_test] +fn check_build() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file( + "src/main.rs", + "extern crate bar; fn main() { ::bar::baz(); }", + ) + .build(); + + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn baz() {}") + .build(); + + foo.cargo("check").run(); + foo.cargo("build").run(); +} + +#[cargo_test] +fn build_check() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file( + "src/main.rs", + "extern crate bar; fn main() { ::bar::baz(); }", + ) + .build(); + + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn baz() {}") + .build(); + + foo.cargo("build -v").run(); + foo.cargo("check -v").run(); +} + +// Checks that where a project has both a lib and a bin, the lib is only checked +// not built. +#[cargo_test] +fn issue_3418() { + let foo = project() + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .build(); + + foo.cargo("check -v") + .with_stderr_contains("[..] --emit=[..]metadata [..]") + .run(); +} + +// Some weirdness that seems to be caused by a crate being built as well as +// checked, but in this case with a proc macro too. +#[cargo_test] +fn issue_3419() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + rustc-serialize = "*" + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate rustc_serialize; + + use rustc_serialize::Decodable; + + pub fn take() {} + "#, + ) + .file( + "src/main.rs", + r#" + extern crate rustc_serialize; + + extern crate foo; + + #[derive(RustcDecodable)] + pub struct Foo; + + fn main() { + foo::take::(); + } + "#, + ) + .build(); + + Package::new("rustc-serialize", "1.0.0") + .file( + "src/lib.rs", + r#" + pub trait Decodable: Sized { + fn decode(d: &mut D) -> Result; + } + pub trait Decoder { + type Error; + fn read_struct(&mut self, s_name: &str, len: usize, f: F) + -> Result + where F: FnOnce(&mut Self) -> Result; + } + "#, + ) + .publish(); + + p.cargo("check").run(); +} + +// Check on a dylib should have a different metadata hash than build. +#[cargo_test] +fn dylib_check_preserves_build_cache() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [lib] + crate-type = ["dylib"] + + [dependencies] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_stderr( + "\ +[..]Compiling foo v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("check").run(); + + p.cargo("build") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); +} + +// test `cargo rustc --profile check` +#[cargo_test] +fn rustc_check() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file( + "src/main.rs", + "extern crate bar; fn main() { ::bar::baz(); }", + ) + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn baz() {}") + .build(); + + foo.cargo("rustc --profile check -- --emit=metadata").run(); + + // Verify compatible usage of --profile with --release, issue #7488 + foo.cargo("rustc --profile check --release -- --emit=metadata") + .run(); + foo.cargo("rustc --profile test --release -- --emit=metadata") + .run(); +} + +#[cargo_test] +fn rustc_check_err() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file( + "src/main.rs", + "extern crate bar; fn main() { ::bar::qux(); }", + ) + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn baz() {}") + .build(); + + foo.cargo("rustc --profile check -- --emit=metadata") + .with_status(101) + .with_stderr_contains("[CHECKING] bar [..]") + .with_stderr_contains("[CHECKING] foo [..]") + .with_stderr_contains("[..]cannot find function `qux` in [..] `bar`") + .run(); +} + +#[cargo_test] +fn check_all() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [workspace] + [dependencies] + b = { path = "b" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("examples/a.rs", "fn main() {}") + .file("tests/a.rs", "") + .file("src/lib.rs", "") + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/main.rs", "fn main() {}") + .file("b/src/lib.rs", "") + .build(); + + p.cargo("check --workspace -v") + .with_stderr_contains("[..] --crate-name foo src/lib.rs [..]") + .with_stderr_contains("[..] --crate-name foo src/main.rs [..]") + .with_stderr_contains("[..] --crate-name b b/src/lib.rs [..]") + .with_stderr_contains("[..] --crate-name b b/src/main.rs [..]") + .run(); +} + +#[cargo_test] +fn check_all_exclude() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") + .build(); + + p.cargo("check --workspace --exclude baz") + .with_stderr_does_not_contain("[CHECKING] baz v0.1.0 [..]") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn check_all_exclude_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") + .build(); + + p.cargo("check --workspace --exclude '*z'") + .with_stderr_does_not_contain("[CHECKING] baz v0.1.0 [..]") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn check_virtual_all_implied() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check -v") + .with_stderr_contains("[..] --crate-name bar bar/src/lib.rs [..]") + .with_stderr_contains("[..] --crate-name baz baz/src/lib.rs [..]") + .run(); +} + +#[cargo_test] +fn check_virtual_manifest_one_project() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") + .build(); + + p.cargo("check -p bar") + .with_stderr_does_not_contain("[CHECKING] baz v0.1.0 [..]") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn check_virtual_manifest_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check -p '*z'") + .with_stderr_does_not_contain("[CHECKING] bar v0.1.0 [..]") + .with_stderr( + "\ +[CHECKING] baz v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn exclude_warns_on_non_existing_package() { + let p = project().file("src/lib.rs", "").build(); + p.cargo("check --workspace --exclude bar") + .with_stdout("") + .with_stderr( + "\ +[WARNING] excluded package(s) `bar` not found in workspace `[CWD]` +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn targets_selected_default() { + let foo = project() + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", "pub fn smth() {}") + .file("examples/example1.rs", "fn main() {}") + .file("tests/test2.rs", "#[test] fn t() {}") + .file("benches/bench3.rs", "") + .build(); + + foo.cargo("check -v") + .with_stderr_contains("[..] --crate-name foo src/lib.rs [..]") + .with_stderr_contains("[..] --crate-name foo src/main.rs [..]") + .with_stderr_does_not_contain("[..] --crate-name example1 examples/example1.rs [..]") + .with_stderr_does_not_contain("[..] --crate-name test2 tests/test2.rs [..]") + .with_stderr_does_not_contain("[..] --crate-name bench3 benches/bench3.rs [..]") + .run(); +} + +#[cargo_test] +fn targets_selected_all() { + let foo = project() + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", "pub fn smth() {}") + .file("examples/example1.rs", "fn main() {}") + .file("tests/test2.rs", "#[test] fn t() {}") + .file("benches/bench3.rs", "") + .build(); + + foo.cargo("check --all-targets -v") + .with_stderr_contains("[..] --crate-name foo src/lib.rs [..]") + .with_stderr_contains("[..] --crate-name foo src/main.rs [..]") + .with_stderr_contains("[..] --crate-name example1 examples/example1.rs [..]") + .with_stderr_contains("[..] --crate-name test2 tests/test2.rs [..]") + .with_stderr_contains("[..] --crate-name bench3 benches/bench3.rs [..]") + .run(); +} + +#[cargo_test] +fn check_unit_test_profile() { + let foo = project() + .file( + "src/lib.rs", + r#" + #[cfg(test)] + mod tests { + #[test] + fn it_works() { + badtext + } + } + "#, + ) + .build(); + + foo.cargo("check").run(); + foo.cargo("check --profile test") + .with_status(101) + .with_stderr_contains("[..]badtext[..]") + .run(); +} + +// Verify what is checked with various command-line filters. +#[cargo_test] +fn check_filters() { + let p = project() + .file( + "src/lib.rs", + r#" + fn unused_normal_lib() {} + #[cfg(test)] + mod tests { + fn unused_unit_lib() {} + } + "#, + ) + .file( + "src/main.rs", + r#" + fn main() {} + fn unused_normal_bin() {} + #[cfg(test)] + mod tests { + fn unused_unit_bin() {} + } + "#, + ) + .file( + "tests/t1.rs", + r#" + fn unused_normal_t1() {} + #[cfg(test)] + mod tests { + fn unused_unit_t1() {} + } + "#, + ) + .file( + "examples/ex1.rs", + r#" + fn main() {} + fn unused_normal_ex1() {} + #[cfg(test)] + mod tests { + fn unused_unit_ex1() {} + } + "#, + ) + .file( + "benches/b1.rs", + r#" + fn unused_normal_b1() {} + #[cfg(test)] + mod tests { + fn unused_unit_b1() {} + } + "#, + ) + .build(); + + p.cargo("check") + .with_stderr_contains("[..]unused_normal_lib[..]") + .with_stderr_contains("[..]unused_normal_bin[..]") + .with_stderr_does_not_contain("[..]unused_normal_t1[..]") + .with_stderr_does_not_contain("[..]unused_normal_ex1[..]") + .with_stderr_does_not_contain("[..]unused_normal_b1[..]") + .with_stderr_does_not_contain("[..]unused_unit_[..]") + .run(); + p.root().join("target").rm_rf(); + p.cargo("check --tests -v") + .with_stderr_contains("[..] --crate-name foo src/lib.rs [..] --test [..]") + .with_stderr_contains("[..] --crate-name foo src/lib.rs [..] --crate-type lib [..]") + .with_stderr_contains("[..] --crate-name foo src/main.rs [..] --test [..]") + .with_stderr_contains("[..]unused_unit_lib[..]") + .with_stderr_contains("[..]unused_unit_bin[..]") + .with_stderr_contains("[..]unused_normal_lib[..]") + .with_stderr_contains("[..]unused_normal_bin[..]") + .with_stderr_contains("[..]unused_unit_t1[..]") + .with_stderr_does_not_contain("[..]unused_normal_ex1[..]") + .with_stderr_does_not_contain("[..]unused_unit_ex1[..]") + .with_stderr_does_not_contain("[..]unused_normal_b1[..]") + .with_stderr_does_not_contain("[..]unused_unit_b1[..]") + .with_stderr_does_not_contain("[..]--crate-type bin[..]") + .run(); + p.root().join("target").rm_rf(); + p.cargo("check --test t1 -v") + .with_stderr_contains("[..]unused_normal_lib[..]") + .with_stderr_contains("[..]unused_unit_t1[..]") + .with_stderr_does_not_contain("[..]unused_unit_lib[..]") + .with_stderr_does_not_contain("[..]unused_normal_bin[..]") + .with_stderr_does_not_contain("[..]unused_unit_bin[..]") + .with_stderr_does_not_contain("[..]unused_normal_ex1[..]") + .with_stderr_does_not_contain("[..]unused_normal_b1[..]") + .with_stderr_does_not_contain("[..]unused_unit_ex1[..]") + .with_stderr_does_not_contain("[..]unused_unit_b1[..]") + .run(); + p.root().join("target").rm_rf(); + p.cargo("check --all-targets -v") + .with_stderr_contains("[..]unused_normal_lib[..]") + .with_stderr_contains("[..]unused_normal_bin[..]") + .with_stderr_contains("[..]unused_normal_t1[..]") + .with_stderr_contains("[..]unused_normal_ex1[..]") + .with_stderr_contains("[..]unused_normal_b1[..]") + .with_stderr_contains("[..]unused_unit_b1[..]") + .with_stderr_contains("[..]unused_unit_t1[..]") + .with_stderr_contains("[..]unused_unit_lib[..]") + .with_stderr_contains("[..]unused_unit_bin[..]") + .with_stderr_does_not_contain("[..]unused_unit_ex1[..]") + .run(); +} + +#[cargo_test] +fn check_artifacts() { + // Verify which artifacts are created when running check (#4059). + let p = project() + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .file("tests/t1.rs", "") + .file("examples/ex1.rs", "fn main() {}") + .file("benches/b1.rs", "") + .build(); + + p.cargo("check").run(); + assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); + assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); + assert!(!p.root().join("target/debug").join(exe("foo")).is_file()); + assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 2); + + p.root().join("target").rm_rf(); + p.cargo("check --lib").run(); + assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); + assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); + assert!(!p.root().join("target/debug").join(exe("foo")).is_file()); + assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 1); + + p.root().join("target").rm_rf(); + p.cargo("check --bin foo").run(); + assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); + assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); + assert!(!p.root().join("target/debug").join(exe("foo")).is_file()); + assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 2); + + p.root().join("target").rm_rf(); + p.cargo("check --test t1").run(); + assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); + assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); + assert!(!p.root().join("target/debug").join(exe("foo")).is_file()); + assert_eq!(p.glob("target/debug/t1-*").count(), 0); + assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 1); + assert_eq!(p.glob("target/debug/deps/libt1-*.rmeta").count(), 1); + + p.root().join("target").rm_rf(); + p.cargo("check --example ex1").run(); + assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); + assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); + assert!(!p + .root() + .join("target/debug/examples") + .join(exe("ex1")) + .is_file()); + assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 1); + assert_eq!(p.glob("target/debug/examples/libex1-*.rmeta").count(), 1); + + p.root().join("target").rm_rf(); + p.cargo("check --bench b1").run(); + assert!(!p.root().join("target/debug/libfoo.rmeta").is_file()); + assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); + assert!(!p.root().join("target/debug").join(exe("foo")).is_file()); + assert_eq!(p.glob("target/debug/b1-*").count(), 0); + assert_eq!(p.glob("target/debug/deps/libfoo-*.rmeta").count(), 1); + assert_eq!(p.glob("target/debug/deps/libb1-*.rmeta").count(), 1); +} + +#[cargo_test] +fn short_message_format() { + let foo = project() + .file("src/lib.rs", "fn foo() { let _x: bool = 'a'; }") + .build(); + foo.cargo("check --message-format=short") + .with_status(101) + .with_stderr_contains( + "\ +src/lib.rs:1:27: error[E0308]: mismatched types +error: could not compile `foo` due to previous error +", + ) + .run(); +} + +#[cargo_test] +fn proc_macro() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "demo" + version = "0.0.1" + + [lib] + proc-macro = true + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate proc_macro; + + use proc_macro::TokenStream; + + #[proc_macro_derive(Foo)] + pub fn demo(_input: TokenStream) -> TokenStream { + "".parse().unwrap() + } + "#, + ) + .file( + "src/main.rs", + r#" + #[macro_use] + extern crate demo; + + #[derive(Foo)] + struct A; + + fn main() {} + "#, + ) + .build(); + p.cargo("check -v").env("CARGO_LOG", "cargo=trace").run(); +} + +#[cargo_test] +fn check_keep_going() { + let foo = project() + .file("src/bin/one.rs", "compile_error!(\"ONE\"); fn main() {}") + .file("src/bin/two.rs", "compile_error!(\"TWO\"); fn main() {}") + .build(); + + // Due to -j1, without --keep-going only one of the two bins would be built. + foo.cargo("check -j1 --keep-going -Zunstable-options") + .masquerade_as_nightly_cargo(&["keep-going"]) + .with_status(101) + .with_stderr_contains("error: ONE") + .with_stderr_contains("error: TWO") + .run(); +} + +#[cargo_test] +fn does_not_use_empty_rustc_wrapper() { + // An empty RUSTC_WRAPPER environment variable won't be used. + // The env var will also override the config, essentially unsetting it. + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config.toml", + r#" + [build] + rustc-wrapper = "do-not-execute-me" + "#, + ) + .build(); + p.cargo("check").env("RUSTC_WRAPPER", "").run(); +} + +#[cargo_test] +fn does_not_use_empty_rustc_workspace_wrapper() { + let p = project().file("src/lib.rs", "").build(); + p.cargo("check").env("RUSTC_WORKSPACE_WRAPPER", "").run(); +} + +#[cargo_test] +fn error_from_deep_recursion() -> Result<(), fmt::Error> { + let mut big_macro = String::new(); + writeln!(big_macro, "macro_rules! m {{")?; + for i in 0..130 { + writeln!(big_macro, "({}) => {{ m!({}); }};", i, i + 1)?; + } + writeln!(big_macro, "}}")?; + writeln!(big_macro, "m!(0);")?; + + let p = project().file("src/lib.rs", &big_macro).build(); + p.cargo("check --message-format=json") + .with_status(101) + .with_stdout_contains( + "[..]\"message\":\"recursion limit reached while expanding [..]`m[..]`\"[..]", + ) + .run(); + + Ok(()) +} + +#[cargo_test] +fn rustc_workspace_wrapper_affects_all_workspace_members() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check") + .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) + .with_stderr_contains("WRAPPER CALLED: rustc --crate-name bar [..]") + .with_stderr_contains("WRAPPER CALLED: rustc --crate-name baz [..]") + .run(); +} + +#[cargo_test] +fn rustc_workspace_wrapper_includes_path_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + + [dependencies] + baz = { path = "baz" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check --workspace") + .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) + .with_stderr_contains("WRAPPER CALLED: rustc --crate-name foo [..]") + .with_stderr_contains("WRAPPER CALLED: rustc --crate-name bar [..]") + .with_stderr_contains("WRAPPER CALLED: rustc --crate-name baz [..]") + .run(); +} + +#[cargo_test] +fn rustc_workspace_wrapper_respects_primary_units() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check -p bar") + .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) + .with_stderr_contains("WRAPPER CALLED: rustc --crate-name bar [..]") + .with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name baz [..]") + .run(); +} + +#[cargo_test] +fn rustc_workspace_wrapper_excludes_published_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + + [dependencies] + baz = "1.0.0" + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + Package::new("baz", "1.0.0").publish(); + + p.cargo("check --workspace -v") + .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) + .with_stderr_contains("WRAPPER CALLED: rustc --crate-name foo [..]") + .with_stderr_contains("WRAPPER CALLED: rustc --crate-name bar [..]") + .with_stderr_contains("[CHECKING] baz [..]") + .with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name baz [..]") + .run(); +} + +#[cargo_test] +fn warn_manifest_package_and_project() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [project] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] manifest at `[CWD]` contains both `project` and `package`, this could become a hard error in the future +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn git_manifest_package_and_project() { + let p = project(); + let git_project = git::new("bar", |p| { + p.file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + + [project] + name = "bar" + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "") + }); + + let p = p + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies.bar] + version = "0.0.1" + git = '{}' + + "#, + git_project.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `[..]` +[CHECKING] bar v0.0.1 ([..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn warn_manifest_with_project() { + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] manifest at `[CWD]` contains `[project]` instead of `[package]`, this could become a hard error in the future +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn git_manifest_with_project() { + let p = project(); + let git_project = git::new("bar", |p| { + p.file( + "Cargo.toml", + r#" + [project] + name = "bar" + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "") + }); + + let p = p + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies.bar] + version = "0.0.1" + git = '{}' + + "#, + git_project.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `[..]` +[CHECKING] bar v0.0.1 ([..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn check_fixable_warning() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "use std::io;") + .build(); + + foo.cargo("check") + .with_stderr_contains("[..] (run `cargo fix --lib -p foo` to apply 1 suggestion)") + .run(); +} + +#[cargo_test] +fn check_fixable_test_warning() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file( + "src/lib.rs", + "\ +mod tests { + #[test] + fn t1() { + use std::io; + } +} + ", + ) + .build(); + + foo.cargo("check --all-targets") + .with_stderr_contains("[..] (run `cargo fix --lib -p foo --tests` to apply 1 suggestion)") + .run(); + foo.cargo("fix --lib -p foo --tests --allow-no-vcs").run(); + assert!(!foo.read_file("src/lib.rs").contains("use std::io;")); +} + +#[cargo_test] +fn check_fixable_error_no_fix() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file( + "src/lib.rs", + "use std::io;\n#[derive(Debug(x))]\nstruct Foo;", + ) + .build(); + + let rustc_message = raw_rustc_output(&foo, "src/lib.rs", &[]); + let expected_output = format!( + "\ +[CHECKING] foo v0.0.1 ([..]) +{}\ +[WARNING] `foo` (lib) generated 1 warning +[ERROR] could not compile `foo` due to previous error; 1 warning emitted +", + rustc_message + ); + foo.cargo("check") + .with_status(101) + .with_stderr(expected_output) + .run(); +} + +#[cargo_test] +fn check_fixable_warning_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("foo/src/lib.rs", "use std::io;") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + + [dependencies] + foo = { path = "../foo" } + "#, + ) + .file("bar/src/lib.rs", "use std::io;") + .build(); + + p.cargo("check") + .with_stderr_contains("[..] (run `cargo fix --lib -p foo` to apply 1 suggestion)") + .with_stderr_contains("[..] (run `cargo fix --lib -p bar` to apply 1 suggestion)") + .run(); +} + +#[cargo_test] +fn check_fixable_example() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + "#, + ) + .file("examples/ex1.rs", "use std::fmt; fn main() {}") + .build(); + p.cargo("check --all-targets") + .with_stderr_contains("[..] (run `cargo fix --example \"ex1\"` to apply 1 suggestion)") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn check_fixable_bench() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + + #[bench] + fn bench_hello(_b: &mut test::Bencher) { + use std::io; + assert_eq!(hello(), "hello") + } + "#, + ) + .file( + "benches/bench.rs", + " + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_b: &mut test::Bencher) { use std::fmt; } + ", + ) + .build(); + p.cargo("check --all-targets") + .with_stderr_contains("[..] (run `cargo fix --bench \"bench\"` to apply 1 suggestion)") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn check_fixable_mixed() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + + #[bench] + fn bench_hello(_b: &mut test::Bencher) { + use std::io; + assert_eq!(hello(), "hello") + } + #[test] + fn t1() { + use std::fmt; + } + "#, + ) + .file("examples/ex1.rs", "use std::fmt; fn main() {}") + .file( + "benches/bench.rs", + " + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_b: &mut test::Bencher) { use std::fmt; } + ", + ) + .build(); + p.cargo("check --all-targets") + .with_stderr_contains("[..] (run `cargo fix --bin \"foo\" --tests` to apply 2 suggestions)") + .with_stderr_contains("[..] (run `cargo fix --example \"ex1\"` to apply 1 suggestion)") + .with_stderr_contains("[..] (run `cargo fix --bench \"bench\"` to apply 1 suggestion)") + .run(); +} + +#[cargo_test] +fn check_fixable_warning_for_clippy() { + // A wrapper around `rustc` instead of calling `clippy` + let clippy_driver = project() + .at(cargo_test_support::paths::global_root().join("clippy-driver")) + .file("Cargo.toml", &basic_manifest("clippy-driver", "0.0.1")) + .file( + "src/main.rs", + r#" + fn main() { + let mut args = std::env::args_os(); + let _me = args.next().unwrap(); + let rustc = args.next().unwrap(); + let status = std::process::Command::new(rustc).args(args).status().unwrap(); + std::process::exit(status.code().unwrap_or(1)); + } + "#, + ) + .build(); + clippy_driver.cargo("build").run(); + + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + // We don't want to show a warning that is `clippy` + // specific since we are using a `rustc` wrapper + // inplace of `clippy` + .file("src/lib.rs", "use std::io;") + .build(); + + foo.cargo("check") + // We can't use `clippy` so we use a `rustc` workspace wrapper instead + .env( + "RUSTC_WORKSPACE_WRAPPER", + clippy_driver.bin("clippy-driver"), + ) + .with_stderr_contains("[..] (run `cargo clippy --fix --lib -p foo` to apply 1 suggestion)") + .run(); +} + +#[cargo_test] +fn check_unused_manifest_keys() { + Package::new("dep", "0.1.0").publish(); + Package::new("foo", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.2.0" + authors = [] + + [dependencies] + dep = { version = "0.1.0", wxz = "wxz" } + foo = { version = "0.1.0", abc = "abc" } + + [dev-dependencies] + foo = { version = "0.1.0", wxz = "wxz" } + + [build-dependencies] + foo = { version = "0.1.0", wxz = "wxz" } + + [target.'cfg(windows)'.dependencies] + foo = { version = "0.1.0", wxz = "wxz" } + + [target.x86_64-pc-windows-gnu.dev-dependencies] + foo = { version = "0.1.0", wxz = "wxz" } + + [target.bar.build-dependencies] + foo = { version = "0.1.0", wxz = "wxz" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] unused manifest key: dependencies.dep.wxz +[WARNING] unused manifest key: dependencies.foo.abc +[WARNING] unused manifest key: dev-dependencies.foo.wxz +[WARNING] unused manifest key: build-dependencies.foo.wxz +[WARNING] unused manifest key: target.bar.build-dependencies.foo.wxz +[WARNING] unused manifest key: target.cfg(windows).dependencies.foo.wxz +[WARNING] unused manifest key: target.x86_64-pc-windows-gnu.dev-dependencies.foo.wxz +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.1.0 ([..]) +[DOWNLOADED] dep v0.1.0 ([..]) +[CHECKING] [..] +[CHECKING] [..] +[CHECKING] bar v0.2.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} diff --git a/tests/testsuite/check_cfg.rs b/tests/testsuite/check_cfg.rs new file mode 100644 index 0000000..c35da63 --- /dev/null +++ b/tests/testsuite/check_cfg.rs @@ -0,0 +1,588 @@ +//! Tests for -Zcheck-cfg. + +use cargo_test_support::{basic_manifest, project}; + +macro_rules! x { + ($tool:tt => $what:tt $(of $who:tt)?) => {{ + #[cfg(windows)] + { + concat!("[RUNNING] [..]", $tool, "[..] --check-cfg ", + $what, '(', $($who,)* ')', "[..]") + } + #[cfg(not(windows))] + { + concat!("[RUNNING] [..]", $tool, "[..] --check-cfg '", + $what, '(', $($who,)* ')', "'", "[..]") + } + }}; + ($tool:tt => $what:tt of $who:tt with $($values:tt)*) => {{ + #[cfg(windows)] + { + concat!("[RUNNING] [..]", $tool, "[..] --check-cfg \"", + $what, '(', $who, $(", ", "/\"", $values, "/\"",)* ")", '"', "[..]") + } + #[cfg(not(windows))] + { + concat!("[RUNNING] [..]", $tool, "[..] --check-cfg '", + $what, '(', $who, $(", ", "\"", $values, "\"",)* ")", "'", "[..]") + } + }}; +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + f_a = [] + f_b = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v -Zcheck-cfg=features") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn features_with_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar/" } + + [features] + f_a = [] + f_b = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}") + .build(); + + p.cargo("check -v -Zcheck-cfg=features") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values" of "feature")) + .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn features_with_opt_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar/", optional = true } + + [features] + default = ["bar"] + f_a = [] + f_b = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}") + .build(); + + p.cargo("check -v -Zcheck-cfg=features") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values" of "feature")) + .with_stderr_contains(x!("rustc" => "values" of "feature" with "bar" "default" "f_a" "f_b")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn features_with_namespaced_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar/", optional = true } + + [features] + f_a = ["dep:bar"] + f_b = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}") + .build(); + + p.cargo("check -v -Zcheck-cfg=features") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn well_known_names() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v -Zcheck-cfg=names") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "names")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn well_known_values() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v -Zcheck-cfg=values") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn cli_all_options() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + f_a = [] + f_b = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v -Zcheck-cfg=features,names,values") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "names")) + .with_stderr_contains(x!("rustc" => "values")) + .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn features_with_cargo_check() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + f_a = [] + f_b = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v -Zcheck-cfg=features") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn well_known_names_with_check() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v -Zcheck-cfg=names") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "names")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn well_known_values_with_check() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v -Zcheck-cfg=values") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn features_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + f_a = [] + f_b = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("test -v -Zcheck-cfg=features") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn features_doctest() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + default = ["f_a"] + f_a = [] + f_b = [] + "#, + ) + .file("src/lib.rs", "#[allow(dead_code)] fn foo() {}") + .build(); + + p.cargo("test -v --doc -Zcheck-cfg=features") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values" of "feature" with "default" "f_a" "f_b")) + .with_stderr_contains(x!("rustdoc" => "values" of "feature" with "default" "f_a" "f_b")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn well_known_names_test() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("test -v -Zcheck-cfg=names") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "names")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn well_known_values_test() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("test -v -Zcheck-cfg=values") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn well_known_names_doctest() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/lib.rs", "#[allow(dead_code)] fn foo() {}") + .build(); + + p.cargo("test -v --doc -Zcheck-cfg=names") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "names")) + .with_stderr_contains(x!("rustdoc" => "names")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn well_known_values_doctest() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/lib.rs", "#[allow(dead_code)] fn foo() {}") + .build(); + + p.cargo("test -v --doc -Zcheck-cfg=values") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "values")) + .with_stderr_contains(x!("rustdoc" => "values")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn features_doc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + default = ["f_a"] + f_a = [] + f_b = [] + "#, + ) + .file("src/lib.rs", "#[allow(dead_code)] fn foo() {}") + .build(); + + p.cargo("doc -v -Zcheck-cfg=features") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustdoc" => "values" of "feature" with "default" "f_a" "f_b")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn build_script_feedback() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#"fn main() { println!("cargo:rustc-check-cfg=names(foo)"); }"#, + ) + .build(); + + p.cargo("check -v -Zcheck-cfg=output") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "names" of "foo")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn build_script_doc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#"fn main() { println!("cargo:rustc-check-cfg=names(foo)"); }"#, + ) + .build(); + p.cargo("doc -v -Zcheck-cfg=output") + .with_stderr_does_not_contain("rustc [..] --check-cfg [..]") + .with_stderr_contains(x!("rustdoc" => "names" of "foo")) + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc [..] build.rs [..]` +[RUNNING] `[..]/build-script-build` +[DOCUMENTING] foo [..] +[RUNNING] `rustdoc [..] src/main.rs [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .masquerade_as_nightly_cargo(&["check-cfg"]) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn build_script_override() { + let target = cargo_test_support::rustc_host(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + links = "a" + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("build.rs", "") + .file( + ".cargo/config", + &format!( + r#" + [target.{}.a] + rustc-check-cfg = ["names(foo)"] + "#, + target + ), + ) + .build(); + + p.cargo("check -v -Zcheck-cfg=output") + .with_stderr_contains(x!("rustc" => "names" of "foo")) + .masquerade_as_nightly_cargo(&["check-cfg"]) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn build_script_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + r#"fn main() { + println!("cargo:rustc-check-cfg=names(foo)"); + println!("cargo:rustc-cfg=foo"); + }"#, + ) + .file( + "src/lib.rs", + r#" + /// + /// ``` + /// extern crate foo; + /// + /// fn main() { + /// foo::foo() + /// } + /// ``` + /// + #[cfg(foo)] + pub fn foo() {} + + #[cfg(foo)] + #[test] + fn test_foo() { + foo() + } + "#, + ) + .file("tests/test.rs", "#[cfg(foo)] #[test] fn test_bar() {}") + .build(); + + p.cargo("test -v -Zcheck-cfg=output") + .with_stderr_contains(x!("rustc" => "names" of "foo")) + .with_stderr_contains(x!("rustdoc" => "names" of "foo")) + .with_stdout_contains("test test_foo ... ok") + .with_stdout_contains("test test_bar ... ok") + .with_stdout_contains_n("test [..] ... ok", 3) + .masquerade_as_nightly_cargo(&["check-cfg"]) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn config_valid() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + f_a = [] + f_b = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config.toml", + r#" + [unstable] + check-cfg = ["features", "names", "values"] + "#, + ) + .build(); + + p.cargo("check -v -Zcheck-cfg=features,names,values") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains(x!("rustc" => "names")) + .with_stderr_contains(x!("rustc" => "values")) + .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b")) + .run(); +} + +#[cargo_test(nightly, reason = "--check-cfg is unstable")] +fn config_invalid() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config.toml", + r#" + [unstable] + check-cfg = ["va"] + "#, + ) + .build(); + + p.cargo("check") + .masquerade_as_nightly_cargo(&["check-cfg"]) + .with_stderr_contains("error: unstable check-cfg only takes `features`, `names`, `values` or `output` as valid inputs") + .with_status(101) + .run(); +} diff --git a/tests/testsuite/clean.rs b/tests/testsuite/clean.rs new file mode 100644 index 0000000..e0885fd --- /dev/null +++ b/tests/testsuite/clean.rs @@ -0,0 +1,675 @@ +//! Tests for the `cargo clean` command. + +use cargo_test_support::registry::Package; +use cargo_test_support::{ + basic_bin_manifest, basic_manifest, git, main_file, project, project_in, rustc_host, +}; +use glob::GlobError; +use std::env; +use std::path::{Path, PathBuf}; + +#[cargo_test] +fn cargo_clean_simple() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build").run(); + assert!(p.build_dir().is_dir()); + + p.cargo("clean").run(); + assert!(!p.build_dir().is_dir()); +} + +#[cargo_test] +fn different_dir() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .file("src/bar/a.rs", "") + .build(); + + p.cargo("build").run(); + assert!(p.build_dir().is_dir()); + + p.cargo("clean").cwd("src").with_stdout("").run(); + assert!(!p.build_dir().is_dir()); +} + +#[cargo_test] +fn clean_multiple_packages() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.d1] + path = "d1" + [dependencies.d2] + path = "d2" + + [[bin]] + name = "foo" + "#, + ) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .file("d1/Cargo.toml", &basic_bin_manifest("d1")) + .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }") + .file("d2/Cargo.toml", &basic_bin_manifest("d2")) + .file("d2/src/main.rs", "fn main() { println!(\"d2\"); }") + .build(); + + p.cargo("build -p d1 -p d2 -p foo").run(); + + let d1_path = &p + .build_dir() + .join("debug") + .join(format!("d1{}", env::consts::EXE_SUFFIX)); + let d2_path = &p + .build_dir() + .join("debug") + .join(format!("d2{}", env::consts::EXE_SUFFIX)); + + assert!(p.bin("foo").is_file()); + assert!(d1_path.is_file()); + assert!(d2_path.is_file()); + + p.cargo("clean -p d1 -p d2") + .cwd("src") + .with_stdout("") + .run(); + assert!(p.bin("foo").is_file()); + assert!(!d1_path.is_file()); + assert!(!d2_path.is_file()); +} + +#[cargo_test] +fn clean_multiple_packages_in_glob_char_path() { + let p = project_in("[d1]") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + let foo_path = &p.build_dir().join("debug").join("deps"); + + #[cfg(not(target_env = "msvc"))] + let file_glob = "foo-*"; + + #[cfg(target_env = "msvc")] + let file_glob = "foo.pdb"; + + // Assert that build artifacts are produced + p.cargo("build").run(); + assert_ne!(get_build_artifacts(foo_path, file_glob).len(), 0); + + // Assert that build artifacts are destroyed + p.cargo("clean -p foo").run(); + assert_eq!(get_build_artifacts(foo_path, file_glob).len(), 0); +} + +fn get_build_artifacts(path: &PathBuf, file_glob: &str) -> Vec> { + let pattern = path.to_str().expect("expected utf-8 path"); + let pattern = glob::Pattern::escape(pattern); + + let path = PathBuf::from(pattern).join(file_glob); + let path = path.to_str().expect("expected utf-8 path"); + glob::glob(path) + .expect("expected glob to run") + .into_iter() + .collect::>>() +} + +#[cargo_test] +fn clean_p_only_cleans_specified_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [ + "foo", + "foo_core", + "foo-base", + ] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "//! foo") + .file("foo_core/Cargo.toml", &basic_manifest("foo_core", "0.1.0")) + .file("foo_core/src/lib.rs", "//! foo_core") + .file("foo-base/Cargo.toml", &basic_manifest("foo-base", "0.1.0")) + .file("foo-base/src/lib.rs", "//! foo-base") + .build(); + + let fingerprint_path = &p.build_dir().join("debug").join(".fingerprint"); + + p.cargo("build -p foo -p foo_core -p foo-base").run(); + + let mut fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); + + // Artifacts present for all after building + assert!(fingerprint_names.iter().any(|e| e == "foo")); + let num_foo_core_artifacts = fingerprint_names + .iter() + .filter(|&e| e == "foo_core") + .count(); + assert_ne!(num_foo_core_artifacts, 0); + let num_foo_base_artifacts = fingerprint_names + .iter() + .filter(|&e| e == "foo-base") + .count(); + assert_ne!(num_foo_base_artifacts, 0); + + p.cargo("clean -p foo").run(); + + fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); + + // Cleaning `foo` leaves artifacts for the others + assert!(!fingerprint_names.iter().any(|e| e == "foo")); + assert_eq!( + fingerprint_names + .iter() + .filter(|&e| e == "foo_core") + .count(), + num_foo_core_artifacts, + ); + assert_eq!( + fingerprint_names + .iter() + .filter(|&e| e == "foo-base") + .count(), + num_foo_core_artifacts, + ); +} + +fn get_fingerprints_without_hashes(fingerprint_path: &Path) -> Vec { + std::fs::read_dir(fingerprint_path) + .expect("Build dir should be readable") + .filter_map(|entry| entry.ok()) + .map(|entry| { + let name = entry.file_name(); + let name = name + .into_string() + .expect("fingerprint name should be UTF-8"); + name.rsplit_once('-') + .expect("Name should contain at least one hyphen") + .0 + .to_owned() + }) + .collect() +} + +#[cargo_test] +fn clean_release() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build --release").run(); + + p.cargo("clean -p foo").run(); + p.cargo("build --release").with_stdout("").run(); + + p.cargo("clean -p foo --release").run(); + p.cargo("build --release") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] release [optimized] target(s) in [..] +", + ) + .run(); + + p.cargo("build").run(); + + p.cargo("clean").arg("--release").run(); + assert!(p.build_dir().is_dir()); + assert!(p.build_dir().join("debug").is_dir()); + assert!(!p.build_dir().join("release").is_dir()); +} + +#[cargo_test] +fn clean_doc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("doc").run(); + + let doc_path = &p.build_dir().join("doc"); + + assert!(doc_path.is_dir()); + + p.cargo("clean --doc").run(); + + assert!(!doc_path.is_dir()); + assert!(p.build_dir().is_dir()); +} + +#[cargo_test] +fn build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + use std::path::PathBuf; + use std::env; + + fn main() { + let out = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + if env::var("FIRST").is_ok() { + std::fs::File::create(out.join("out")).unwrap(); + } else { + assert!(!out.join("out").exists()); + } + } + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build").env("FIRST", "1").run(); + p.cargo("clean -p foo").run(); + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] build.rs [..]` +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc [..] src/main.rs [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn clean_git() { + let git = git::new("dep", |project| { + project + .file("Cargo.toml", &basic_manifest("dep", "0.5.0")) + .file("src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + dep = {{ git = '{}' }} + "#, + git.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build").run(); + p.cargo("clean -p dep").with_stdout("").run(); + p.cargo("build").run(); +} + +#[cargo_test] +fn registry() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.1.0").publish(); + + p.cargo("build").run(); + p.cargo("clean -p bar").with_stdout("").run(); + p.cargo("build").run(); +} + +#[cargo_test] +fn clean_verbose() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = "0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.1.0").publish(); + + p.cargo("build").run(); + let mut expected = String::from( + "\ +[REMOVING] [..]target/debug/.fingerprint/bar[..] +[REMOVING] [..]target/debug/deps/libbar[..].rlib +[REMOVING] [..]target/debug/deps/bar-[..].d +[REMOVING] [..]target/debug/deps/libbar[..].rmeta +", + ); + if cfg!(target_os = "macos") { + // Rust 1.69 has changed so that split-debuginfo=unpacked includes unpacked for rlibs. + for obj in p.glob("target/debug/deps/bar-*.o") { + expected.push_str(&format!("[REMOVING] [..]{}", obj.unwrap().display())); + } + } + p.cargo("clean -p bar --verbose") + .with_stderr_unordered(&expected) + .run(); + p.cargo("build").run(); +} + +#[cargo_test] +fn clean_remove_rlib_rmeta() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + assert!(p.target_debug_dir().join("libfoo.rlib").exists()); + let rmeta = p.glob("target/debug/deps/*.rmeta").next().unwrap().unwrap(); + assert!(rmeta.exists()); + p.cargo("clean -p foo").run(); + assert!(!p.target_debug_dir().join("libfoo.rlib").exists()); + assert!(!rmeta.exists()); +} + +#[cargo_test] +fn package_cleans_all_the_things() { + // -p cleans everything + // Use dashes everywhere to make sure dash/underscore stuff is handled. + for crate_type in &["rlib", "dylib", "cdylib", "staticlib", "proc-macro"] { + // Try each crate type individually since the behavior changes when + // they are combined. + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo-bar" + version = "0.1.0" + + [lib] + crate-type = ["{}"] + "#, + crate_type + ), + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build").run(); + p.cargo("clean -p foo-bar").run(); + assert_all_clean(&p.build_dir()); + } + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo-bar" + version = "0.1.0" + edition = "2018" + + [lib] + crate-type = ["rlib", "dylib", "staticlib"] + + [[example]] + name = "foo-ex-rlib" + crate-type = ["rlib"] + test = true + + [[example]] + name = "foo-ex-cdylib" + crate-type = ["cdylib"] + test = true + + [[example]] + name = "foo-ex-bin" + test = true + "#, + ) + .file("src/lib.rs", "") + .file("src/lib/some-main.rs", "fn main() {}") + .file("src/bin/other-main.rs", "fn main() {}") + .file("examples/foo-ex-rlib.rs", "") + .file("examples/foo-ex-cdylib.rs", "") + .file("examples/foo-ex-bin.rs", "fn main() {}") + .file("tests/foo-test.rs", "") + .file("benches/foo-bench.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + p.cargo("build --all-targets") + .env("CARGO_INCREMENTAL", "1") + .run(); + p.cargo("test --all-targets") + .env("CARGO_INCREMENTAL", "1") + .run(); + p.cargo("check --all-targets") + .env("CARGO_INCREMENTAL", "1") + .run(); + p.cargo("clean -p foo-bar").run(); + assert_all_clean(&p.build_dir()); + + // Try some targets. + p.cargo("build --all-targets --target") + .arg(rustc_host()) + .run(); + p.cargo("clean -p foo-bar --target").arg(rustc_host()).run(); + assert_all_clean(&p.build_dir()); +} + +// Ensures that all files for the package have been deleted. +#[track_caller] +fn assert_all_clean(build_dir: &Path) { + let walker = walkdir::WalkDir::new(build_dir).into_iter(); + for entry in walker.filter_entry(|e| { + let path = e.path(); + // This is a known limitation, clean can't differentiate between + // the different build scripts from different packages. + !(path + .file_name() + .unwrap() + .to_str() + .unwrap() + .starts_with("build_script_build") + && path + .parent() + .unwrap() + .file_name() + .unwrap() + .to_str() + .unwrap() + == "incremental") + }) { + let entry = entry.unwrap(); + let path = entry.path(); + if let ".rustc_info.json" | ".cargo-lock" | "CACHEDIR.TAG" = + path.file_name().unwrap().to_str().unwrap() + { + continue; + } + if path.is_symlink() || path.is_file() { + panic!("{:?} was not cleaned", path); + } + } +} + +#[cargo_test] +fn clean_spec_multiple() { + // clean -p foo where foo matches multiple versions + Package::new("bar", "1.0.0").publish(); + Package::new("bar", "2.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar1 = {version="1.0", package="bar"} + bar2 = {version="2.0", package="bar"} + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + // Check suggestion for bad pkgid. + p.cargo("clean -p baz") + .with_status(101) + .with_stderr( + "\ +error: package ID specification `baz` did not match any packages + +Did you mean `bar`? +", + ) + .run(); + + p.cargo("clean -p bar:1.0.0") + .with_stderr( + "warning: version qualifier in `-p bar:1.0.0` is ignored, \ + cleaning all versions of `bar` found", + ) + .run(); + let mut walker = walkdir::WalkDir::new(p.build_dir()) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| { + let n = e.file_name().to_str().unwrap(); + n.starts_with("bar") || n.starts_with("libbar") + }); + if let Some(e) = walker.next() { + panic!("{:?} was not cleaned", e.path()); + } +} + +#[cargo_test] +fn clean_spec_reserved() { + // Clean when a target (like a test) has a reserved name. In this case, + // make sure `clean -p` doesn't delete the reserved directory `build` when + // there is a test named `build`. + Package::new("bar", "1.0.0") + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .file("tests/build.rs", "") + .build(); + + p.cargo("build --all-targets").run(); + assert!(p.target_debug_dir().join("build").is_dir()); + let build_test = p.glob("target/debug/deps/build-*").next().unwrap().unwrap(); + assert!(build_test.exists()); + // Tests are never "uplifted". + assert!(p.glob("target/debug/build-*").next().is_none()); + + p.cargo("clean -p foo").run(); + // Should not delete this. + assert!(p.target_debug_dir().join("build").is_dir()); + + // This should not rebuild bar. + p.cargo("build -v --all-targets") + .with_stderr( + "\ +[FRESH] bar v1.0.0 +[COMPILING] foo v0.1.0 [..] +[RUNNING] `rustc [..] +[RUNNING] `rustc [..] +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); +} diff --git a/tests/testsuite/collisions.rs b/tests/testsuite/collisions.rs new file mode 100644 index 0000000..77e05dd --- /dev/null +++ b/tests/testsuite/collisions.rs @@ -0,0 +1,550 @@ +//! Tests for when multiple artifacts have the same output filename. +//! See https://github.com/rust-lang/cargo/issues/6313 for more details. +//! Ideally these should never happen, but I don't think we'll ever be able to +//! prevent all collisions. + +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_manifest, cross_compile, project}; +use std::env; + +#[cargo_test] +fn collision_dylib() { + // Path dependencies don't include metadata hash in filename for dylibs. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "1.0.0" + + [lib] + crate-type = ["dylib"] + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "1.0.0" + + [lib] + crate-type = ["dylib"] + name = "a" + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + // `j=1` is required because on Windows you'll get an error due to + // two processes writing to the file at the same time. + p.cargo("build -j=1") + .with_stderr_contains(&format!("\ +[WARNING] output filename collision. +The lib target `a` in package `b v1.0.0 ([..]/foo/b)` has the same output filename as the lib target `a` in package `a v1.0.0 ([..]/foo/a)`. +Colliding filename is: [..]/foo/target/debug/deps/{}a{} +The targets should have unique names. +Consider changing their names to be unique or compiling them separately. +This may become a hard error in the future; see . +", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX)) + .run(); +} + +#[cargo_test] +fn collision_example() { + // Examples in a workspace can easily collide. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "1.0.0")) + .file("a/examples/ex1.rs", "fn main() {}") + .file("b/Cargo.toml", &basic_manifest("b", "1.0.0")) + .file("b/examples/ex1.rs", "fn main() {}") + .build(); + + // `j=1` is required because on Windows you'll get an error due to + // two processes writing to the file at the same time. + p.cargo("build --examples -j=1") + .with_stderr_contains("\ +[WARNING] output filename collision. +The example target `ex1` in package `b v1.0.0 ([..]/foo/b)` has the same output filename as the example target `ex1` in package `a v1.0.0 ([..]/foo/a)`. +Colliding filename is: [..]/foo/target/debug/examples/ex1[EXE] +The targets should have unique names. +Consider changing their names to be unique or compiling them separately. +This may become a hard error in the future; see . +") + .run(); +} + +#[cargo_test] +// See https://github.com/rust-lang/cargo/issues/7493 +#[cfg_attr( + any(target_env = "msvc", target_vendor = "apple"), + ignore = "--out-dir and examples are currently broken on MSVC and apple" +)] +fn collision_export() { + // `--out-dir` combines some things which can cause conflicts. + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("examples/foo.rs", "fn main() {}") + .file("src/main.rs", "fn main() {}") + .build(); + + // -j1 to avoid issues with two processes writing to the same file at the + // same time. + p.cargo("build -j1 --out-dir=out -Z unstable-options --bins --examples") + .masquerade_as_nightly_cargo(&["out-dir"]) + .with_stderr_contains("\ +[WARNING] `--out-dir` filename collision. +The example target `foo` in package `foo v1.0.0 ([..]/foo)` has the same output filename as the bin target `foo` in package `foo v1.0.0 ([..]/foo)`. +Colliding filename is: [..]/foo/out/foo[EXE] +The exported filenames should be unique. +Consider changing their names to be unique or compiling them separately. +This may become a hard error in the future; see . +") + .run(); +} + +#[cargo_test] +fn collision_doc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + foo2 = { path = "foo2" } + "#, + ) + .file("src/lib.rs", "") + .file( + "foo2/Cargo.toml", + r#" + [package] + name = "foo2" + version = "0.1.0" + + [lib] + name = "foo" + "#, + ) + .file("foo2/src/lib.rs", "") + .build(); + + p.cargo("doc -j=1") + .with_stderr_contains( + "\ +[WARNING] output filename collision. +The lib target `foo` in package `foo2 v0.1.0 ([..]/foo/foo2)` has the same output \ +filename as the lib target `foo` in package `foo v0.1.0 ([..]/foo)`. +Colliding filename is: [..]/foo/target/doc/foo/index.html +The targets should have unique names. +This is a known bug where multiple crates with the same name use +the same path; see . +", + ) + .run(); +} + +#[cargo_test] +fn collision_doc_multiple_versions() { + // Multiple versions of the same package. + Package::new("old-dep", "1.0.0").publish(); + Package::new("bar", "1.0.0").dep("old-dep", "1.0").publish(); + // Note that this removes "old-dep". Just checking what happens when there + // are orphans. + Package::new("bar", "2.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + bar2 = { package="bar", version="2.0" } + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Should only document bar 2.0, should not document old-dep. + p.cargo("doc") + .with_stderr_unordered( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v2.0.0 [..] +[DOWNLOADED] bar v1.0.0 [..] +[DOWNLOADED] old-dep v1.0.0 [..] +[CHECKING] old-dep v1.0.0 +[CHECKING] bar v2.0.0 +[CHECKING] bar v1.0.0 +[DOCUMENTING] bar v2.0.0 +[FINISHED] [..] +[DOCUMENTING] foo v0.1.0 [..] +", + ) + .run(); +} + +#[cargo_test] +fn collision_doc_host_target_feature_split() { + // Same dependency built twice due to different features. + // + // foo v0.1.0 + // ├── common v1.0.0 + // │ └── common-dep v1.0.0 + // └── pm v0.1.0 (proc-macro) + // └── common v1.0.0 + // └── common-dep v1.0.0 + // [build-dependencies] + // └── common-dep v1.0.0 + // + // Here `common` and `common-dep` are built twice. `common-dep` has + // different features for host versus target. + Package::new("common-dep", "1.0.0") + .feature("bdep-feat", &[]) + .file( + "src/lib.rs", + r#" + /// Some doc + pub fn f() {} + + /// Another doc + #[cfg(feature = "bdep-feat")] + pub fn bdep_func() {} + "#, + ) + .publish(); + Package::new("common", "1.0.0") + .dep("common-dep", "1.0") + .file( + "src/lib.rs", + r#" + /// Some doc + pub fn f() {} + "#, + ) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "2" + + [dependencies] + pm = { path = "pm" } + common = "1.0" + + [build-dependencies] + common-dep = { version = "1.0", features = ["bdep-feat"] } + "#, + ) + .file( + "src/lib.rs", + r#" + /// Some doc + pub fn f() {} + "#, + ) + .file("build.rs", "fn main() {}") + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + edition = "2018" + + [lib] + proc-macro = true + + [dependencies] + common = "1.0" + "#, + ) + .file( + "pm/src/lib.rs", + r#" + use proc_macro::TokenStream; + + /// Some doc + #[proc_macro] + pub fn pm(_input: TokenStream) -> TokenStream { + "".parse().unwrap() + } + "#, + ) + .build(); + + // No warnings, no duplicates, common and common-dep only documented once. + p.cargo("doc") + // Cannot check full output due to https://github.com/rust-lang/cargo/issues/9076 + .with_stderr_does_not_contain("[WARNING][..]") + .run(); + + assert!(p.build_dir().join("doc/common_dep/fn.f.html").exists()); + assert!(!p + .build_dir() + .join("doc/common_dep/fn.bdep_func.html") + .exists()); + assert!(p.build_dir().join("doc/common/fn.f.html").exists()); + assert!(p.build_dir().join("doc/pm/macro.pm.html").exists()); + assert!(p.build_dir().join("doc/foo/fn.f.html").exists()); +} + +#[cargo_test] +fn collision_doc_profile_split() { + // Same dependency built twice due to different profile settings. + Package::new("common", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + pm = { path = "pm" } + common = "1.0" + + [profile.dev] + opt-level = 2 + "#, + ) + .file("src/lib.rs", "") + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [dependencies] + common = "1.0" + + [lib] + proc-macro = true + "#, + ) + .file("pm/src/lib.rs", "") + .build(); + + // Just to verify that common is normally built twice. + // This is unordered because in rare cases `pm` may start + // building in-between the two `common`. + p.cargo("build -v") + .with_stderr_unordered( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] common v1.0.0 [..] +[COMPILING] common v1.0.0 +[RUNNING] `rustc --crate-name common [..] +[RUNNING] `rustc --crate-name common [..] +[COMPILING] pm v0.1.0 [..] +[RUNNING] `rustc --crate-name pm [..] +[COMPILING] foo v0.1.0 [..] +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] [..] +", + ) + .run(); + + // Should only document common once, no warnings. + p.cargo("doc") + .with_stderr_unordered( + "\ +[CHECKING] common v1.0.0 +[DOCUMENTING] common v1.0.0 +[DOCUMENTING] pm v0.1.0 [..] +[DOCUMENTING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn collision_doc_sources() { + // Different sources with the same package. + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + bar2 = { path = "bar", package = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("doc -j=1") + .with_stderr_unordered( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 [..] +[WARNING] output filename collision. +The lib target `bar` in package `bar v1.0.0` has the same output filename as \ +the lib target `bar` in package `bar v1.0.0 ([..]/foo/bar)`. +Colliding filename is: [..]/foo/target/doc/bar/index.html +The targets should have unique names. +This is a known bug where multiple crates with the same name use +the same path; see . +[CHECKING] bar v1.0.0 [..] +[DOCUMENTING] bar v1.0.0 [..] +[DOCUMENTING] bar v1.0.0 +[CHECKING] bar v1.0.0 +[DOCUMENTING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn collision_doc_target() { + // collision in doc with --target, doesn't fail due to orphans + if cross_compile::disabled() { + return; + } + + Package::new("orphaned", "1.0.0").publish(); + Package::new("bar", "1.0.0") + .dep("orphaned", "1.0") + .publish(); + Package::new("bar", "2.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar2 = { version = "2.0", package="bar" } + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("doc --target") + .arg(cross_compile::alternate()) + .with_stderr_unordered( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] orphaned v1.0.0 [..] +[DOWNLOADED] bar v2.0.0 [..] +[DOWNLOADED] bar v1.0.0 [..] +[CHECKING] orphaned v1.0.0 +[DOCUMENTING] bar v2.0.0 +[CHECKING] bar v2.0.0 +[CHECKING] bar v1.0.0 +[DOCUMENTING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn collision_with_root() { + // Check for a doc collision between a root package and a dependency. + // In this case, `foo-macro` comes from both the workspace and crates.io. + // This checks that the duplicate correction code doesn't choke on this + // by removing the root unit. + Package::new("foo-macro", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["abc", "foo-macro"] + "#, + ) + .file( + "abc/Cargo.toml", + r#" + [package] + name = "abc" + version = "1.0.0" + + [dependencies] + foo-macro = "1.0" + "#, + ) + .file("abc/src/lib.rs", "") + .file( + "foo-macro/Cargo.toml", + r#" + [package] + name = "foo-macro" + version = "1.0.0" + + [lib] + proc-macro = true + + [dependencies] + abc = {path="../abc"} + "#, + ) + .file("foo-macro/src/lib.rs", "") + .build(); + + p.cargo("doc -j=1") + .with_stderr_unordered("\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] foo-macro v1.0.0 [..] +warning: output filename collision. +The lib target `foo-macro` in package `foo-macro v1.0.0` has the same output filename as the lib target `foo-macro` in package `foo-macro v1.0.0 [..]`. +Colliding filename is: [CWD]/target/doc/foo_macro/index.html +The targets should have unique names. +This is a known bug where multiple crates with the same name use +the same path; see . +[CHECKING] foo-macro v1.0.0 +[DOCUMENTING] foo-macro v1.0.0 +[CHECKING] abc v1.0.0 [..] +[DOCUMENTING] foo-macro v1.0.0 [..] +[DOCUMENTING] abc v1.0.0 [..] +[FINISHED] [..] +") + .run(); +} diff --git a/tests/testsuite/concurrent.rs b/tests/testsuite/concurrent.rs new file mode 100644 index 0000000..fe4ecfc --- /dev/null +++ b/tests/testsuite/concurrent.rs @@ -0,0 +1,507 @@ +//! Tests for running multiple `cargo` processes at the same time. + +use std::fs; +use std::net::TcpListener; +use std::process::Stdio; +use std::sync::mpsc::channel; +use std::thread; +use std::{env, str}; + +use cargo_test_support::cargo_process; +use cargo_test_support::git; +use cargo_test_support::install::{assert_has_installed_exe, cargo_home}; +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_manifest, execs, project, slow_cpu_multiplier}; + +fn pkg(name: &str, vers: &str) { + Package::new(name, vers) + .file("src/main.rs", "fn main() {{}}") + .publish(); +} + +#[cargo_test] +fn multiple_installs() { + let p = project() + .no_manifest() + .file("a/Cargo.toml", &basic_manifest("foo", "0.0.0")) + .file("a/src/main.rs", "fn main() {}") + .file("b/Cargo.toml", &basic_manifest("bar", "0.0.0")) + .file("b/src/main.rs", "fn main() {}"); + let p = p.build(); + + let mut a = p.cargo("install").cwd("a").build_command(); + let mut b = p.cargo("install").cwd("b").build_command(); + + a.stdout(Stdio::piped()).stderr(Stdio::piped()); + b.stdout(Stdio::piped()).stderr(Stdio::piped()); + + let a = a.spawn().unwrap(); + let b = b.spawn().unwrap(); + let a = thread::spawn(move || a.wait_with_output().unwrap()); + let b = b.wait_with_output().unwrap(); + let a = a.join().unwrap(); + + execs().run_output(&a); + execs().run_output(&b); + + assert_has_installed_exe(cargo_home(), "foo"); + assert_has_installed_exe(cargo_home(), "bar"); +} + +#[cargo_test] +fn concurrent_installs() { + const LOCKED_BUILD: &str = "waiting for file lock on build directory"; + + pkg("foo", "0.0.1"); + pkg("bar", "0.0.1"); + + let mut a = cargo_process("install foo").build_command(); + let mut b = cargo_process("install bar").build_command(); + + a.stdout(Stdio::piped()).stderr(Stdio::piped()); + b.stdout(Stdio::piped()).stderr(Stdio::piped()); + + let a = a.spawn().unwrap(); + let b = b.spawn().unwrap(); + let a = thread::spawn(move || a.wait_with_output().unwrap()); + let b = b.wait_with_output().unwrap(); + let a = a.join().unwrap(); + + assert!(!str::from_utf8(&a.stderr).unwrap().contains(LOCKED_BUILD)); + assert!(!str::from_utf8(&b.stderr).unwrap().contains(LOCKED_BUILD)); + + execs().run_output(&a); + execs().run_output(&b); + + assert_has_installed_exe(cargo_home(), "foo"); + assert_has_installed_exe(cargo_home(), "bar"); +} + +#[cargo_test] +fn one_install_should_be_bad() { + let p = project() + .no_manifest() + .file("a/Cargo.toml", &basic_manifest("foo", "0.0.0")) + .file("a/src/main.rs", "fn main() {}") + .file("b/Cargo.toml", &basic_manifest("foo", "0.0.0")) + .file("b/src/main.rs", "fn main() {}"); + let p = p.build(); + + let mut a = p.cargo("install").cwd("a").build_command(); + let mut b = p.cargo("install").cwd("b").build_command(); + + a.stdout(Stdio::piped()).stderr(Stdio::piped()); + b.stdout(Stdio::piped()).stderr(Stdio::piped()); + + let a = a.spawn().unwrap(); + let b = b.spawn().unwrap(); + let a = thread::spawn(move || a.wait_with_output().unwrap()); + let b = b.wait_with_output().unwrap(); + let a = a.join().unwrap(); + + execs().run_output(&a); + execs().run_output(&b); + + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn multiple_registry_fetches() { + let mut pkg = Package::new("bar", "1.0.2"); + for i in 0..10 { + let name = format!("foo{}", i); + Package::new(&name, "1.0.0").publish(); + pkg.dep(&name, "*"); + } + pkg.publish(); + + let p = project() + .no_manifest() + .file( + "a/Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [dependencies] + bar = "*" + "#, + ) + .file("a/src/main.rs", "fn main() {}") + .file( + "b/Cargo.toml", + r#" + [package] + name = "bar" + authors = [] + version = "0.0.0" + + [dependencies] + bar = "*" + "#, + ) + .file("b/src/main.rs", "fn main() {}"); + let p = p.build(); + + let mut a = p.cargo("build").cwd("a").build_command(); + let mut b = p.cargo("build").cwd("b").build_command(); + + a.stdout(Stdio::piped()).stderr(Stdio::piped()); + b.stdout(Stdio::piped()).stderr(Stdio::piped()); + + let a = a.spawn().unwrap(); + let b = b.spawn().unwrap(); + let a = thread::spawn(move || a.wait_with_output().unwrap()); + let b = b.wait_with_output().unwrap(); + let a = a.join().unwrap(); + + execs().run_output(&a); + execs().run_output(&b); + + let suffix = env::consts::EXE_SUFFIX; + assert!(p + .root() + .join("a/target/debug") + .join(format!("foo{}", suffix)) + .is_file()); + assert!(p + .root() + .join("b/target/debug") + .join(format!("bar{}", suffix)) + .is_file()); +} + +#[cargo_test] +fn git_same_repo_different_tags() { + let a = git::new("dep", |project| { + project + .file("Cargo.toml", &basic_manifest("dep", "0.5.0")) + .file("src/lib.rs", "pub fn tag1() {}") + }); + + let repo = git2::Repository::open(&a.root()).unwrap(); + git::tag(&repo, "tag1"); + + a.change_file("src/lib.rs", "pub fn tag2() {}"); + git::add(&repo); + git::commit(&repo); + git::tag(&repo, "tag2"); + + let p = project() + .no_manifest() + .file( + "a/Cargo.toml", + &format!( + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [dependencies] + dep = {{ git = '{}', tag = 'tag1' }} + "#, + a.url() + ), + ) + .file( + "a/src/main.rs", + "extern crate dep; fn main() { dep::tag1(); }", + ) + .file( + "b/Cargo.toml", + &format!( + r#" + [package] + name = "bar" + authors = [] + version = "0.0.0" + + [dependencies] + dep = {{ git = '{}', tag = 'tag2' }} + "#, + a.url() + ), + ) + .file( + "b/src/main.rs", + "extern crate dep; fn main() { dep::tag2(); }", + ); + let p = p.build(); + + let mut a = p.cargo("build -v").cwd("a").build_command(); + let mut b = p.cargo("build -v").cwd("b").build_command(); + + a.stdout(Stdio::piped()).stderr(Stdio::piped()); + b.stdout(Stdio::piped()).stderr(Stdio::piped()); + + let a = a.spawn().unwrap(); + let b = b.spawn().unwrap(); + let a = thread::spawn(move || a.wait_with_output().unwrap()); + let b = b.wait_with_output().unwrap(); + let a = a.join().unwrap(); + + execs().run_output(&a); + execs().run_output(&b); +} + +#[cargo_test] +fn git_same_branch_different_revs() { + let a = git::new("dep", |project| { + project + .file("Cargo.toml", &basic_manifest("dep", "0.5.0")) + .file("src/lib.rs", "pub fn f1() {}") + }); + + let p = project() + .no_manifest() + .file( + "a/Cargo.toml", + &format!( + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [dependencies] + dep = {{ git = '{}' }} + "#, + a.url() + ), + ) + .file( + "a/src/main.rs", + "extern crate dep; fn main() { dep::f1(); }", + ) + .file( + "b/Cargo.toml", + &format!( + r#" + [package] + name = "bar" + authors = [] + version = "0.0.0" + + [dependencies] + dep = {{ git = '{}' }} + "#, + a.url() + ), + ) + .file( + "b/src/main.rs", + "extern crate dep; fn main() { dep::f2(); }", + ); + let p = p.build(); + + // Generate a Cargo.lock pointing at the current rev, then clear out the + // target directory + p.cargo("build").cwd("a").run(); + fs::remove_dir_all(p.root().join("a/target")).unwrap(); + + // Make a new commit on the master branch + let repo = git2::Repository::open(&a.root()).unwrap(); + a.change_file("src/lib.rs", "pub fn f2() {}"); + git::add(&repo); + git::commit(&repo); + + // Now run both builds in parallel. The build of `b` should pick up the + // newest commit while the build of `a` should use the locked old commit. + let mut a = p.cargo("build").cwd("a").build_command(); + let mut b = p.cargo("build").cwd("b").build_command(); + + a.stdout(Stdio::piped()).stderr(Stdio::piped()); + b.stdout(Stdio::piped()).stderr(Stdio::piped()); + + let a = a.spawn().unwrap(); + let b = b.spawn().unwrap(); + let a = thread::spawn(move || a.wait_with_output().unwrap()); + let b = b.wait_with_output().unwrap(); + let a = a.join().unwrap(); + + execs().run_output(&a); + execs().run_output(&b); +} + +#[cargo_test] +fn same_project() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", ""); + let p = p.build(); + + let mut a = p.cargo("build").build_command(); + let mut b = p.cargo("build").build_command(); + + a.stdout(Stdio::piped()).stderr(Stdio::piped()); + b.stdout(Stdio::piped()).stderr(Stdio::piped()); + + let a = a.spawn().unwrap(); + let b = b.spawn().unwrap(); + let a = thread::spawn(move || a.wait_with_output().unwrap()); + let b = b.wait_with_output().unwrap(); + let a = a.join().unwrap(); + + execs().run_output(&a); + execs().run_output(&b); +} + +// Make sure that if Cargo dies while holding a lock that it's released and the +// next Cargo to come in will take over cleanly. +#[cargo_test] +fn killing_cargo_releases_the_lock() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + use std::net::TcpStream; + + fn main() { + if std::env::var("A").is_ok() { + TcpStream::connect(&std::env::var("ADDR").unwrap()[..]) + .unwrap(); + std::thread::sleep(std::time::Duration::new(10, 0)); + } + } + "#, + ); + let p = p.build(); + + // Our build script will connect to our local TCP socket to inform us that + // it's started and that's how we know that `a` will have the lock + // when we kill it. + let l = TcpListener::bind("127.0.0.1:0").unwrap(); + let mut a = p.cargo("build").build_command(); + let mut b = p.cargo("build").build_command(); + a.stdout(Stdio::piped()).stderr(Stdio::piped()); + b.stdout(Stdio::piped()).stderr(Stdio::piped()); + a.env("ADDR", l.local_addr().unwrap().to_string()) + .env("A", "a"); + b.env("ADDR", l.local_addr().unwrap().to_string()) + .env_remove("A"); + + // Spawn `a`, wait for it to get to the build script (at which point the + // lock is held), then kill it. + let mut a = a.spawn().unwrap(); + l.accept().unwrap(); + a.kill().unwrap(); + + // Spawn `b`, then just finish the output of a/b the same way the above + // tests does. + let b = b.spawn().unwrap(); + let a = thread::spawn(move || a.wait_with_output().unwrap()); + let b = b.wait_with_output().unwrap(); + let a = a.join().unwrap(); + + // We killed `a`, so it shouldn't succeed, but `b` should have succeeded. + assert!(!a.status.success()); + execs().run_output(&b); +} + +#[cargo_test] +fn debug_release_ok() { + let p = project().file("src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("build").run(); + fs::remove_dir_all(p.root().join("target")).unwrap(); + + let mut a = p.cargo("build").build_command(); + let mut b = p.cargo("build --release").build_command(); + a.stdout(Stdio::piped()).stderr(Stdio::piped()); + b.stdout(Stdio::piped()).stderr(Stdio::piped()); + let a = a.spawn().unwrap(); + let b = b.spawn().unwrap(); + let a = thread::spawn(move || a.wait_with_output().unwrap()); + let b = b.wait_with_output().unwrap(); + let a = a.join().unwrap(); + + execs() + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run_output(&a); + execs() + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 [..] +[FINISHED] release [optimized] target(s) in [..] +", + ) + .run_output(&b); +} + +#[cargo_test] +fn no_deadlock_with_git_dependencies() { + let dep1 = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + .file("src/lib.rs", "") + }); + + let dep2 = git::new("dep2", |project| { + project + .file("Cargo.toml", &basic_manifest("dep2", "0.5.0")) + .file("src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [dependencies] + dep1 = {{ git = '{}' }} + dep2 = {{ git = '{}' }} + "#, + dep1.url(), + dep2.url() + ), + ) + .file("src/main.rs", "fn main() { }"); + let p = p.build(); + + let n_concurrent_builds = 5; + + let (tx, rx) = channel(); + for _ in 0..n_concurrent_builds { + let cmd = p + .cargo("build") + .build_command() + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn(); + let tx = tx.clone(); + thread::spawn(move || { + let result = cmd.unwrap().wait_with_output().unwrap(); + tx.send(result).unwrap() + }); + } + + for _ in 0..n_concurrent_builds { + let result = rx.recv_timeout(slow_cpu_multiplier(30)).expect("Deadlock!"); + execs().run_output(&result); + } +} diff --git a/tests/testsuite/config.rs b/tests/testsuite/config.rs new file mode 100644 index 0000000..92e1f42 --- /dev/null +++ b/tests/testsuite/config.rs @@ -0,0 +1,1596 @@ +//! Tests for config settings. + +use cargo::core::{PackageIdSpec, Shell}; +use cargo::util::config::{self, Config, Definition, SslVersionConfig, StringList}; +use cargo::util::interning::InternedString; +use cargo::util::toml::{self as cargo_toml, VecStringOrBool as VSOB}; +use cargo::CargoResult; +use cargo_test_support::compare; +use cargo_test_support::{panic_error, paths, project, symlink_supported, t}; +use serde::Deserialize; +use std::borrow::Borrow; +use std::collections::{BTreeMap, HashMap}; +use std::fs; +use std::io; +use std::os; +use std::path::{Path, PathBuf}; + +/// Helper for constructing a `Config` object. +pub struct ConfigBuilder { + env: HashMap, + unstable: Vec, + config_args: Vec, + cwd: Option, + enable_nightly_features: bool, +} + +impl ConfigBuilder { + pub fn new() -> ConfigBuilder { + ConfigBuilder { + env: HashMap::new(), + unstable: Vec::new(), + config_args: Vec::new(), + cwd: None, + enable_nightly_features: false, + } + } + + /// Passes a `-Z` flag. + pub fn unstable_flag(&mut self, s: impl Into) -> &mut Self { + self.unstable.push(s.into()); + self + } + + /// Sets an environment variable. + pub fn env(&mut self, key: impl Into, val: impl Into) -> &mut Self { + self.env.insert(key.into(), val.into()); + self + } + + /// Unconditionally enable nightly features, even on stable channels. + pub fn nightly_features_allowed(&mut self, allowed: bool) -> &mut Self { + self.enable_nightly_features = allowed; + self + } + + /// Passes a `--config` flag. + pub fn config_arg(&mut self, arg: impl Into) -> &mut Self { + self.config_args.push(arg.into()); + self + } + + /// Sets the current working directory where config files will be loaded. + pub fn cwd(&mut self, path: impl AsRef) -> &mut Self { + self.cwd = Some(paths::root().join(path.as_ref())); + self + } + + /// Creates the `Config`. + pub fn build(&self) -> Config { + self.build_err().unwrap() + } + + /// Creates the `Config`, returning a Result. + pub fn build_err(&self) -> CargoResult { + let output = Box::new(fs::File::create(paths::root().join("shell.out")).unwrap()); + let shell = Shell::from_write(output); + let cwd = self.cwd.clone().unwrap_or_else(|| paths::root()); + let homedir = paths::home(); + let mut config = Config::new(shell, cwd, homedir); + config.nightly_features_allowed = self.enable_nightly_features || !self.unstable.is_empty(); + config.set_env(self.env.clone()); + config.set_search_stop_path(paths::root()); + config.configure( + 0, + false, + None, + false, + false, + false, + &None, + &self.unstable, + &self.config_args, + )?; + Ok(config) + } +} + +fn new_config() -> Config { + ConfigBuilder::new().build() +} + +/// Read the output from Config. +pub fn read_output(config: Config) -> String { + drop(config); // Paranoid about flushing the file. + let path = paths::root().join("shell.out"); + fs::read_to_string(path).unwrap() +} + +#[cargo_test] +fn read_env_vars_for_config() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + fn main() { + assert_eq!(env::var("NUM_JOBS").unwrap(), "100"); + } + "#, + ) + .build(); + + p.cargo("check").env("CARGO_BUILD_JOBS", "100").run(); +} + +pub fn write_config(config: &str) { + write_config_at(paths::root().join(".cargo/config"), config); +} + +pub fn write_config_at(path: impl AsRef, contents: &str) { + let path = paths::root().join(path.as_ref()); + fs::create_dir_all(path.parent().unwrap()).unwrap(); + fs::write(path, contents).unwrap(); +} + +pub fn write_config_toml(config: &str) { + write_config_at(paths::root().join(".cargo/config.toml"), config); +} + +#[cfg(unix)] +fn symlink_file(target: &Path, link: &Path) -> io::Result<()> { + os::unix::fs::symlink(target, link) +} + +#[cfg(windows)] +fn symlink_file(target: &Path, link: &Path) -> io::Result<()> { + os::windows::fs::symlink_file(target, link) +} + +fn symlink_config_to_config_toml() { + let toml_path = paths::root().join(".cargo/config.toml"); + let symlink_path = paths::root().join(".cargo/config"); + t!(symlink_file(&toml_path, &symlink_path)); +} + +#[track_caller] +pub fn assert_error>(error: E, msgs: &str) { + let causes = error + .borrow() + .chain() + .enumerate() + .map(|(i, e)| { + if i == 0 { + e.to_string() + } else { + format!("Caused by:\n {}", e) + } + }) + .collect::>() + .join("\n\n"); + assert_match(msgs, &causes); +} + +#[track_caller] +pub fn assert_match(expected: &str, actual: &str) { + if let Err(e) = compare::match_exact(expected, actual, "output", "", None) { + panic_error("", e); + } +} + +#[cargo_test] +fn get_config() { + write_config( + "\ +[S] +f1 = 123 +", + ); + + let config = new_config(); + + #[derive(Debug, Deserialize, Eq, PartialEq)] + struct S { + f1: Option, + } + let s: S = config.get("S").unwrap(); + assert_eq!(s, S { f1: Some(123) }); + let config = ConfigBuilder::new().env("CARGO_S_F1", "456").build(); + let s: S = config.get("S").unwrap(); + assert_eq!(s, S { f1: Some(456) }); +} + +#[cfg(windows)] +#[cargo_test] +fn environment_variable_casing() { + // Issue #11814: Environment variable names are case-insensitive on Windows. + let config = ConfigBuilder::new() + .env("Path", "abc") + .env("Two-Words", "abc") + .env("two_words", "def") + .build(); + + let var = config.get_env("PATH").unwrap(); + assert_eq!(var, String::from("abc")); + + let var = config.get_env("path").unwrap(); + assert_eq!(var, String::from("abc")); + + let var = config.get_env("TWO-WORDS").unwrap(); + assert_eq!(var, String::from("abc")); + + // Make sure that we can still distinguish between dashes and underscores + // in variable names. + let var = config.get_env("Two_Words").unwrap(); + assert_eq!(var, String::from("def")); +} + +#[cargo_test] +fn config_works_with_extension() { + write_config_toml( + "\ +[foo] +f1 = 1 +", + ); + + let config = new_config(); + + assert_eq!(config.get::>("foo.f1").unwrap(), Some(1)); +} + +#[cargo_test] +fn config_ambiguous_filename_symlink_doesnt_warn() { + // Windows requires special permissions to create symlinks. + // If we don't have permission, just skip this test. + if !symlink_supported() { + return; + }; + + write_config_toml( + "\ +[foo] +f1 = 1 +", + ); + + symlink_config_to_config_toml(); + + let config = new_config(); + + assert_eq!(config.get::>("foo.f1").unwrap(), Some(1)); + + // It should NOT have warned for the symlink. + let output = read_output(config); + assert_eq!(output, ""); +} + +#[cargo_test] +fn config_ambiguous_filename() { + write_config( + "\ +[foo] +f1 = 1 +", + ); + + write_config_toml( + "\ +[foo] +f1 = 2 +", + ); + + let config = new_config(); + + // It should use the value from the one without the extension for + // backwards compatibility. + assert_eq!(config.get::>("foo.f1").unwrap(), Some(1)); + + // But it also should have warned. + let output = read_output(config); + let expected = "\ +warning: Both `[..]/.cargo/config` and `[..]/.cargo/config.toml` exist. Using `[..]/.cargo/config` +"; + assert_match(expected, &output); +} + +#[cargo_test] +fn config_unused_fields() { + write_config( + "\ +[S] +unused = 456 +", + ); + + let config = ConfigBuilder::new() + .env("CARGO_S_UNUSED2", "1") + .env("CARGO_S2_UNUSED", "2") + .build(); + + #[derive(Debug, Deserialize, Eq, PartialEq)] + struct S { + f1: Option, + } + // This prints a warning (verified below). + let s: S = config.get("S").unwrap(); + assert_eq!(s, S { f1: None }); + // This does not print anything, we cannot easily/reliably warn for + // environment variables. + let s: S = config.get("S2").unwrap(); + assert_eq!(s, S { f1: None }); + + // Verify the warnings. + let output = read_output(config); + let expected = "\ +warning: unused config key `S.unused` in `[..]/.cargo/config` +"; + assert_match(expected, &output); +} + +#[cargo_test] +fn config_load_toml_profile() { + write_config( + "\ +[profile.dev] +opt-level = 's' +lto = true +codegen-units=4 +debug = true +debug-assertions = true +rpath = true +panic = 'abort' +overflow-checks = true +incremental = true + +[profile.dev.build-override] +opt-level = 1 + +[profile.dev.package.bar] +codegen-units = 9 + +[profile.no-lto] +inherits = 'dev' +dir-name = 'without-lto' +lto = false +", + ); + + let config = ConfigBuilder::new() + .unstable_flag("advanced-env") + .env("CARGO_PROFILE_DEV_CODEGEN_UNITS", "5") + .env("CARGO_PROFILE_DEV_BUILD_OVERRIDE_CODEGEN_UNITS", "11") + .env("CARGO_PROFILE_DEV_PACKAGE_env_CODEGEN_UNITS", "13") + .env("CARGO_PROFILE_DEV_PACKAGE_bar_OPT_LEVEL", "2") + .build(); + + // TODO: don't use actual `tomlprofile`. + let p: cargo_toml::TomlProfile = config.get("profile.dev").unwrap(); + let mut packages = BTreeMap::new(); + let key = + cargo_toml::ProfilePackageSpec::Spec(::cargo::core::PackageIdSpec::parse("bar").unwrap()); + let o_profile = cargo_toml::TomlProfile { + opt_level: Some(cargo_toml::TomlOptLevel("2".to_string())), + codegen_units: Some(9), + ..Default::default() + }; + packages.insert(key, o_profile); + let key = + cargo_toml::ProfilePackageSpec::Spec(::cargo::core::PackageIdSpec::parse("env").unwrap()); + let o_profile = cargo_toml::TomlProfile { + codegen_units: Some(13), + ..Default::default() + }; + packages.insert(key, o_profile); + + assert_eq!( + p, + cargo_toml::TomlProfile { + opt_level: Some(cargo_toml::TomlOptLevel("s".to_string())), + lto: Some(cargo_toml::StringOrBool::Bool(true)), + codegen_units: Some(5), + debug: Some(cargo_toml::U32OrBool::Bool(true)), + debug_assertions: Some(true), + rpath: Some(true), + panic: Some("abort".to_string()), + overflow_checks: Some(true), + incremental: Some(true), + package: Some(packages), + build_override: Some(Box::new(cargo_toml::TomlProfile { + opt_level: Some(cargo_toml::TomlOptLevel("1".to_string())), + codegen_units: Some(11), + ..Default::default() + })), + ..Default::default() + } + ); + + let p: cargo_toml::TomlProfile = config.get("profile.no-lto").unwrap(); + assert_eq!( + p, + cargo_toml::TomlProfile { + lto: Some(cargo_toml::StringOrBool::Bool(false)), + dir_name: Some(InternedString::new("without-lto")), + inherits: Some(InternedString::new("dev")), + ..Default::default() + } + ); +} + +#[cargo_test] +fn profile_env_var_prefix() { + // Check for a bug with collision on DEBUG vs DEBUG_ASSERTIONS. + let config = ConfigBuilder::new() + .env("CARGO_PROFILE_DEV_DEBUG_ASSERTIONS", "false") + .build(); + let p: cargo_toml::TomlProfile = config.get("profile.dev").unwrap(); + assert_eq!(p.debug_assertions, Some(false)); + assert_eq!(p.debug, None); + + let config = ConfigBuilder::new() + .env("CARGO_PROFILE_DEV_DEBUG", "1") + .build(); + let p: cargo_toml::TomlProfile = config.get("profile.dev").unwrap(); + assert_eq!(p.debug_assertions, None); + assert_eq!(p.debug, Some(cargo_toml::U32OrBool::U32(1))); + + let config = ConfigBuilder::new() + .env("CARGO_PROFILE_DEV_DEBUG_ASSERTIONS", "false") + .env("CARGO_PROFILE_DEV_DEBUG", "1") + .build(); + let p: cargo_toml::TomlProfile = config.get("profile.dev").unwrap(); + assert_eq!(p.debug_assertions, Some(false)); + assert_eq!(p.debug, Some(cargo_toml::U32OrBool::U32(1))); +} + +#[cargo_test] +fn config_deserialize_any() { + // Some tests to exercise deserialize_any for deserializers that need to + // be told the format. + write_config( + "\ +a = true +b = ['b'] +c = ['c'] +", + ); + + // advanced-env + let config = ConfigBuilder::new() + .unstable_flag("advanced-env") + .env("CARGO_ENVB", "false") + .env("CARGO_C", "['d']") + .env("CARGO_ENVL", "['a', 'b']") + .build(); + assert_eq!(config.get::("a").unwrap(), VSOB::Bool(true)); + assert_eq!( + config.get::("b").unwrap(), + VSOB::VecString(vec!["b".to_string()]) + ); + assert_eq!( + config.get::("c").unwrap(), + VSOB::VecString(vec!["c".to_string(), "d".to_string()]) + ); + assert_eq!(config.get::("envb").unwrap(), VSOB::Bool(false)); + assert_eq!( + config.get::("envl").unwrap(), + VSOB::VecString(vec!["a".to_string(), "b".to_string()]) + ); + + // Demonstrate where merging logic isn't very smart. This could be improved. + let config = ConfigBuilder::new().env("CARGO_A", "x y").build(); + assert_error( + config.get::("a").unwrap_err(), + "\ +error in environment variable `CARGO_A`: could not load config key `a` + +Caused by: + invalid type: string \"x y\", expected a boolean or vector of strings", + ); + + // Normal env. + let config = ConfigBuilder::new() + .unstable_flag("advanced-env") + .env("CARGO_B", "d e") + .env("CARGO_C", "f g") + .build(); + assert_eq!( + config.get::("b").unwrap(), + VSOB::VecString(vec!["b".to_string(), "d".to_string(), "e".to_string()]) + ); + assert_eq!( + config.get::("c").unwrap(), + VSOB::VecString(vec!["c".to_string(), "f".to_string(), "g".to_string()]) + ); + + // config-cli + // This test demonstrates that ConfigValue::merge isn't very smart. + // It would be nice if it was smarter. + let config = ConfigBuilder::new().config_arg("a = ['a']").build_err(); + assert_error( + config.unwrap_err(), + "\ +failed to merge --config key `a` into `[..]/.cargo/config` + +Caused by: + failed to merge config value from `--config cli option` into `[..]/.cargo/config`: \ +expected boolean, but found array", + ); + + // config-cli and advanced-env + let config = ConfigBuilder::new() + .unstable_flag("advanced-env") + .config_arg("b=['clib']") + .config_arg("c=['clic']") + .env("CARGO_B", "env1 env2") + .env("CARGO_C", "['e1', 'e2']") + .build(); + assert_eq!( + config.get::("b").unwrap(), + VSOB::VecString(vec![ + "b".to_string(), + "clib".to_string(), + "env1".to_string(), + "env2".to_string() + ]) + ); + assert_eq!( + config.get::("c").unwrap(), + VSOB::VecString(vec![ + "c".to_string(), + "clic".to_string(), + "e1".to_string(), + "e2".to_string() + ]) + ); +} + +#[cargo_test] +fn config_toml_errors() { + write_config( + "\ +[profile.dev] +opt-level = 'foo' +", + ); + + let config = new_config(); + + assert_error( + config + .get::("profile.dev") + .unwrap_err(), + "\ +error in [..]/.cargo/config: could not load config key `profile.dev.opt-level` + +Caused by: + must be `0`, `1`, `2`, `3`, `s` or `z`, but found the string: \"foo\"", + ); + + let config = ConfigBuilder::new() + .env("CARGO_PROFILE_DEV_OPT_LEVEL", "asdf") + .build(); + + assert_error( + config.get::("profile.dev").unwrap_err(), + "\ +error in environment variable `CARGO_PROFILE_DEV_OPT_LEVEL`: could not load config key `profile.dev.opt-level` + +Caused by: + must be `0`, `1`, `2`, `3`, `s` or `z`, but found the string: \"asdf\"", + ); +} + +#[cargo_test] +fn load_nested() { + write_config( + "\ +[nest.foo] +f1 = 1 +f2 = 2 +[nest.bar] +asdf = 3 +", + ); + + let config = ConfigBuilder::new() + .unstable_flag("advanced-env") + .env("CARGO_NEST_foo_f2", "3") + .env("CARGO_NESTE_foo_f1", "1") + .env("CARGO_NESTE_foo_f2", "3") + .env("CARGO_NESTE_bar_asdf", "3") + .build(); + + type Nested = HashMap>; + + let n: Nested = config.get("nest").unwrap(); + let mut expected = HashMap::new(); + let mut foo = HashMap::new(); + foo.insert("f1".to_string(), 1); + foo.insert("f2".to_string(), 3); + expected.insert("foo".to_string(), foo); + let mut bar = HashMap::new(); + bar.insert("asdf".to_string(), 3); + expected.insert("bar".to_string(), bar); + assert_eq!(n, expected); + + let n: Nested = config.get("neste").unwrap(); + assert_eq!(n, expected); +} + +#[cargo_test] +fn get_errors() { + write_config( + "\ +[S] +f1 = 123 +f2 = 'asdf' +big = 123456789 +", + ); + + let config = ConfigBuilder::new() + .env("CARGO_E_S", "asdf") + .env("CARGO_E_BIG", "123456789") + .build(); + assert_error( + config.get::("foo").unwrap_err(), + "missing config key `foo`", + ); + assert_error( + config.get::("foo.bar").unwrap_err(), + "missing config key `foo.bar`", + ); + assert_error( + config.get::("S.f2").unwrap_err(), + "error in [..]/.cargo/config: `S.f2` expected an integer, but found a string", + ); + assert_error( + config.get::("S.big").unwrap_err(), + "\ +error in [..].cargo/config: could not load config key `S.big` + +Caused by: + invalid value: integer `123456789`, expected u8", + ); + + // Environment variable type errors. + assert_error( + config.get::("e.s").unwrap_err(), + "error in environment variable `CARGO_E_S`: invalid digit found in string", + ); + assert_error( + config.get::("e.big").unwrap_err(), + "\ +error in environment variable `CARGO_E_BIG`: could not load config key `e.big` + +Caused by: + invalid value: integer `123456789`, expected i8", + ); + + #[derive(Debug, Deserialize)] + #[allow(dead_code)] + struct S { + f1: i64, + f2: String, + f3: i64, + big: i64, + } + assert_error(config.get::("S").unwrap_err(), "missing field `f3`"); +} + +#[cargo_test] +fn config_get_option() { + write_config( + "\ +[foo] +f1 = 1 +", + ); + + let config = ConfigBuilder::new().env("CARGO_BAR_ASDF", "3").build(); + + assert_eq!(config.get::>("a").unwrap(), None); + assert_eq!(config.get::>("a.b").unwrap(), None); + assert_eq!(config.get::>("foo.f1").unwrap(), Some(1)); + assert_eq!(config.get::>("bar.asdf").unwrap(), Some(3)); + assert_eq!(config.get::>("bar.zzzz").unwrap(), None); +} + +#[cargo_test] +fn config_bad_toml() { + write_config("asdf"); + let config = new_config(); + assert_error( + config.get::("foo").unwrap_err(), + "\ +could not load Cargo configuration + +Caused by: + could not parse TOML configuration in `[..]/.cargo/config` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 1, column 5 + | +1 | asdf + | ^ +expected `.`, `=`", + ); +} + +#[cargo_test] +fn config_get_list() { + write_config( + "\ +l1 = [] +l2 = ['one', 'two'] +l3 = 123 +l4 = ['one', 'two'] + +[nested] +l = ['x'] + +[nested2] +l = ['y'] + +[nested-empty] +", + ); + + type L = Vec; + + let config = ConfigBuilder::new() + .unstable_flag("advanced-env") + .env("CARGO_L4", "['three', 'four']") + .env("CARGO_L5", "['a']") + .env("CARGO_ENV_EMPTY", "[]") + .env("CARGO_ENV_BLANK", "") + .env("CARGO_ENV_NUM", "1") + .env("CARGO_ENV_NUM_LIST", "[1]") + .env("CARGO_ENV_TEXT", "asdf") + .env("CARGO_LEPAIR", "['a', 'b']") + .env("CARGO_NESTED2_L", "['z']") + .env("CARGO_NESTEDE_L", "['env']") + .env("CARGO_BAD_ENV", "[zzz]") + .build(); + + assert_eq!(config.get::("unset").unwrap(), vec![] as Vec); + assert_eq!(config.get::("l1").unwrap(), vec![] as Vec); + assert_eq!(config.get::("l2").unwrap(), vec!["one", "two"]); + assert_error( + config.get::("l3").unwrap_err(), + "\ +invalid configuration for key `l3` +expected a list, but found a integer for `l3` in [..]/.cargo/config", + ); + assert_eq!( + config.get::("l4").unwrap(), + vec!["one", "two", "three", "four"] + ); + assert_eq!(config.get::("l5").unwrap(), vec!["a"]); + assert_eq!(config.get::("env-empty").unwrap(), vec![] as Vec); + assert_eq!(config.get::("env-blank").unwrap(), vec![] as Vec); + assert_eq!(config.get::("env-num").unwrap(), vec!["1".to_string()]); + assert_error( + config.get::("env-num-list").unwrap_err(), + "error in environment variable `CARGO_ENV_NUM_LIST`: \ + expected string, found integer", + ); + assert_eq!( + config.get::("env-text").unwrap(), + vec!["asdf".to_string()] + ); + // "invalid number" here isn't the best error, but I think it's just toml.rs. + assert_error( + config.get::("bad-env").unwrap_err(), + "\ +error in environment variable `CARGO_BAD_ENV`: could not parse TOML list: TOML parse error at line 1, column 2 + | +1 | [zzz] + | ^ +invalid array +expected `]` +", + ); + + // Try some other sequence-like types. + assert_eq!( + config + .get::<(String, String, String, String)>("l4") + .unwrap(), + ( + "one".to_string(), + "two".to_string(), + "three".to_string(), + "four".to_string() + ) + ); + assert_eq!(config.get::<(String,)>("l5").unwrap(), ("a".to_string(),)); + + // Tuple struct + #[derive(Debug, Deserialize, Eq, PartialEq)] + struct TupS(String, String); + assert_eq!( + config.get::("lepair").unwrap(), + TupS("a".to_string(), "b".to_string()) + ); + + // Nested with an option. + #[derive(Debug, Deserialize, Eq, PartialEq)] + struct S { + l: Option>, + } + assert_eq!(config.get::("nested-empty").unwrap(), S { l: None }); + assert_eq!( + config.get::("nested").unwrap(), + S { + l: Some(vec!["x".to_string()]), + } + ); + assert_eq!( + config.get::("nested2").unwrap(), + S { + l: Some(vec!["y".to_string(), "z".to_string()]), + } + ); + assert_eq!( + config.get::("nestede").unwrap(), + S { + l: Some(vec!["env".to_string()]), + } + ); +} + +#[cargo_test] +fn config_get_other_types() { + write_config( + "\ +ns = 123 +ns2 = 456 +", + ); + + let config = ConfigBuilder::new() + .env("CARGO_NSE", "987") + .env("CARGO_NS2", "654") + .build(); + + #[derive(Debug, Deserialize, Eq, PartialEq)] + #[serde(transparent)] + struct NewS(i32); + assert_eq!(config.get::("ns").unwrap(), NewS(123)); + assert_eq!(config.get::("ns2").unwrap(), NewS(654)); + assert_eq!(config.get::("nse").unwrap(), NewS(987)); + assert_error( + config.get::("unset").unwrap_err(), + "missing config key `unset`", + ); +} + +#[cargo_test] +fn config_relative_path() { + write_config(&format!( + "\ +p1 = 'foo/bar' +p2 = '../abc' +p3 = 'b/c' +abs = '{}' +", + paths::home().display(), + )); + + let config = ConfigBuilder::new() + .env("CARGO_EPATH", "a/b") + .env("CARGO_P3", "d/e") + .build(); + + assert_eq!( + config + .get::("p1") + .unwrap() + .resolve_path(&config), + paths::root().join("foo/bar") + ); + assert_eq!( + config + .get::("p2") + .unwrap() + .resolve_path(&config), + paths::root().join("../abc") + ); + assert_eq!( + config + .get::("p3") + .unwrap() + .resolve_path(&config), + paths::root().join("d/e") + ); + assert_eq!( + config + .get::("abs") + .unwrap() + .resolve_path(&config), + paths::home() + ); + assert_eq!( + config + .get::("epath") + .unwrap() + .resolve_path(&config), + paths::root().join("a/b") + ); +} + +#[cargo_test] +fn config_get_integers() { + write_config( + "\ +npos = 123456789 +nneg = -123456789 +i64max = 9223372036854775807 +", + ); + + let config = ConfigBuilder::new() + .env("CARGO_EPOS", "123456789") + .env("CARGO_ENEG", "-1") + .env("CARGO_EI64MAX", "9223372036854775807") + .build(); + + assert_eq!( + config.get::("i64max").unwrap(), + 9_223_372_036_854_775_807 + ); + assert_eq!( + config.get::("i64max").unwrap(), + 9_223_372_036_854_775_807 + ); + assert_eq!( + config.get::("ei64max").unwrap(), + 9_223_372_036_854_775_807 + ); + assert_eq!( + config.get::("ei64max").unwrap(), + 9_223_372_036_854_775_807 + ); + + assert_error( + config.get::("nneg").unwrap_err(), + "\ +error in [..].cargo/config: could not load config key `nneg` + +Caused by: + invalid value: integer `-123456789`, expected u32", + ); + assert_error( + config.get::("eneg").unwrap_err(), + "\ +error in environment variable `CARGO_ENEG`: could not load config key `eneg` + +Caused by: + invalid value: integer `-1`, expected u32", + ); + assert_error( + config.get::("npos").unwrap_err(), + "\ +error in [..].cargo/config: could not load config key `npos` + +Caused by: + invalid value: integer `123456789`, expected i8", + ); + assert_error( + config.get::("epos").unwrap_err(), + "\ +error in environment variable `CARGO_EPOS`: could not load config key `epos` + +Caused by: + invalid value: integer `123456789`, expected i8", + ); +} + +#[cargo_test] +fn config_get_ssl_version_missing() { + write_config( + "\ +[http] +hello = 'world' +", + ); + + let config = new_config(); + + assert!(config + .get::>("http.ssl-version") + .unwrap() + .is_none()); +} + +#[cargo_test] +fn config_get_ssl_version_single() { + write_config( + "\ +[http] +ssl-version = 'tlsv1.2' +", + ); + + let config = new_config(); + + let a = config + .get::>("http.ssl-version") + .unwrap() + .unwrap(); + match a { + SslVersionConfig::Single(v) => assert_eq!(&v, "tlsv1.2"), + SslVersionConfig::Range(_) => panic!("Did not expect ssl version min/max."), + }; +} + +#[cargo_test] +fn config_get_ssl_version_min_max() { + write_config( + "\ +[http] +ssl-version.min = 'tlsv1.2' +ssl-version.max = 'tlsv1.3' +", + ); + + let config = new_config(); + + let a = config + .get::>("http.ssl-version") + .unwrap() + .unwrap(); + match a { + SslVersionConfig::Single(_) => panic!("Did not expect exact ssl version."), + SslVersionConfig::Range(range) => { + assert_eq!(range.min, Some(String::from("tlsv1.2"))); + assert_eq!(range.max, Some(String::from("tlsv1.3"))); + } + }; +} + +#[cargo_test] +fn config_get_ssl_version_both_forms_configured() { + // this is not allowed + write_config( + "\ +[http] +ssl-version = 'tlsv1.1' +ssl-version.min = 'tlsv1.2' +ssl-version.max = 'tlsv1.3' +", + ); + + let config = new_config(); + + assert_error( + config + .get::("http.ssl-version") + .unwrap_err(), + "\ +could not load Cargo configuration + +Caused by: + could not parse TOML configuration in `[..]/.cargo/config` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 3, column 1 + | +3 | ssl-version.min = 'tlsv1.2' + | ^ +dotted key `ssl-version` attempted to extend non-table type (string) +", + ); +} + +#[cargo_test] +/// Assert that unstable options can be configured with the `unstable` table in +/// cargo config files +fn unstable_table_notation() { + write_config( + "\ +[unstable] +print-im-a-teapot = true +", + ); + let config = ConfigBuilder::new().nightly_features_allowed(true).build(); + assert_eq!(config.cli_unstable().print_im_a_teapot, true); +} + +#[cargo_test] +/// Assert that dotted notation works for configuring unstable options +fn unstable_dotted_notation() { + write_config( + "\ +unstable.print-im-a-teapot = true +", + ); + let config = ConfigBuilder::new().nightly_features_allowed(true).build(); + assert_eq!(config.cli_unstable().print_im_a_teapot, true); +} + +#[cargo_test] +/// Assert that Zflags on the CLI take precedence over those from config +fn unstable_cli_precedence() { + write_config( + "\ +unstable.print-im-a-teapot = true +", + ); + let config = ConfigBuilder::new().nightly_features_allowed(true).build(); + assert_eq!(config.cli_unstable().print_im_a_teapot, true); + + let config = ConfigBuilder::new() + .unstable_flag("print-im-a-teapot=no") + .build(); + assert_eq!(config.cli_unstable().print_im_a_teapot, false); +} + +#[cargo_test] +/// Assert that attempting to set an unstable flag that doesn't exist via config +/// is ignored on stable +fn unstable_invalid_flag_ignored_on_stable() { + write_config( + "\ +unstable.an-invalid-flag = 'yes' +", + ); + assert!(ConfigBuilder::new().build_err().is_ok()); +} + +#[cargo_test] +/// Assert that unstable options can be configured with the `unstable` table in +/// cargo config files +fn unstable_flags_ignored_on_stable() { + write_config( + "\ +[unstable] +print-im-a-teapot = true +", + ); + // Enforce stable channel even when testing on nightly. + let config = ConfigBuilder::new().nightly_features_allowed(false).build(); + assert_eq!(config.cli_unstable().print_im_a_teapot, false); +} + +#[cargo_test] +fn table_merge_failure() { + // Config::merge fails to merge entries in two tables. + write_config_at( + "foo/.cargo/config", + " + [table] + key = ['foo'] + ", + ); + write_config_at( + ".cargo/config", + " + [table] + key = 'bar' + ", + ); + + #[derive(Debug, Deserialize)] + #[allow(dead_code)] + struct Table { + key: StringList, + } + let config = ConfigBuilder::new().cwd("foo").build(); + assert_error( + config.get::("table").unwrap_err(), + "\ +could not load Cargo configuration + +Caused by: + failed to merge configuration at `[..]/.cargo/config` + +Caused by: + failed to merge key `table` between [..]/foo/.cargo/config and [..]/.cargo/config + +Caused by: + failed to merge key `key` between [..]/foo/.cargo/config and [..]/.cargo/config + +Caused by: + failed to merge config value from `[..]/.cargo/config` into `[..]/foo/.cargo/config`: \ + expected array, but found string", + ); +} + +#[cargo_test] +fn non_string_in_array() { + // Currently only strings are supported. + write_config("foo = [1, 2, 3]"); + let config = new_config(); + assert_error( + config.get::>("foo").unwrap_err(), + "\ +could not load Cargo configuration + +Caused by: + failed to load TOML configuration from `[..]/.cargo/config` + +Caused by: + failed to parse key `foo` + +Caused by: + expected string but found integer in list", + ); +} + +#[cargo_test] +fn struct_with_opt_inner_struct() { + // Struct with a key that is Option of another struct. + // Check that can be defined with environment variable. + #[derive(Deserialize)] + struct Inner { + value: Option, + } + #[derive(Deserialize)] + struct Foo { + inner: Option, + } + let config = ConfigBuilder::new() + .env("CARGO_FOO_INNER_VALUE", "12") + .build(); + let f: Foo = config.get("foo").unwrap(); + assert_eq!(f.inner.unwrap().value.unwrap(), 12); +} + +#[cargo_test] +fn struct_with_default_inner_struct() { + // Struct with serde defaults. + // Check that can be defined with environment variable. + #[derive(Deserialize, Default)] + #[serde(default)] + struct Inner { + value: i32, + } + #[derive(Deserialize, Default)] + #[serde(default)] + struct Foo { + inner: Inner, + } + let config = ConfigBuilder::new() + .env("CARGO_FOO_INNER_VALUE", "12") + .build(); + let f: Foo = config.get("foo").unwrap(); + assert_eq!(f.inner.value, 12); +} + +#[cargo_test] +fn overlapping_env_config() { + // Issue where one key is a prefix of another. + #[derive(Deserialize)] + #[serde(rename_all = "kebab-case")] + struct Ambig { + debug: Option, + debug_assertions: Option, + } + let config = ConfigBuilder::new() + .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true") + .build(); + + let s: Ambig = config.get("ambig").unwrap(); + assert_eq!(s.debug_assertions, Some(true)); + assert_eq!(s.debug, None); + + let config = ConfigBuilder::new().env("CARGO_AMBIG_DEBUG", "0").build(); + let s: Ambig = config.get("ambig").unwrap(); + assert_eq!(s.debug_assertions, None); + assert_eq!(s.debug, Some(0)); + + let config = ConfigBuilder::new() + .env("CARGO_AMBIG_DEBUG", "1") + .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true") + .build(); + let s: Ambig = config.get("ambig").unwrap(); + assert_eq!(s.debug_assertions, Some(true)); + assert_eq!(s.debug, Some(1)); +} + +#[cargo_test] +fn overlapping_env_with_defaults_errors_out() { + // Issue where one key is a prefix of another. + // This is a limitation of mapping environment variables on to a hierarchy. + // Check that we error out when we hit ambiguity in this way, rather than + // the more-surprising defaulting through. + // If, in the future, we can handle this more correctly, feel free to delete + // this test. + #[derive(Deserialize, Default)] + #[serde(default, rename_all = "kebab-case")] + struct Ambig { + debug: u32, + debug_assertions: bool, + } + let config = ConfigBuilder::new() + .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true") + .build(); + let err = config.get::("ambig").err().unwrap(); + assert!(format!("{}", err).contains("missing config key `ambig.debug`")); + + let config = ConfigBuilder::new().env("CARGO_AMBIG_DEBUG", "5").build(); + let s: Ambig = config.get("ambig").unwrap(); + assert_eq!(s.debug_assertions, bool::default()); + assert_eq!(s.debug, 5); + + let config = ConfigBuilder::new() + .env("CARGO_AMBIG_DEBUG", "1") + .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true") + .build(); + let s: Ambig = config.get("ambig").unwrap(); + assert_eq!(s.debug_assertions, true); + assert_eq!(s.debug, 1); +} + +#[cargo_test] +fn struct_with_overlapping_inner_struct_and_defaults() { + // Struct with serde defaults. + // Check that can be defined with environment variable. + #[derive(Deserialize, Default)] + #[serde(default)] + struct Inner { + value: i32, + } + + // Containing struct with a prefix of inner + // + // This is a limitation of mapping environment variables on to a hierarchy. + // Check that we error out when we hit ambiguity in this way, rather than + // the more-surprising defaulting through. + // If, in the future, we can handle this more correctly, feel free to delete + // this case. + #[derive(Deserialize, Default)] + #[serde(default)] + struct PrefixContainer { + inn: bool, + inner: Inner, + } + let config = ConfigBuilder::new() + .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12") + .build(); + let err = config + .get::("prefixcontainer") + .err() + .unwrap(); + assert!(format!("{}", err).contains("missing config key `prefixcontainer.inn`")); + let config = ConfigBuilder::new() + .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12") + .env("CARGO_PREFIXCONTAINER_INN", "true") + .build(); + let f: PrefixContainer = config.get("prefixcontainer").unwrap(); + assert_eq!(f.inner.value, 12); + assert_eq!(f.inn, true); + + // Containing struct where the inner value's field is a prefix of another + // + // This is a limitation of mapping environment variables on to a hierarchy. + // Check that we error out when we hit ambiguity in this way, rather than + // the more-surprising defaulting through. + // If, in the future, we can handle this more correctly, feel free to delete + // this case. + #[derive(Deserialize, Default)] + #[serde(default)] + struct InversePrefixContainer { + inner_field: bool, + inner: Inner, + } + let config = ConfigBuilder::new() + .env("CARGO_INVERSEPREFIXCONTAINER_INNER_VALUE", "12") + .build(); + let f: InversePrefixContainer = config.get("inverseprefixcontainer").unwrap(); + assert_eq!(f.inner_field, bool::default()); + assert_eq!(f.inner.value, 12); +} + +#[cargo_test] +fn string_list_tricky_env() { + // Make sure StringList handles typed env values. + let config = ConfigBuilder::new() + .env("CARGO_KEY1", "123") + .env("CARGO_KEY2", "true") + .env("CARGO_KEY3", "1 2") + .build(); + let x = config.get::("key1").unwrap(); + assert_eq!(x.as_slice(), &["123".to_string()]); + let x = config.get::("key2").unwrap(); + assert_eq!(x.as_slice(), &["true".to_string()]); + let x = config.get::("key3").unwrap(); + assert_eq!(x.as_slice(), &["1".to_string(), "2".to_string()]); +} + +#[cargo_test] +fn string_list_wrong_type() { + // What happens if StringList is given then wrong type. + write_config("some_list = 123"); + let config = ConfigBuilder::new().build(); + assert_error( + config.get::("some_list").unwrap_err(), + "\ +invalid configuration for key `some_list` +expected a string or array of strings, but found a integer for `some_list` in [..]/.cargo/config", + ); + + write_config("some_list = \"1 2\""); + let config = ConfigBuilder::new().build(); + let x = config.get::("some_list").unwrap(); + assert_eq!(x.as_slice(), &["1".to_string(), "2".to_string()]); +} + +#[cargo_test] +fn string_list_advanced_env() { + // StringList with advanced env. + let config = ConfigBuilder::new() + .unstable_flag("advanced-env") + .env("CARGO_KEY1", "[]") + .env("CARGO_KEY2", "['1 2', '3']") + .env("CARGO_KEY3", "[123]") + .build(); + let x = config.get::("key1").unwrap(); + assert_eq!(x.as_slice(), &[] as &[String]); + let x = config.get::("key2").unwrap(); + assert_eq!(x.as_slice(), &["1 2".to_string(), "3".to_string()]); + assert_error( + config.get::("key3").unwrap_err(), + "error in environment variable `CARGO_KEY3`: expected string, found integer", + ); +} + +#[cargo_test] +fn parse_strip_with_string() { + write_config( + "\ +[profile.release] +strip = 'debuginfo' +", + ); + + let config = new_config(); + + let p: cargo_toml::TomlProfile = config.get("profile.release").unwrap(); + let strip = p.strip.unwrap(); + assert_eq!( + strip, + cargo_toml::StringOrBool::String("debuginfo".to_string()) + ); +} + +#[cargo_test] +fn cargo_target_empty_cfg() { + write_config( + "\ +[build] +target-dir = '' +", + ); + + let config = new_config(); + + assert_error( + config.target_dir().unwrap_err(), + "the target directory is set to an empty string in [..]/.cargo/config", + ); +} + +#[cargo_test] +fn cargo_target_empty_env() { + let project = project().build(); + + project.cargo("check") + .env("CARGO_TARGET_DIR", "") + .with_stderr("error: the target directory is set to an empty string in the `CARGO_TARGET_DIR` environment variable") + .with_status(101) + .run() +} + +#[cargo_test] +fn all_profile_options() { + // Check that all profile options can be serialized/deserialized. + let base_settings = cargo_toml::TomlProfile { + opt_level: Some(cargo_toml::TomlOptLevel("0".to_string())), + lto: Some(cargo_toml::StringOrBool::String("thin".to_string())), + codegen_backend: Some(InternedString::new("example")), + codegen_units: Some(123), + debug: Some(cargo_toml::U32OrBool::U32(1)), + split_debuginfo: Some("packed".to_string()), + debug_assertions: Some(true), + rpath: Some(true), + panic: Some("abort".to_string()), + overflow_checks: Some(true), + incremental: Some(true), + dir_name: Some(InternedString::new("dir_name")), + inherits: Some(InternedString::new("debug")), + strip: Some(cargo_toml::StringOrBool::String("symbols".to_string())), + package: None, + build_override: None, + rustflags: None, + }; + let mut overrides = BTreeMap::new(); + let key = cargo_toml::ProfilePackageSpec::Spec(PackageIdSpec::parse("foo").unwrap()); + overrides.insert(key, base_settings.clone()); + let profile = cargo_toml::TomlProfile { + build_override: Some(Box::new(base_settings.clone())), + package: Some(overrides), + ..base_settings + }; + let profile_toml = toml::to_string(&profile).unwrap(); + let roundtrip: cargo_toml::TomlProfile = toml::from_str(&profile_toml).unwrap(); + let roundtrip_toml = toml::to_string(&roundtrip).unwrap(); + compare::assert_match_exact(&profile_toml, &roundtrip_toml); +} + +#[cargo_test] +fn value_in_array() { + // Value in an array should work + let root_path = paths::root().join(".cargo/config.toml"); + write_config_at( + &root_path, + "\ +[net.ssh] +known-hosts = [ + \"example.com ...\", + \"example.net ...\", +] +", + ); + + let foo_path = paths::root().join("foo/.cargo/config.toml"); + write_config_at( + &foo_path, + "\ +[net.ssh] +known-hosts = [ + \"example.org ...\", +] +", + ); + + let config = ConfigBuilder::new() + .cwd("foo") + // environment variables don't actually work for known-hosts due to + // space splitting, but this is included here just to validate that + // they work (particularly if other Vec config vars are added + // in the future). + .env("CARGO_NET_SSH_KNOWN_HOSTS", "env-example") + .build(); + let net_config = config.net_config().unwrap(); + let kh = net_config + .ssh + .as_ref() + .unwrap() + .known_hosts + .as_ref() + .unwrap(); + assert_eq!(kh.len(), 4); + assert_eq!(kh[0].val, "example.org ..."); + assert_eq!(kh[0].definition, Definition::Path(foo_path.clone())); + assert_eq!(kh[1].val, "example.com ..."); + assert_eq!(kh[1].definition, Definition::Path(root_path.clone())); + assert_eq!(kh[2].val, "example.net ..."); + assert_eq!(kh[2].definition, Definition::Path(root_path.clone())); + assert_eq!(kh[3].val, "env-example"); + assert_eq!( + kh[3].definition, + Definition::Environment("CARGO_NET_SSH_KNOWN_HOSTS".to_string()) + ); +} diff --git a/tests/testsuite/config_cli.rs b/tests/testsuite/config_cli.rs new file mode 100644 index 0000000..1120e27 --- /dev/null +++ b/tests/testsuite/config_cli.rs @@ -0,0 +1,564 @@ +//! Tests for the --config CLI option. + +use super::config::{ + assert_error, assert_match, read_output, write_config, write_config_at, ConfigBuilder, +}; +use cargo::util::config::Definition; +use cargo_test_support::paths; +use std::{collections::HashMap, fs}; + +#[cargo_test] +fn basic() { + // Simple example. + let config = ConfigBuilder::new().config_arg("foo='bar'").build(); + assert_eq!(config.get::("foo").unwrap(), "bar"); +} + +#[cargo_test] +fn cli_priority() { + // Command line takes priority over files and env vars. + write_config( + " + demo_list = ['a'] + [build] + jobs = 3 + rustc = 'file' + [term] + quiet = false + verbose = false + ", + ); + let config = ConfigBuilder::new().build(); + assert_eq!(config.get::("build.jobs").unwrap(), 3); + assert_eq!(config.get::("build.rustc").unwrap(), "file"); + assert_eq!(config.get::("term.quiet").unwrap(), false); + assert_eq!(config.get::("term.verbose").unwrap(), false); + + let config = ConfigBuilder::new() + .env("CARGO_BUILD_JOBS", "2") + .env("CARGO_BUILD_RUSTC", "env") + .env("CARGO_TERM_VERBOSE", "false") + .config_arg("build.jobs=1") + .config_arg("build.rustc='cli'") + .config_arg("term.verbose=true") + .build(); + assert_eq!(config.get::("build.jobs").unwrap(), 1); + assert_eq!(config.get::("build.rustc").unwrap(), "cli"); + assert_eq!(config.get::("term.verbose").unwrap(), true); + + // Setting both term.verbose and term.quiet is invalid and is tested + // in the run test suite. + let config = ConfigBuilder::new() + .env("CARGO_TERM_QUIET", "false") + .config_arg("term.quiet=true") + .build(); + assert_eq!(config.get::("term.quiet").unwrap(), true); +} + +#[cargo_test] +fn merge_primitives_for_multiple_cli_occurrences() { + let config_path0 = ".cargo/file0.toml"; + write_config_at(config_path0, "k = 'file0'"); + let config_path1 = ".cargo/file1.toml"; + write_config_at(config_path1, "k = 'file1'"); + + // k=env0 + let config = ConfigBuilder::new().env("CARGO_K", "env0").build(); + assert_eq!(config.get::("k").unwrap(), "env0"); + + // k=env0 + // --config k='cli0' + // --config k='cli1' + let config = ConfigBuilder::new() + .env("CARGO_K", "env0") + .config_arg("k='cli0'") + .config_arg("k='cli1'") + .build(); + assert_eq!(config.get::("k").unwrap(), "cli1"); + + // Env has a lower priority when comparing with file from CLI arg. + // + // k=env0 + // --config k='cli0' + // --config k='cli1' + // --config .cargo/file0.toml + let config = ConfigBuilder::new() + .env("CARGO_K", "env0") + .config_arg("k='cli0'") + .config_arg("k='cli1'") + .config_arg(config_path0) + .build(); + assert_eq!(config.get::("k").unwrap(), "file0"); + + // k=env0 + // --config k='cli0' + // --config k='cli1' + // --config .cargo/file0.toml + // --config k='cli2' + let config = ConfigBuilder::new() + .env("CARGO_K", "env0") + .config_arg("k='cli0'") + .config_arg("k='cli1'") + .config_arg(config_path0) + .config_arg("k='cli2'") + .build(); + assert_eq!(config.get::("k").unwrap(), "cli2"); + + // k=env0 + // --config k='cli0' + // --config k='cli1' + // --config .cargo/file0.toml + // --config k='cli2' + // --config .cargo/file1.toml + let config = ConfigBuilder::new() + .env("CARGO_K", "env0") + .config_arg("k='cli0'") + .config_arg("k='cli1'") + .config_arg(config_path0) + .config_arg("k='cli2'") + .config_arg(config_path1) + .build(); + assert_eq!(config.get::("k").unwrap(), "file1"); +} + +#[cargo_test] +fn merges_array() { + // Array entries are appended. + write_config( + " + [build] + rustflags = ['--file'] + ", + ); + let config = ConfigBuilder::new() + .config_arg("build.rustflags = ['--cli']") + .build(); + assert_eq!( + config.get::>("build.rustflags").unwrap(), + ["--file", "--cli"] + ); + + // With normal env. + let config = ConfigBuilder::new() + .env("CARGO_BUILD_RUSTFLAGS", "--env1 --env2") + .config_arg("build.rustflags = ['--cli']") + .build(); + // The order of cli/env is a little questionable here, but would require + // much more complex merging logic. + assert_eq!( + config.get::>("build.rustflags").unwrap(), + ["--file", "--cli", "--env1", "--env2"] + ); + + // With advanced-env. + let config = ConfigBuilder::new() + .unstable_flag("advanced-env") + .env("CARGO_BUILD_RUSTFLAGS", "--env") + .config_arg("build.rustflags = ['--cli']") + .build(); + assert_eq!( + config.get::>("build.rustflags").unwrap(), + ["--file", "--cli", "--env"] + ); + + // Merges multiple instances. + let config = ConfigBuilder::new() + .config_arg("build.rustflags=['--one']") + .config_arg("build.rustflags=['--two']") + .build(); + assert_eq!( + config.get::>("build.rustflags").unwrap(), + ["--file", "--one", "--two"] + ); +} + +#[cargo_test] +fn string_list_array() { + // Using the StringList type. + write_config( + " + [build] + rustflags = ['--file'] + ", + ); + let config = ConfigBuilder::new() + .config_arg("build.rustflags = ['--cli']") + .build(); + assert_eq!( + config + .get::("build.rustflags") + .unwrap() + .as_slice(), + ["--file", "--cli"] + ); + + // With normal env. + let config = ConfigBuilder::new() + .env("CARGO_BUILD_RUSTFLAGS", "--env1 --env2") + .config_arg("build.rustflags = ['--cli']") + .build(); + assert_eq!( + config + .get::("build.rustflags") + .unwrap() + .as_slice(), + ["--file", "--cli", "--env1", "--env2"] + ); + + // With advanced-env. + let config = ConfigBuilder::new() + .unstable_flag("advanced-env") + .env("CARGO_BUILD_RUSTFLAGS", "['--env']") + .config_arg("build.rustflags = ['--cli']") + .build(); + assert_eq!( + config + .get::("build.rustflags") + .unwrap() + .as_slice(), + ["--file", "--cli", "--env"] + ); +} + +#[cargo_test] +fn merges_table() { + // Tables are merged. + write_config( + " + [foo] + key1 = 1 + key2 = 2 + key3 = 3 + ", + ); + let config = ConfigBuilder::new() + .config_arg("foo.key2 = 4") + .config_arg("foo.key3 = 5") + .config_arg("foo.key4 = 6") + .build(); + assert_eq!(config.get::("foo.key1").unwrap(), 1); + assert_eq!(config.get::("foo.key2").unwrap(), 4); + assert_eq!(config.get::("foo.key3").unwrap(), 5); + assert_eq!(config.get::("foo.key4").unwrap(), 6); + + // With env. + let config = ConfigBuilder::new() + .env("CARGO_FOO_KEY3", "7") + .env("CARGO_FOO_KEY4", "8") + .env("CARGO_FOO_KEY5", "9") + .config_arg("foo.key2 = 4") + .config_arg("foo.key3 = 5") + .config_arg("foo.key4 = 6") + .build(); + assert_eq!(config.get::("foo.key1").unwrap(), 1); + assert_eq!(config.get::("foo.key2").unwrap(), 4); + assert_eq!(config.get::("foo.key3").unwrap(), 5); + assert_eq!(config.get::("foo.key4").unwrap(), 6); + assert_eq!(config.get::("foo.key5").unwrap(), 9); +} + +#[cargo_test] +fn merge_array_mixed_def_paths() { + // Merging of arrays with different def sites. + write_config( + " + paths = ['file'] + ", + ); + // Create a directory for CWD to differentiate the paths. + let somedir = paths::root().join("somedir"); + fs::create_dir(&somedir).unwrap(); + let config = ConfigBuilder::new() + .cwd(&somedir) + .config_arg("paths=['cli']") + // env is currently ignored for get_list() + .env("CARGO_PATHS", "env") + .build(); + let paths = config.get_list("paths").unwrap().unwrap(); + // The definition for the root value is somewhat arbitrary, but currently starts with the file because that is what is loaded first. + assert_eq!(paths.definition, Definition::Path(paths::root())); + assert_eq!(paths.val.len(), 2); + assert_eq!(paths.val[0].0, "file"); + assert_eq!(paths.val[0].1.root(&config), paths::root()); + assert_eq!(paths.val[1].0, "cli"); + assert_eq!(paths.val[1].1.root(&config), somedir); +} + +#[cargo_test] +fn enforces_format() { + // These dotted key expressions should all be fine. + let config = ConfigBuilder::new() + .config_arg("a=true") + .config_arg(" b.a = true ") + .config_arg("c.\"b\".'a'=true") + .config_arg("d.\"=\".'='=true") + .config_arg("e.\"'\".'\"'=true") + .build(); + assert_eq!(config.get::("a").unwrap(), true); + assert_eq!( + config.get::>("b").unwrap(), + HashMap::from([("a".to_string(), true)]) + ); + assert_eq!( + config + .get::>>("c") + .unwrap(), + HashMap::from([("b".to_string(), HashMap::from([("a".to_string(), true)]))]) + ); + assert_eq!( + config + .get::>>("d") + .unwrap(), + HashMap::from([("=".to_string(), HashMap::from([("=".to_string(), true)]))]) + ); + assert_eq!( + config + .get::>>("e") + .unwrap(), + HashMap::from([("'".to_string(), HashMap::from([("\"".to_string(), true)]))]) + ); + + // But anything that's not a dotted key expression should be disallowed. + let _ = ConfigBuilder::new() + .config_arg("[a] foo=true") + .build_err() + .unwrap_err(); + let _ = ConfigBuilder::new() + .config_arg("a = true\nb = true") + .build_err() + .unwrap_err(); + + // We also disallow overwriting with tables since it makes merging unclear. + let _ = ConfigBuilder::new() + .config_arg("a = { first = true, second = false }") + .build_err() + .unwrap_err(); + let _ = ConfigBuilder::new() + .config_arg("a = { first = true }") + .build_err() + .unwrap_err(); +} + +#[cargo_test] +fn unused_key() { + // Unused key passed on command line. + let config = ConfigBuilder::new().config_arg("build.unused = 2").build(); + + config.build_config().unwrap(); + let output = read_output(config); + let expected = "\ +warning: unused config key `build.unused` in `--config cli option` +"; + assert_match(expected, &output); +} + +#[cargo_test] +fn rerooted_remains() { + // Re-rooting keeps cli args. + let somedir = paths::root().join("somedir"); + fs::create_dir_all(somedir.join(".cargo")).unwrap(); + fs::write( + somedir.join(".cargo").join("config"), + " + a = 'file1' + b = 'file2' + ", + ) + .unwrap(); + let mut config = ConfigBuilder::new() + .cwd(&somedir) + .config_arg("b='cli1'") + .config_arg("c='cli2'") + .build(); + assert_eq!(config.get::("a").unwrap(), "file1"); + assert_eq!(config.get::("b").unwrap(), "cli1"); + assert_eq!(config.get::("c").unwrap(), "cli2"); + + config.reload_rooted_at(paths::root()).unwrap(); + + assert_eq!(config.get::>("a").unwrap(), None); + assert_eq!(config.get::("b").unwrap(), "cli1"); + assert_eq!(config.get::("c").unwrap(), "cli2"); +} + +#[cargo_test] +fn bad_parse() { + // Fail to TOML parse. + let config = ConfigBuilder::new().config_arg("abc").build_err(); + assert_error( + config.unwrap_err(), + "\ +failed to parse value from --config argument `abc` as a dotted key expression + +Caused by: + TOML parse error at line 1, column 4 + | +1 | abc + | ^ +expected `.`, `=` +", + ); + + let config = ConfigBuilder::new().config_arg("").build_err(); + assert_error( + config.unwrap_err(), + "--config argument `` was not a TOML dotted key expression (such as `build.jobs = 2`)", + ); +} + +#[cargo_test] +fn too_many_values() { + // Currently restricted to only 1 value. + let config = ConfigBuilder::new().config_arg("a=1\nb=2").build_err(); + assert_error( + config.unwrap_err(), + "\ +--config argument `a=1 +b=2` was not a TOML dotted key expression (such as `build.jobs = 2`)", + ); +} + +#[cargo_test] +fn no_disallowed_values() { + let config = ConfigBuilder::new() + .config_arg("registry.token=\"hello\"") + .build_err(); + assert_error( + config.unwrap_err(), + "registry.token cannot be set through --config for security reasons", + ); + let config = ConfigBuilder::new() + .config_arg("registries.crates-io.token=\"hello\"") + .build_err(); + assert_error( + config.unwrap_err(), + "registries.crates-io.token cannot be set through --config for security reasons", + ); + let config = ConfigBuilder::new() + .config_arg("registry.secret-key=\"hello\"") + .build_err(); + assert_error( + config.unwrap_err(), + "registry.secret-key cannot be set through --config for security reasons", + ); + let config = ConfigBuilder::new() + .config_arg("registries.crates-io.secret-key=\"hello\"") + .build_err(); + assert_error( + config.unwrap_err(), + "registries.crates-io.secret-key cannot be set through --config for security reasons", + ); +} + +#[cargo_test] +fn no_inline_table_value() { + // Disallow inline tables + let config = ConfigBuilder::new() + .config_arg("a.b={c = \"d\"}") + .build_err(); + assert_error( + config.unwrap_err(), + "--config argument `a.b={c = \"d\"}` sets a value to an inline table, which is not accepted" + ); +} + +#[cargo_test] +fn no_array_of_tables_values() { + // Disallow array-of-tables when not in dotted form + let config = ConfigBuilder::new() + .config_arg("[[a.b]]\nc = \"d\"") + .build_err(); + assert_error( + config.unwrap_err(), + "\ +--config argument `[[a.b]] +c = \"d\"` was not a TOML dotted key expression (such as `build.jobs = 2`)", + ); +} + +#[cargo_test] +fn no_comments() { + // Disallow comments in dotted form. + let config = ConfigBuilder::new() + .config_arg("a.b = \"c\" # exactly") + .build_err(); + assert_error( + config.unwrap_err(), + "\ +--config argument `a.b = \"c\" # exactly` includes non-whitespace decoration", + ); + + let config = ConfigBuilder::new() + .config_arg("# exactly\na.b = \"c\"") + .build_err(); + assert_error( + config.unwrap_err(), + "\ +--config argument `# exactly\na.b = \"c\"` includes non-whitespace decoration", + ); +} + +#[cargo_test] +fn bad_cv_convert() { + // ConfigValue does not support all TOML types. + let config = ConfigBuilder::new().config_arg("a=2019-12-01").build_err(); + assert_error( + config.unwrap_err(), + "\ +failed to convert --config argument `a=2019-12-01` + +Caused by: + failed to parse key `a` + +Caused by: + found TOML configuration value of unknown type `datetime`", + ); +} + +#[cargo_test] +fn fail_to_merge_multiple_args() { + // Error message when multiple args fail to merge. + let config = ConfigBuilder::new() + .config_arg("foo='a'") + .config_arg("foo=['a']") + .build_err(); + // This is a little repetitive, but hopefully the user can figure it out. + assert_error( + config.unwrap_err(), + "\ +failed to merge --config argument `foo=['a']` + +Caused by: + failed to merge key `foo` between --config cli option and --config cli option + +Caused by: + failed to merge config value from `--config cli option` into `--config cli option`: \ + expected string, but found array", + ); +} + +#[cargo_test] +fn cli_path() { + // --config path_to_file + fs::write(paths::root().join("myconfig.toml"), "key = 123").unwrap(); + let config = ConfigBuilder::new() + .cwd(paths::root()) + .config_arg("myconfig.toml") + .build(); + assert_eq!(config.get::("key").unwrap(), 123); + + let config = ConfigBuilder::new().config_arg("missing.toml").build_err(); + assert_error( + config.unwrap_err(), + "\ +failed to parse value from --config argument `missing.toml` as a dotted key expression + +Caused by: + TOML parse error at line 1, column 13 + | +1 | missing.toml + | ^ +expected `.`, `=` +", + ); +} diff --git a/tests/testsuite/config_include.rs b/tests/testsuite/config_include.rs new file mode 100644 index 0000000..ae56806 --- /dev/null +++ b/tests/testsuite/config_include.rs @@ -0,0 +1,285 @@ +//! Tests for `include` config field. + +use super::config::{assert_error, write_config, write_config_at, ConfigBuilder}; +use cargo_test_support::{no_such_file_err_msg, project}; + +#[cargo_test] +fn gated() { + // Requires -Z flag. + write_config("include='other'"); + write_config_at( + ".cargo/other", + " + othervalue = 1 + ", + ); + let config = ConfigBuilder::new().build(); + assert_eq!(config.get::>("othervalue").unwrap(), None); + let config = ConfigBuilder::new().unstable_flag("config-include").build(); + assert_eq!(config.get::("othervalue").unwrap(), 1); +} + +#[cargo_test] +fn simple() { + // Simple test. + write_config_at( + ".cargo/config", + " + include = 'other' + key1 = 1 + key2 = 2 + ", + ); + write_config_at( + ".cargo/other", + " + key2 = 3 + key3 = 4 + ", + ); + let config = ConfigBuilder::new().unstable_flag("config-include").build(); + assert_eq!(config.get::("key1").unwrap(), 1); + assert_eq!(config.get::("key2").unwrap(), 2); + assert_eq!(config.get::("key3").unwrap(), 4); +} + +#[cargo_test] +fn works_with_cli() { + write_config_at( + ".cargo/config.toml", + " + include = 'other.toml' + [build] + rustflags = ['-W', 'unused'] + ", + ); + write_config_at( + ".cargo/other.toml", + " + [build] + rustflags = ['-W', 'unsafe-code'] + ", + ); + let p = project().file("src/lib.rs", "").build(); + p.cargo("check -v") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 [..] +[RUNNING] `rustc [..]-W unused` +[FINISHED] [..] +", + ) + .run(); + p.cargo("check -v -Z config-include") + .masquerade_as_nightly_cargo(&["config-include"]) + .with_stderr( + "\ +[DIRTY] foo v0.0.1 ([..]): the rustflags changed +[CHECKING] foo v0.0.1 [..] +[RUNNING] `rustc [..]-W unsafe-code -W unused` +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn left_to_right() { + // How it merges multiple includes. + write_config_at( + ".cargo/config", + " + include = ['one', 'two'] + primary = 1 + ", + ); + write_config_at( + ".cargo/one", + " + one = 1 + primary = 2 + ", + ); + write_config_at( + ".cargo/two", + " + two = 2 + primary = 3 + ", + ); + let config = ConfigBuilder::new().unstable_flag("config-include").build(); + assert_eq!(config.get::("primary").unwrap(), 1); + assert_eq!(config.get::("one").unwrap(), 1); + assert_eq!(config.get::("two").unwrap(), 2); +} + +#[cargo_test] +fn missing_file() { + // Error when there's a missing file. + write_config("include='missing'"); + let config = ConfigBuilder::new() + .unstable_flag("config-include") + .build_err(); + assert_error( + config.unwrap_err(), + &format!( + "\ +could not load Cargo configuration + +Caused by: + failed to load config include `missing` from `[..]/.cargo/config` + +Caused by: + failed to read configuration file `[..]/.cargo/missing` + +Caused by: + {}", + no_such_file_err_msg() + ), + ); +} + +#[cargo_test] +fn cycle() { + // Detects a cycle. + write_config_at(".cargo/config", "include='one'"); + write_config_at(".cargo/one", "include='two'"); + write_config_at(".cargo/two", "include='config'"); + let config = ConfigBuilder::new() + .unstable_flag("config-include") + .build_err(); + assert_error( + config.unwrap_err(), + "\ +could not load Cargo configuration + +Caused by: + failed to load config include `one` from `[..]/.cargo/config` + +Caused by: + failed to load config include `two` from `[..]/.cargo/one` + +Caused by: + failed to load config include `config` from `[..]/.cargo/two` + +Caused by: + config `include` cycle detected with path `[..]/.cargo/config`", + ); +} + +#[cargo_test] +fn cli_include() { + // Using --config with include. + // CLI takes priority over files. + write_config_at( + ".cargo/config", + " + foo = 1 + bar = 2 + ", + ); + write_config_at(".cargo/config-foo", "foo = 2"); + let config = ConfigBuilder::new() + .unstable_flag("config-include") + .config_arg("include='.cargo/config-foo'") + .build(); + assert_eq!(config.get::("foo").unwrap(), 2); + assert_eq!(config.get::("bar").unwrap(), 2); +} + +#[cargo_test] +fn bad_format() { + // Not a valid format. + write_config("include = 1"); + let config = ConfigBuilder::new() + .unstable_flag("config-include") + .build_err(); + assert_error( + config.unwrap_err(), + "\ +could not load Cargo configuration + +Caused by: + `include` expected a string or list, but found integer in `[..]/.cargo/config`", + ); +} + +#[cargo_test] +fn cli_include_failed() { + // Error message when CLI include fails to load. + let config = ConfigBuilder::new() + .unstable_flag("config-include") + .config_arg("include='foobar'") + .build_err(); + assert_error( + config.unwrap_err(), + &format!( + "\ +failed to load --config include + +Caused by: + failed to load config include `foobar` from `--config cli option` + +Caused by: + failed to read configuration file `[..]/foobar` + +Caused by: + {}", + no_such_file_err_msg() + ), + ); +} + +#[cargo_test] +fn cli_merge_failed() { + // Error message when CLI include merge fails. + write_config("foo = ['a']"); + write_config_at( + ".cargo/other", + " + foo = 'b' + ", + ); + let config = ConfigBuilder::new() + .unstable_flag("config-include") + .config_arg("include='.cargo/other'") + .build_err(); + // Maybe this error message should mention it was from an include file? + assert_error( + config.unwrap_err(), + "\ +failed to merge --config key `foo` into `[..]/.cargo/config` + +Caused by: + failed to merge config value from `[..]/.cargo/other` into `[..]/.cargo/config`: \ + expected array, but found string", + ); +} + +#[cargo_test] +fn cli_include_take_priority_over_env() { + write_config_at(".cargo/include.toml", "k='include'"); + + // k=env + let config = ConfigBuilder::new().env("CARGO_K", "env").build(); + assert_eq!(config.get::("k").unwrap(), "env"); + + // k=env + // --config 'include=".cargo/include.toml"' + let config = ConfigBuilder::new() + .env("CARGO_K", "env") + .unstable_flag("config-include") + .config_arg("include='.cargo/include.toml'") + .build(); + assert_eq!(config.get::("k").unwrap(), "include"); + + // k=env + // --config '.cargo/foo.toml' + write_config_at(".cargo/foo.toml", "include='include.toml'"); + let config = ConfigBuilder::new() + .env("CARGO_K", "env") + .unstable_flag("config-include") + .config_arg(".cargo/foo.toml") + .build(); + assert_eq!(config.get::("k").unwrap(), "include"); +} diff --git a/tests/testsuite/corrupt_git.rs b/tests/testsuite/corrupt_git.rs new file mode 100644 index 0000000..2569e46 --- /dev/null +++ b/tests/testsuite/corrupt_git.rs @@ -0,0 +1,159 @@ +//! Tests for corrupt git repos. + +use cargo_test_support::paths; +use cargo_test_support::{basic_manifest, git, project}; +use cargo_util::paths as cargopaths; +use std::fs; +use std::path::{Path, PathBuf}; + +#[cargo_test] +fn deleting_database_files() { + let project = project(); + let git_project = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "") + }); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + bar = {{ git = '{}' }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + project.cargo("check").run(); + + let mut files = Vec::new(); + find_files(&paths::home().join(".cargo/git/db"), &mut files); + assert!(!files.is_empty()); + + let log = "cargo::sources::git=trace"; + for file in files { + if !file.exists() { + continue; + } + println!("deleting {}", file.display()); + cargopaths::remove_file(&file).unwrap(); + project.cargo("check -v").env("CARGO_LOG", log).run(); + + if !file.exists() { + continue; + } + println!("truncating {}", file.display()); + make_writable(&file); + fs::OpenOptions::new() + .write(true) + .open(&file) + .unwrap() + .set_len(2) + .unwrap(); + project.cargo("check -v").env("CARGO_LOG", log).run(); + } +} + +#[cargo_test] +fn deleting_checkout_files() { + let project = project(); + let git_project = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "") + }); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + bar = {{ git = '{}' }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + project.cargo("check").run(); + + let dir = paths::home() + .join(".cargo/git/checkouts") + // get the first entry in the checkouts dir for the package's location + .read_dir() + .unwrap() + .next() + .unwrap() + .unwrap() + .path() + // get the first child of that checkout dir for our checkout + .read_dir() + .unwrap() + .next() + .unwrap() + .unwrap() + .path() + // and throw on .git to corrupt things + .join(".git"); + let mut files = Vec::new(); + find_files(&dir, &mut files); + assert!(!files.is_empty()); + + let log = "cargo::sources::git=trace"; + for file in files { + if !file.exists() { + continue; + } + println!("deleting {}", file.display()); + cargopaths::remove_file(&file).unwrap(); + project.cargo("check -v").env("CARGO_LOG", log).run(); + + if !file.exists() { + continue; + } + println!("truncating {}", file.display()); + make_writable(&file); + fs::OpenOptions::new() + .write(true) + .open(&file) + .unwrap() + .set_len(2) + .unwrap(); + project.cargo("check -v").env("CARGO_LOG", log).run(); + } +} + +fn make_writable(path: &Path) { + let mut p = path.metadata().unwrap().permissions(); + p.set_readonly(false); + fs::set_permissions(path, p).unwrap(); +} + +fn find_files(path: &Path, dst: &mut Vec) { + for e in path.read_dir().unwrap() { + let e = e.unwrap(); + let path = e.path(); + if e.file_type().unwrap().is_dir() { + find_files(&path, dst); + } else { + dst.push(path); + } + } +} diff --git a/tests/testsuite/credential_process.rs b/tests/testsuite/credential_process.rs new file mode 100644 index 0000000..0d174b6 --- /dev/null +++ b/tests/testsuite/credential_process.rs @@ -0,0 +1,495 @@ +//! Tests for credential-process. + +use cargo_test_support::registry::TestRegistry; +use cargo_test_support::{basic_manifest, cargo_process, paths, project, registry, Project}; +use std::fs::{self, read_to_string}; + +fn toml_bin(proj: &Project, name: &str) -> String { + proj.bin(name).display().to_string().replace('\\', "\\\\") +} + +#[cargo_test] +fn gated() { + let _alternative = registry::RegistryBuilder::new() + .alternative() + .no_configure_token() + .build(); + + let cratesio = registry::RegistryBuilder::new() + .no_configure_token() + .build(); + + let p = project() + .file( + ".cargo/config", + r#" + [registry] + credential-process = "false" + "#, + ) + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(cratesio.index_url()) + .masquerade_as_nightly_cargo(&["credential-process"]) + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] no token found, please run `cargo login` +or use environment variable CARGO_REGISTRY_TOKEN +", + ) + .run(); + + p.change_file( + ".cargo/config", + r#" + [registry.alternative] + credential-process = "false" + "#, + ); + + p.cargo("publish --no-verify --registry alternative") + .masquerade_as_nightly_cargo(&["credential-process"]) + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] no token found for `alternative`, please run `cargo login --registry alternative` +or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN +", + ) + .run(); +} + +#[cargo_test] +fn warn_both_token_and_process() { + // Specifying both credential-process and a token in config should issue a warning. + let _server = registry::RegistryBuilder::new() + .http_api() + .http_index() + .alternative() + .no_configure_token() + .build(); + let p = project() + .file( + ".cargo/config", + r#" + [registries.alternative] + token = "alternative-sekrit" + credential-process = "false" + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + description = "foo" + authors = [] + license = "MIT" + homepage = "https://example.com/" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --registry alternative -Z credential-process") + .masquerade_as_nightly_cargo(&["credential-process"]) + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] both `token` and `credential-process` were specified in the config for registry `alternative`. +Only one of these values may be set, remove one or the other to proceed. +", + ) + .run(); + + // Try with global credential-process, and registry-specific `token`. + // This should silently use the config token, and not run the "false" exe. + p.change_file( + ".cargo/config", + r#" + [registry] + credential-process = "false" + + [registries.alternative] + token = "alternative-sekrit" + "#, + ); + p.cargo("publish --no-verify --registry alternative -Z credential-process") + .masquerade_as_nightly_cargo(&["credential-process"]) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] +[UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] +", + ) + .run(); +} + +/// Setup for a test that will issue a command that needs to fetch a token. +/// +/// This does the following: +/// +/// * Spawn a thread that will act as an API server. +/// * Create a simple credential-process that will generate a fake token. +/// * Create a simple `foo` project to run the test against. +/// * Configure the credential-process config. +/// +/// Returns the simple `foo` project to test against and the API server handle. +fn get_token_test() -> (Project, TestRegistry) { + // API server that checks that the token is included correctly. + let server = registry::RegistryBuilder::new() + .no_configure_token() + .token(cargo_test_support::registry::Token::Plaintext( + "sekrit".to_string(), + )) + .alternative() + .http_api() + .build(); + // The credential process to use. + let cred_proj = project() + .at("cred_proj") + .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0")) + .file( + "src/main.rs", + r#" + use std::fs::File; + use std::io::Write; + fn main() { + let mut f = File::options() + .write(true) + .create(true) + .append(true) + .open("runs.log") + .unwrap(); + write!(f, "+"); + println!("sekrit"); + } "#, + ) + .build(); + cred_proj.cargo("build").run(); + + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [registries.alternative] + index = "{}" + credential-process = ["{}"] + "#, + server.index_url(), + toml_bin(&cred_proj, "test-cred") + ), + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + description = "foo" + authors = [] + license = "MIT" + homepage = "https://example.com/" + "#, + ) + .file("src/lib.rs", "") + .build(); + (p, server) +} + +#[cargo_test] +fn publish() { + // Checks that credential-process is used for `cargo publish`. + let (p, _t) = get_token_test(); + + p.cargo("publish --no-verify --registry alternative -Z credential-process") + .masquerade_as_nightly_cargo(&["credential-process"]) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] +[UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] +", + ) + .run(); + + let calls = read_to_string(p.root().join("runs.log")).unwrap().len(); + assert_eq!(calls, 1); +} + +#[cargo_test] +fn basic_unsupported() { + // Non-action commands don't support login/logout. + let registry = registry::RegistryBuilder::new() + .no_configure_token() + .build(); + cargo_util::paths::append( + &paths::home().join(".cargo/config"), + br#" + [registry] + credential-process = "false" + "#, + ) + .unwrap(); + + cargo_process("login -Z credential-process abcdefg") + .replace_crates_io(registry.index_url()) + .masquerade_as_nightly_cargo(&["credential-process"]) + .with_status(101) + .with_stderr( + "\ +[UPDATING] crates.io index +[ERROR] credential process `false` cannot be used to log in, \ +the credential-process configuration value must pass the \ +`{action}` argument in the config to support this command +", + ) + .run(); + + cargo_process("logout -Z credential-process") + .replace_crates_io(registry.index_url()) + .masquerade_as_nightly_cargo(&["credential-process", "cargo-logout"]) + .with_status(101) + .with_stderr( + "\ +[ERROR] credential process `false` cannot be used to log out, \ +the credential-process configuration value must pass the \ +`{action}` argument in the config to support this command +", + ) + .run(); +} + +#[cargo_test] +fn login() { + let server = registry::RegistryBuilder::new() + .no_configure_token() + .build(); + // The credential process to use. + let cred_proj = project() + .at("cred_proj") + .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0")) + .file( + "src/main.rs", + r#" + use std::io::Read; + + fn main() {{ + assert_eq!(std::env::var("CARGO_REGISTRY_NAME_OPT").unwrap(), "crates-io"); + assert_eq!(std::env::var("CARGO_REGISTRY_INDEX_URL").unwrap(), "https://github.com/rust-lang/crates.io-index"); + assert_eq!(std::env::args().skip(1).next().unwrap(), "store"); + let mut buffer = String::new(); + std::io::stdin().read_to_string(&mut buffer).unwrap(); + assert_eq!(buffer, "abcdefg\n"); + std::fs::write("token-store", buffer).unwrap(); + }} + "#, + ) + .build(); + cred_proj.cargo("build").run(); + + cargo_util::paths::append( + &paths::home().join(".cargo/config"), + format!( + r#" + [registry] + credential-process = ["{}", "{{action}}"] + "#, + toml_bin(&cred_proj, "test-cred") + ) + .as_bytes(), + ) + .unwrap(); + + cargo_process("login -Z credential-process abcdefg") + .masquerade_as_nightly_cargo(&["credential-process"]) + .replace_crates_io(server.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[LOGIN] token for `crates.io` saved +", + ) + .run(); + assert_eq!( + fs::read_to_string(paths::root().join("token-store")).unwrap(), + "abcdefg\n" + ); +} + +#[cargo_test] +fn logout() { + let server = registry::RegistryBuilder::new() + .no_configure_token() + .build(); + // The credential process to use. + let cred_proj = project() + .at("cred_proj") + .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0")) + .file( + "src/main.rs", + r#" + use std::io::Read; + + fn main() {{ + assert_eq!(std::env::var("CARGO_REGISTRY_NAME_OPT").unwrap(), "crates-io"); + assert_eq!(std::env::var("CARGO_REGISTRY_INDEX_URL").unwrap(), "https://github.com/rust-lang/crates.io-index"); + assert_eq!(std::env::args().skip(1).next().unwrap(), "erase"); + std::fs::write("token-store", "").unwrap(); + eprintln!("token for `crates-io` has been erased!") + }} + "#, + ) + .build(); + cred_proj.cargo("build").run(); + + cargo_util::paths::append( + &paths::home().join(".cargo/config"), + format!( + r#" + [registry] + credential-process = ["{}", "{{action}}"] + "#, + toml_bin(&cred_proj, "test-cred") + ) + .as_bytes(), + ) + .unwrap(); + + cargo_process("logout -Z credential-process") + .masquerade_as_nightly_cargo(&["credential-process", "cargo-logout"]) + .replace_crates_io(server.index_url()) + .with_stderr( + "\ +token for `crates-io` has been erased! +[LOGOUT] token for `crates-io` has been removed from local storage +", + ) + .run(); + assert_eq!( + fs::read_to_string(paths::root().join("token-store")).unwrap(), + "" + ); +} + +#[cargo_test] +fn yank() { + let (p, _t) = get_token_test(); + + p.cargo("yank --version 0.1.0 --registry alternative -Z credential-process") + .masquerade_as_nightly_cargo(&["credential-process"]) + .with_stderr( + "\ +[UPDATING] [..] +[YANK] foo@0.1.0 +", + ) + .run(); +} + +#[cargo_test] +fn owner() { + let (p, _t) = get_token_test(); + + p.cargo("owner --add username --registry alternative -Z credential-process") + .masquerade_as_nightly_cargo(&["credential-process"]) + .with_stderr( + "\ +[UPDATING] [..] +[OWNER] completed! +", + ) + .run(); +} + +#[cargo_test] +fn libexec_path() { + // cargo: prefixed names use the sysroot + let server = registry::RegistryBuilder::new() + .no_configure_token() + .build(); + cargo_util::paths::append( + &paths::home().join(".cargo/config"), + br#" + [registry] + credential-process = "cargo:doesnotexist" + "#, + ) + .unwrap(); + + cargo_process("login -Z credential-process abcdefg") + .masquerade_as_nightly_cargo(&["credential-process"]) + .replace_crates_io(server.index_url()) + .with_status(101) + .with_stderr( + // FIXME: Update "Caused by" error message once rust/pull/87704 is merged. + // On Windows, changing to a custom executable resolver has changed the + // error messages. + &format!("\ +[UPDATING] [..] +[ERROR] failed to execute `[..]libexec/cargo-credential-doesnotexist[EXE]` to store authentication token for registry `crates-io` + +Caused by: + [..] +"), + ) + .run(); +} + +#[cargo_test] +fn invalid_token_output() { + // Error when credential process does not output the expected format for a token. + let _server = registry::RegistryBuilder::new() + .alternative() + .no_configure_token() + .build(); + let cred_proj = project() + .at("cred_proj") + .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0")) + .file("src/main.rs", r#"fn main() { print!("a\nb\n"); } "#) + .build(); + cred_proj.cargo("build").run(); + + cargo_util::paths::append( + &paths::home().join(".cargo/config"), + format!( + r#" + [registry] + credential-process = ["{}"] + "#, + toml_bin(&cred_proj, "test-cred") + ) + .as_bytes(), + ) + .unwrap(); + + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --registry alternative -Z credential-process") + .masquerade_as_nightly_cargo(&["credential-process"]) + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] credential process `[..]test-cred[EXE]` returned more than one line of output; expected a single token +", + ) + .run(); +} diff --git a/tests/testsuite/cross_compile.rs b/tests/testsuite/cross_compile.rs new file mode 100644 index 0000000..cc96445 --- /dev/null +++ b/tests/testsuite/cross_compile.rs @@ -0,0 +1,1342 @@ +//! Tests for cross compiling with --target. +//! +//! See `cargo_test_support::cross_compile` for more detail. + +use cargo_test_support::rustc_host; +use cargo_test_support::{basic_bin_manifest, basic_manifest, cross_compile, project}; + +#[cargo_test] +fn simple_cross() { + if cross_compile::disabled() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + &format!( + r#" + fn main() {{ + assert_eq!(std::env::var("TARGET").unwrap(), "{}"); + }} + "#, + cross_compile::alternate() + ), + ) + .file( + "src/main.rs", + &format!( + r#" + use std::env; + fn main() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + let target = cross_compile::alternate(); + p.cargo("build -v --target").arg(&target).run(); + assert!(p.target_bin(target, "foo").is_file()); + + if cross_compile::can_run_on_host() { + p.process(&p.target_bin(target, "foo")).run(); + } +} + +#[cargo_test] +fn simple_cross_config() { + if cross_compile::disabled() { + return; + } + + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [build] + target = "{}" + "#, + cross_compile::alternate() + ), + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + &format!( + r#" + fn main() {{ + assert_eq!(std::env::var("TARGET").unwrap(), "{}"); + }} + "#, + cross_compile::alternate() + ), + ) + .file( + "src/main.rs", + &format!( + r#" + use std::env; + fn main() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + let target = cross_compile::alternate(); + p.cargo("build -v").run(); + assert!(p.target_bin(target, "foo").is_file()); + + if cross_compile::can_run_on_host() { + p.process(&p.target_bin(target, "foo")).run(); + } +} + +#[cargo_test] +fn simple_deps() { + if cross_compile::disabled() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() { bar::bar(); }") + .build(); + let _p2 = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let target = cross_compile::alternate(); + p.cargo("build --target").arg(&target).run(); + assert!(p.target_bin(target, "foo").is_file()); + + if cross_compile::can_run_on_host() { + p.process(&p.target_bin(target, "foo")).run(); + } +} + +/// Always take care of setting these so that +/// `cross_compile::alternate()` is the actually-picked target +fn per_crate_target_test( + default_target: Option<&'static str>, + forced_target: Option<&'static str>, + arg_target: Option<&'static str>, +) { + if cross_compile::disabled() { + return; + } + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = "build.rs" + {} + {} + "#, + default_target + .map(|t| format!(r#"default-target = "{}""#, t)) + .unwrap_or(String::new()), + forced_target + .map(|t| format!(r#"forced-target = "{}""#, t)) + .unwrap_or(String::new()), + ), + ) + .file( + "build.rs", + &format!( + r#" + fn main() {{ + assert_eq!(std::env::var("TARGET").unwrap(), "{}"); + }} + "#, + cross_compile::alternate() + ), + ) + .file( + "src/main.rs", + &format!( + r#" + use std::env; + fn main() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + let mut cmd = p.cargo("build -v"); + if let Some(t) = arg_target { + cmd.arg("--target").arg(&t); + } + cmd.masquerade_as_nightly_cargo(&["per-package-target"]) + .run(); + assert!(p.target_bin(cross_compile::alternate(), "foo").is_file()); + + if cross_compile::can_run_on_host() { + p.process(&p.target_bin(cross_compile::alternate(), "foo")) + .run(); + } +} + +#[cargo_test] +fn per_crate_default_target_is_default() { + per_crate_target_test(Some(cross_compile::alternate()), None, None); +} + +#[cargo_test] +fn per_crate_default_target_gets_overridden() { + per_crate_target_test( + Some(cross_compile::unused()), + None, + Some(cross_compile::alternate()), + ); +} + +#[cargo_test] +fn per_crate_forced_target_is_default() { + per_crate_target_test(None, Some(cross_compile::alternate()), None); +} + +#[cargo_test] +fn per_crate_forced_target_does_not_get_overridden() { + per_crate_target_test( + None, + Some(cross_compile::alternate()), + Some(cross_compile::unused()), + ); +} + +#[cargo_test] +fn workspace_with_multiple_targets() { + if cross_compile::disabled() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["native", "cross"] + "#, + ) + .file( + "native/Cargo.toml", + r#" + cargo-features = ["per-package-target"] + + [package] + name = "native" + version = "0.0.0" + authors = [] + build = "build.rs" + "#, + ) + .file( + "native/build.rs", + &format!( + r#" + fn main() {{ + assert_eq!(std::env::var("TARGET").unwrap(), "{}"); + }} + "#, + cross_compile::native() + ), + ) + .file( + "native/src/main.rs", + &format!( + r#" + use std::env; + fn main() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + "#, + cross_compile::native_arch() + ), + ) + .file( + "cross/Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + + [package] + name = "cross" + version = "0.0.0" + authors = [] + build = "build.rs" + default-target = "{}" + "#, + cross_compile::alternate(), + ), + ) + .file( + "cross/build.rs", + &format!( + r#" + fn main() {{ + assert_eq!(std::env::var("TARGET").unwrap(), "{}"); + }} + "#, + cross_compile::alternate() + ), + ) + .file( + "cross/src/main.rs", + &format!( + r#" + use std::env; + fn main() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + let mut cmd = p.cargo("build -v"); + cmd.masquerade_as_nightly_cargo(&["per-package-target"]) + .run(); + + assert!(p.bin("native").is_file()); + assert!(p.target_bin(cross_compile::alternate(), "cross").is_file()); + + p.process(&p.bin("native")).run(); + if cross_compile::can_run_on_host() { + p.process(&p.target_bin(cross_compile::alternate(), "cross")) + .run(); + } +} + +#[cargo_test] +fn linker() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + linker = "my-linker-tool" + "#, + target + ), + ) + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/foo.rs", + &format!( + r#" + use std::env; + fn main() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + p.cargo("build -v --target") + .arg(&target) + .with_status(101) + .with_stderr_contains(&format!( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc --crate-name foo src/foo.rs [..]--crate-type bin \ + --emit=[..]link[..]-C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [CWD]/target/{target}/debug/deps \ + --target {target} \ + -C linker=my-linker-tool \ + -L dependency=[CWD]/target/{target}/debug/deps \ + -L dependency=[CWD]/target/debug/deps` +", + target = target, + )) + .run(); +} + +#[cargo_test(nightly, reason = "plugins are unstable")] +fn plugin_with_extra_dylib_dep() { + if cross_compile::disabled() { + return; + } + + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file( + "src/main.rs", + r#" + #![feature(plugin)] + #![plugin(bar)] + + fn main() {} + "#, + ) + .build(); + let _bar = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + name = "bar" + plugin = true + + [dependencies.baz] + path = "../baz" + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(rustc_private)] + + extern crate baz; + extern crate rustc_driver; + + use rustc_driver::plugin::Registry; + + #[no_mangle] + pub fn __rustc_plugin_registrar(reg: &mut Registry) { + println!("{}", baz::baz()); + } + "#, + ) + .build(); + let _baz = project() + .at("baz") + .file( + "Cargo.toml", + r#" + [package] + name = "baz" + version = "0.0.1" + authors = [] + + [lib] + name = "baz" + crate_type = ["dylib"] + "#, + ) + .file("src/lib.rs", "pub fn baz() -> i32 { 1 }") + .build(); + + let target = cross_compile::alternate(); + foo.cargo("build --target").arg(&target).run(); +} + +#[cargo_test] +fn cross_tests() { + if !cross_compile::can_run_on_host() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [[bin]] + name = "bar" + "#, + ) + .file( + "src/bin/bar.rs", + &format!( + r#" + #[allow(unused_extern_crates)] + extern crate foo; + use std::env; + fn main() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + #[test] fn test() {{ main() }} + "#, + cross_compile::alternate_arch() + ), + ) + .file( + "src/lib.rs", + &format!( + r#" + use std::env; + pub fn foo() {{ assert_eq!(env::consts::ARCH, "{}"); }} + #[test] fn test_foo() {{ foo() }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + let target = cross_compile::alternate(); + p.cargo("test --target") + .arg(&target) + .with_stderr(&format!( + "\ +[COMPILING] foo v0.0.0 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/{triple}/debug/deps/foo-[..][EXE]) +[RUNNING] [..] (target/{triple}/debug/deps/bar-[..][EXE])", + triple = target + )) + .with_stdout_contains("test test_foo ... ok") + .with_stdout_contains("test test ... ok") + .run(); +} + +#[cargo_test] +fn no_cross_doctests() { + if cross_compile::disabled() { + return; + } + + let p = project() + .file( + "src/lib.rs", + r#" + //! ``` + //! extern crate foo; + //! assert!(true); + //! ``` + "#, + ) + .build(); + + let host_output = "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[DOCTEST] foo +"; + + println!("a"); + p.cargo("test").with_stderr(&host_output).run(); + + println!("b"); + let target = rustc_host(); + p.cargo("test -v --target") + .arg(&target) + // Unordered since the two `rustc` invocations happen concurrently. + .with_stderr_unordered(&format!( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..]--crate-type lib[..] +[RUNNING] `rustc --crate-name foo [..]--test[..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[CWD]/target/{target}/debug/deps/foo-[..][EXE]` +[DOCTEST] foo +[RUNNING] `rustdoc [..]--target {target}[..]` +", + )) + .with_stdout( + " +running 0 tests + +test result: ok. 0 passed[..] + + +running 1 test +test src/lib.rs - (line 2) ... ok + +test result: ok. 1 passed[..] + +", + ) + .run(); + + println!("c"); + let target = cross_compile::alternate(); + + // This will build the library, but does not build or run doc tests. + // This should probably be a warning or error. + p.cargo("test -v --doc --target") + .arg(&target) + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[NOTE] skipping doctests for foo v0.0.1 ([ROOT]/foo) (lib), \ +cross-compilation doctests are not yet supported +See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#doctest-xcompile \ +for more information. +", + ) + .run(); + + if !cross_compile::can_run_on_host() { + return; + } + + // This tests the library, but does not run the doc tests. + p.cargo("test -v --target") + .arg(&target) + .with_stderr(&format!( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..]--test[..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[CWD]/target/{triple}/debug/deps/foo-[..][EXE]` +[NOTE] skipping doctests for foo v0.0.1 ([ROOT]/foo) (lib), \ +cross-compilation doctests are not yet supported +See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#doctest-xcompile \ +for more information. +", + triple = target + )) + .run(); +} + +#[cargo_test] +fn simple_cargo_run() { + if !cross_compile::can_run_on_host() { + return; + } + + let p = project() + .file( + "src/main.rs", + &format!( + r#" + use std::env; + fn main() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + let target = cross_compile::alternate(); + p.cargo("run --target").arg(&target).run(); +} + +#[cargo_test] +fn cross_with_a_build_script() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = 'build.rs' + "#, + ) + .file( + "build.rs", + &format!( + r#" + use std::env; + use std::path::PathBuf; + fn main() {{ + assert_eq!(env::var("TARGET").unwrap(), "{0}"); + let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + assert_eq!(path.file_name().unwrap().to_str().unwrap(), "out"); + path.pop(); + assert!(path.file_name().unwrap().to_str().unwrap() + .starts_with("foo-")); + path.pop(); + assert_eq!(path.file_name().unwrap().to_str().unwrap(), "build"); + path.pop(); + assert_eq!(path.file_name().unwrap().to_str().unwrap(), "debug"); + path.pop(); + assert_eq!(path.file_name().unwrap().to_str().unwrap(), "{0}"); + path.pop(); + assert_eq!(path.file_name().unwrap().to_str().unwrap(), "target"); + }} + "#, + target + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v --target") + .arg(&target) + .with_stderr(&format!( + "\ +[COMPILING] foo v0.0.0 ([CWD]) +[RUNNING] `rustc [..] build.rs [..] --out-dir [CWD]/target/debug/build/foo-[..]` +[RUNNING] `[CWD]/target/debug/build/foo-[..]/build-script-build` +[RUNNING] `rustc [..] src/main.rs [..] --target {target} [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + target = target, + )) + .run(); +} + +#[cargo_test] +fn build_script_needed_for_host_and_target() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + let host = rustc_host(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = 'build.rs' + + [dependencies.d1] + path = "d1" + [build-dependencies.d2] + path = "d2" + "#, + ) + .file( + "build.rs", + r#" + #[allow(unused_extern_crates)] + extern crate d2; + fn main() { d2::d2(); } + "#, + ) + .file( + "src/main.rs", + " + #[allow(unused_extern_crates)] + extern crate d1; + fn main() { d1::d1(); } + ", + ) + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.0" + authors = [] + build = 'build.rs' + "#, + ) + .file("d1/src/lib.rs", "pub fn d1() {}") + .file( + "d1/build.rs", + r#" + use std::env; + fn main() { + let target = env::var("TARGET").unwrap(); + println!("cargo:rustc-flags=-L /path/to/{}", target); + } + "#, + ) + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.0" + authors = [] + + [dependencies.d1] + path = "../d1" + "#, + ) + .file( + "d2/src/lib.rs", + " + #[allow(unused_extern_crates)] + extern crate d1; + pub fn d2() { d1::d1(); } + ", + ) + .build(); + + p.cargo("build -v --target") + .arg(&target) + .with_stderr_contains(&"[COMPILING] d1 v0.0.0 ([CWD]/d1)") + .with_stderr_contains( + "[RUNNING] `rustc [..] d1/build.rs [..] --out-dir [CWD]/target/debug/build/d1-[..]`", + ) + .with_stderr_contains("[RUNNING] `[CWD]/target/debug/build/d1-[..]/build-script-build`") + .with_stderr_contains("[RUNNING] `rustc [..] d1/src/lib.rs [..]`") + .with_stderr_contains("[COMPILING] d2 v0.0.0 ([CWD]/d2)") + .with_stderr_contains(&format!( + "[RUNNING] `rustc [..] d2/src/lib.rs [..] -L /path/to/{host}`", + host = host + )) + .with_stderr_contains("[COMPILING] foo v0.0.0 ([CWD])") + .with_stderr_contains(&format!( + "[RUNNING] `rustc [..] build.rs [..] --out-dir [CWD]/target/debug/build/foo-[..] \ + -L /path/to/{host}`", + host = host + )) + .with_stderr_contains(&format!( + "[RUNNING] `rustc [..] src/main.rs [..] --target {target} [..] \ + -L /path/to/{target}`", + target = target + )) + .run(); +} + +#[cargo_test] +fn build_deps_for_the_right_arch() { + if cross_compile::disabled() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies.d2] + path = "d2" + "#, + ) + .file("src/main.rs", "extern crate d2; fn main() {}") + .file("d1/Cargo.toml", &basic_manifest("d1", "0.0.0")) + .file("d1/src/lib.rs", "pub fn d1() {}") + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.0" + authors = [] + build = "build.rs" + + [build-dependencies.d1] + path = "../d1" + "#, + ) + .file("d2/build.rs", "extern crate d1; fn main() {}") + .file("d2/src/lib.rs", "") + .build(); + + let target = cross_compile::alternate(); + p.cargo("build -v --target").arg(&target).run(); +} + +#[cargo_test] +fn build_script_only_host() { + if cross_compile::disabled() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = "build.rs" + + [build-dependencies.d1] + path = "d1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("build.rs", "extern crate d1; fn main() {}") + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.0" + authors = [] + build = "build.rs" + "#, + ) + .file("d1/src/lib.rs", "pub fn d1() {}") + .file( + "d1/build.rs", + r#" + use std::env; + + fn main() { + assert!(env::var("OUT_DIR").unwrap().replace("\\", "/") + .contains("target/debug/build/d1-"), + "bad: {:?}", env::var("OUT_DIR")); + } + "#, + ) + .build(); + + let target = cross_compile::alternate(); + p.cargo("build -v --target").arg(&target).run(); +} + +#[cargo_test] +fn plugin_build_script_right_arch() { + if cross_compile::disabled() { + return; + } + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + + [lib] + name = "foo" + plugin = true + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v --target") + .arg(cross_compile::alternate()) + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] build.rs [..]` +[RUNNING] `[..]/build-script-build` +[RUNNING] `rustc [..] src/lib.rs [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_script_with_platform_specific_dependencies() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + let host = rustc_host(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + + [build-dependencies.d1] + path = "d1" + "#, + ) + .file( + "build.rs", + " + #[allow(unused_extern_crates)] + extern crate d1; + fn main() {} + ", + ) + .file("src/lib.rs", "") + .file( + "d1/Cargo.toml", + &format!( + r#" + [package] + name = "d1" + version = "0.0.0" + authors = [] + + [target.{}.dependencies] + d2 = {{ path = "../d2" }} + "#, + host + ), + ) + .file( + "d1/src/lib.rs", + "#[allow(unused_extern_crates)] extern crate d2;", + ) + .file("d2/Cargo.toml", &basic_manifest("d2", "0.0.0")) + .file("d2/src/lib.rs", "") + .build(); + + p.cargo("build -v --target") + .arg(&target) + .with_stderr(&format!( + "\ +[COMPILING] d2 v0.0.0 ([..]) +[RUNNING] `rustc [..] d2/src/lib.rs [..]` +[COMPILING] d1 v0.0.0 ([..]) +[RUNNING] `rustc [..] d1/src/lib.rs [..]` +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] build.rs [..]` +[RUNNING] `[CWD]/target/debug/build/foo-[..]/build-script-build` +[RUNNING] `rustc [..] src/lib.rs [..] --target {target} [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + target = target + )) + .run(); +} + +#[cargo_test] +fn platform_specific_dependencies_do_not_leak() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + let host = rustc_host(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + + [dependencies.d1] + path = "d1" + + [build-dependencies.d1] + path = "d1" + "#, + ) + .file("build.rs", "extern crate d1; fn main() {}") + .file("src/lib.rs", "") + .file( + "d1/Cargo.toml", + &format!( + r#" + [package] + name = "d1" + version = "0.0.0" + authors = [] + + [target.{}.dependencies] + d2 = {{ path = "../d2" }} + "#, + host + ), + ) + .file("d1/src/lib.rs", "extern crate d2;") + .file("d1/Cargo.toml", &basic_manifest("d1", "0.0.0")) + .file("d2/src/lib.rs", "") + .build(); + + p.cargo("build -v --target") + .arg(&target) + .with_status(101) + .with_stderr_contains("[..] can't find crate for `d2`[..]") + .run(); +} + +#[cargo_test] +fn platform_specific_variables_reflected_in_build_scripts() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + let host = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + + [target.{host}.dependencies] + d1 = {{ path = "d1" }} + + [target.{target}.dependencies] + d2 = {{ path = "d2" }} + "#, + host = host, + target = target + ), + ) + .file( + "build.rs", + &format!( + r#" + use std::env; + + fn main() {{ + let platform = env::var("TARGET").unwrap(); + let (expected, not_expected) = match &platform[..] {{ + "{host}" => ("DEP_D1_VAL", "DEP_D2_VAL"), + "{target}" => ("DEP_D2_VAL", "DEP_D1_VAL"), + _ => panic!("unknown platform") + }}; + + env::var(expected).ok() + .expect(&format!("missing {{}}", expected)); + env::var(not_expected).err() + .expect(&format!("found {{}}", not_expected)); + }} + "#, + host = host, + target = target + ), + ) + .file("src/lib.rs", "") + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.0" + authors = [] + links = "d1" + build = "build.rs" + "#, + ) + .file("d1/build.rs", r#"fn main() { println!("cargo:val=1") }"#) + .file("d1/src/lib.rs", "") + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.0" + authors = [] + links = "d2" + build = "build.rs" + "#, + ) + .file("d2/build.rs", r#"fn main() { println!("cargo:val=1") }"#) + .file("d2/src/lib.rs", "") + .build(); + + p.cargo("build -v").run(); + p.cargo("build -v --target").arg(&target).run(); +} + +#[cargo_test] +#[cfg_attr( + target_os = "macos", + ignore = "don't have a dylib cross target on macos" +)] +fn cross_test_dylib() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo" + crate_type = ["dylib"] + + [dependencies.bar] + path = "bar" + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate bar as the_bar; + + pub fn bar() { the_bar::baz(); } + + #[test] + fn foo() { bar(); } + "#, + ) + .file( + "tests/test.rs", + r#" + extern crate foo as the_foo; + + #[test] + fn foo() { the_foo::bar(); } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + name = "bar" + crate_type = ["dylib"] + "#, + ) + .file( + "bar/src/lib.rs", + &format!( + r#" + use std::env; + pub fn baz() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + p.cargo("test --target") + .arg(&target) + .with_stderr(&format!( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/{arch}/debug/deps/foo-[..][EXE]) +[RUNNING] [..] (target/{arch}/debug/deps/test-[..][EXE])", + arch = cross_compile::alternate() + )) + .with_stdout_contains_n("test foo ... ok", 2) + .run(); +} + +#[cargo_test(nightly, reason = "-Zdoctest-xcompile is unstable")] +fn doctest_xcompile_linker() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + let p = project() + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + linker = "my-linker-tool" + "#, + target + ), + ) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file( + "src/lib.rs", + r#" + /// ``` + /// assert_eq!(1, 1); + /// ``` + pub fn foo() {} + "#, + ) + .build(); + + // Fails because `my-linker-tool` doesn't actually exist. + p.cargo("test --doc -v -Zdoctest-xcompile --target") + .arg(&target) + .with_status(101) + .masquerade_as_nightly_cargo(&["doctest-xcompile"]) + .with_stderr_contains(&format!( + "\ +[RUNNING] `rustdoc --crate-type lib --crate-name foo --test [..]\ + --target {target} [..] -C linker=my-linker-tool[..] +", + target = target, + )) + .run(); +} diff --git a/tests/testsuite/cross_publish.rs b/tests/testsuite/cross_publish.rs new file mode 100644 index 0000000..5b1416e --- /dev/null +++ b/tests/testsuite/cross_publish.rs @@ -0,0 +1,119 @@ +//! Tests for publishing using the `--target` flag. + +use std::fs::File; + +use cargo_test_support::{cross_compile, project, publish, registry}; + +#[cargo_test] +fn simple_cross_package() { + if cross_compile::disabled() { + return; + } + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + license = "MIT" + description = "foo" + repository = "bar" + "#, + ) + .file( + "src/main.rs", + &format!( + r#" + use std::env; + fn main() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + let target = cross_compile::alternate(); + + p.cargo("package --target") + .arg(&target) + .with_stderr( + "\ +[PACKAGING] foo v0.0.0 ([CWD]) +[VERIFYING] foo v0.0.0 ([CWD]) +[COMPILING] foo v0.0.0 ([CWD]/target/package/foo-0.0.0) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 4 files, [..] ([..] compressed) +", + ) + .run(); + + // Check that the tarball contains the files + let f = File::open(&p.root().join("target/package/foo-0.0.0.crate")).unwrap(); + publish::validate_crate_contents( + f, + "foo-0.0.0.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + &[], + ); +} + +#[cargo_test] +fn publish_with_target() { + if cross_compile::disabled() { + return; + } + + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + license = "MIT" + description = "foo" + repository = "bar" + "#, + ) + .file( + "src/main.rs", + &format!( + r#" + use std::env; + fn main() {{ + assert_eq!(env::consts::ARCH, "{}"); + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + let target = cross_compile::alternate(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .arg("--target") + .arg(&target) + .with_stderr( + "\ +[UPDATING] crates.io index +[PACKAGING] foo v0.0.0 ([CWD]) +[VERIFYING] foo v0.0.0 ([CWD]) +[COMPILING] foo v0.0.0 ([CWD]/target/package/foo-0.0.0) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] +[UPLOADING] foo v0.0.0 ([CWD]) +[UPDATING] crates.io index +", + ) + .run(); +} diff --git a/tests/testsuite/custom_target.rs b/tests/testsuite/custom_target.rs new file mode 100644 index 0000000..b7ad4d8 --- /dev/null +++ b/tests/testsuite/custom_target.rs @@ -0,0 +1,250 @@ +//! Tests for custom json target specifications. + +use cargo_test_support::{basic_manifest, project}; +use std::fs; + +const MINIMAL_LIB: &str = r#" +#![feature(no_core)] +#![feature(lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized { + // Empty. +} +#[lang = "copy"] +pub trait Copy { + // Empty. +} +"#; + +const SIMPLE_SPEC: &str = r#" +{ + "llvm-target": "x86_64-unknown-none-gnu", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "os": "none", + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "executables": true +} +"#; + +#[cargo_test(nightly, reason = "requires features no_core, lang_items")] +fn custom_target_minimal() { + let p = project() + .file( + "src/lib.rs", + &" + __MINIMAL_LIB__ + + pub fn foo() -> u32 { + 42 + } + " + .replace("__MINIMAL_LIB__", MINIMAL_LIB), + ) + .file("custom-target.json", SIMPLE_SPEC) + .build(); + + p.cargo("build --lib --target custom-target.json -v").run(); + p.cargo("build --lib --target src/../custom-target.json -v") + .run(); + + // Ensure that the correct style of flag is passed to --target with doc tests. + p.cargo("test --doc --target src/../custom-target.json -v -Zdoctest-xcompile") + .masquerade_as_nightly_cargo(&["doctest-xcompile", "no_core", "lang_items"]) + .with_stderr_contains("[RUNNING] `rustdoc [..]--target [..]foo/custom-target.json[..]") + .run(); +} + +#[cargo_test(nightly, reason = "requires features no_core, lang_items, auto_traits")] +fn custom_target_dependency() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.0.1" + authors = ["author@example.com"] + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(no_core)] + #![feature(lang_items)] + #![feature(auto_traits)] + #![no_core] + + extern crate bar; + + pub fn foo() -> u32 { + bar::bar() + } + + #[lang = "freeze"] + unsafe auto trait Freeze {} + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file( + "bar/src/lib.rs", + &" + __MINIMAL_LIB__ + + pub fn bar() -> u32 { + 42 + } + " + .replace("__MINIMAL_LIB__", MINIMAL_LIB), + ) + .file("custom-target.json", SIMPLE_SPEC) + .build(); + + p.cargo("build --lib --target custom-target.json -v").run(); +} + +#[cargo_test(nightly, reason = "requires features no_core, lang_items")] +fn custom_bin_target() { + let p = project() + .file( + "src/main.rs", + &" + #![no_main] + __MINIMAL_LIB__ + " + .replace("__MINIMAL_LIB__", MINIMAL_LIB), + ) + .file("custom-bin-target.json", SIMPLE_SPEC) + .build(); + + p.cargo("build --target custom-bin-target.json -v").run(); +} + +#[cargo_test(nightly, reason = "requires features no_core, lang_items")] +fn changing_spec_rebuilds() { + // Changing the .json file will trigger a rebuild. + let p = project() + .file( + "src/lib.rs", + &" + __MINIMAL_LIB__ + + pub fn foo() -> u32 { + 42 + } + " + .replace("__MINIMAL_LIB__", MINIMAL_LIB), + ) + .file("custom-target.json", SIMPLE_SPEC) + .build(); + + p.cargo("build --lib --target custom-target.json -v").run(); + p.cargo("build --lib --target custom-target.json -v") + .with_stderr( + "\ +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); + let spec_path = p.root().join("custom-target.json"); + let spec = fs::read_to_string(&spec_path).unwrap(); + // Some arbitrary change that I hope is safe. + let spec = spec.replace('{', "{\n\"vendor\": \"unknown\",\n"); + fs::write(&spec_path, spec).unwrap(); + p.cargo("build --lib --target custom-target.json -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 [..] +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "requires features no_core, lang_items")] +fn changing_spec_relearns_crate_types() { + // Changing the .json file will invalidate the cache of crate types. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [lib] + crate-type = ["cdylib"] + "#, + ) + .file("src/lib.rs", MINIMAL_LIB) + .file("custom-target.json", SIMPLE_SPEC) + .build(); + + p.cargo("build --lib --target custom-target.json -v") + .with_status(101) + .with_stderr("error: cannot produce cdylib for `foo [..]") + .run(); + + // Enable dynamic linking. + let spec_path = p.root().join("custom-target.json"); + let spec = fs::read_to_string(&spec_path).unwrap(); + let spec = spec.replace('{', "{\n\"dynamic-linking\": true,\n"); + fs::write(&spec_path, spec).unwrap(); + + p.cargo("build --lib --target custom-target.json -v") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "requires features no_core, lang_items")] +fn custom_target_ignores_filepath() { + // Changing the path of the .json file will not trigger a rebuild. + let p = project() + .file( + "src/lib.rs", + &" + __MINIMAL_LIB__ + + pub fn foo() -> u32 { + 42 + } + " + .replace("__MINIMAL_LIB__", MINIMAL_LIB), + ) + .file("b/custom-target.json", SIMPLE_SPEC) + .file("a/custom-target.json", SIMPLE_SPEC) + .build(); + + // Should build the library the first time. + p.cargo("build --lib --target a/custom-target.json") + .with_stderr( + "\ +[..]Compiling foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + // But not the second time, even though the path to the custom target is dfferent. + p.cargo("build --lib --target b/custom-target.json") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); +} diff --git a/tests/testsuite/death.rs b/tests/testsuite/death.rs new file mode 100644 index 0000000..f0e182d --- /dev/null +++ b/tests/testsuite/death.rs @@ -0,0 +1,101 @@ +//! Tests for ctrl-C handling. + +use std::fs; +use std::io::{self, Read}; +use std::net::TcpListener; +use std::process::{Child, Stdio}; +use std::thread; + +use cargo_test_support::{project, slow_cpu_multiplier}; + +#[cargo_test] +fn ctrl_c_kills_everyone() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + &format!( + r#" + use std::net::TcpStream; + use std::io::Read; + + fn main() {{ + let mut socket = TcpStream::connect("{}").unwrap(); + let _ = socket.read(&mut [0; 10]); + panic!("that read should never return"); + }} + "#, + addr + ), + ) + .build(); + + let mut cargo = p.cargo("check").build_command(); + cargo + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1"); + let mut child = cargo.spawn().unwrap(); + + let mut sock = listener.accept().unwrap().0; + ctrl_c(&mut child); + + assert!(!child.wait().unwrap().success()); + match sock.read(&mut [0; 10]) { + Ok(n) => assert_eq!(n, 0), + Err(e) => assert_eq!(e.kind(), io::ErrorKind::ConnectionReset), + } + + // Ok so what we just did was spawn cargo that spawned a build script, then + // we killed cargo in hopes of it killing the build script as well. If all + // went well the build script is now dead. On Windows, however, this is + // enforced with job objects which means that it may actually be in the + // *process* of being torn down at this point. + // + // Now on Windows we can't completely remove a file until all handles to it + // have been closed. Including those that represent running processes. So if + // we were to return here then there may still be an open reference to some + // file in the build directory. What we want to actually do is wait for the + // build script to *complete* exit. Take care of that by blowing away the + // build directory here, and panicking if we eventually spin too long + // without being able to. + for i in 0..10 { + match fs::remove_dir_all(&p.root().join("target")) { + Ok(()) => return, + Err(e) => println!("attempt {}: {}", i, e), + } + thread::sleep(slow_cpu_multiplier(100)); + } + + panic!( + "couldn't remove build directory after a few tries, seems like \ + we won't be able to!" + ); +} + +#[cfg(unix)] +pub fn ctrl_c(child: &mut Child) { + let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) }; + if r < 0 { + panic!("failed to kill: {}", io::Error::last_os_error()); + } +} + +#[cfg(windows)] +pub fn ctrl_c(child: &mut Child) { + child.kill().unwrap(); +} diff --git a/tests/testsuite/dep_info.rs b/tests/testsuite/dep_info.rs new file mode 100644 index 0000000..e9ea477 --- /dev/null +++ b/tests/testsuite/dep_info.rs @@ -0,0 +1,600 @@ +//! Tests for dep-info files. This includes the dep-info file Cargo creates in +//! the output directory, and the ones stored in the fingerprint. + +use cargo_test_support::compare::assert_match_exact; +use cargo_test_support::paths::{self, CargoPathExt}; +use cargo_test_support::registry::Package; +use cargo_test_support::{ + basic_bin_manifest, basic_manifest, main_file, project, rustc_host, Project, +}; +use filetime::FileTime; +use std::fs; +use std::path::Path; +use std::str; + +// Helper for testing dep-info files in the fingerprint dir. +#[track_caller] +fn assert_deps(project: &Project, fingerprint: &str, test_cb: impl Fn(&Path, &[(u8, &str)])) { + let mut files = project + .glob(fingerprint) + .map(|f| f.expect("unwrap glob result")) + // Filter out `.json` entries. + .filter(|f| f.extension().is_none()); + let info_path = files + .next() + .unwrap_or_else(|| panic!("expected 1 dep-info file at {}, found 0", fingerprint)); + assert!(files.next().is_none(), "expected only 1 dep-info file"); + let dep_info = fs::read(&info_path).unwrap(); + let dep_info = &mut &dep_info[..]; + let deps = (0..read_usize(dep_info)) + .map(|_| { + ( + read_u8(dep_info), + str::from_utf8(read_bytes(dep_info)).unwrap(), + ) + }) + .collect::>(); + test_cb(&info_path, &deps); + + fn read_usize(bytes: &mut &[u8]) -> usize { + let ret = &bytes[..4]; + *bytes = &bytes[4..]; + + u32::from_le_bytes(ret.try_into().unwrap()) as usize + } + + fn read_u8(bytes: &mut &[u8]) -> u8 { + let ret = bytes[0]; + *bytes = &bytes[1..]; + ret + } + + fn read_bytes<'a>(bytes: &mut &'a [u8]) -> &'a [u8] { + let n = read_usize(bytes); + let ret = &bytes[..n]; + *bytes = &bytes[n..]; + ret + } +} + +fn assert_deps_contains(project: &Project, fingerprint: &str, expected: &[(u8, &str)]) { + assert_deps(project, fingerprint, |info_path, entries| { + for (e_kind, e_path) in expected { + let pattern = glob::Pattern::new(e_path).unwrap(); + let count = entries + .iter() + .filter(|(kind, path)| kind == e_kind && pattern.matches(path)) + .count(); + if count != 1 { + panic!( + "Expected 1 match of {} {} in {:?}, got {}:\n{:#?}", + e_kind, e_path, info_path, count, entries + ); + } + } + }) +} + +#[cargo_test] +fn build_dep_info() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build").run(); + + let depinfo_bin_path = &p.bin("foo").with_extension("d"); + + assert!(depinfo_bin_path.is_file()); + + let depinfo = p.read_file(depinfo_bin_path.to_str().unwrap()); + + let bin_path = p.bin("foo"); + let src_path = p.root().join("src").join("foo.rs"); + if !depinfo.lines().any(|line| { + line.starts_with(&format!("{}:", bin_path.display())) + && line.contains(src_path.to_str().unwrap()) + }) { + panic!( + "Could not find {:?}: {:?} in {:?}", + bin_path, src_path, depinfo_bin_path + ); + } +} + +#[cargo_test] +fn build_dep_info_lib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["lib"] + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + p.cargo("build --example=ex").run(); + assert!(p.example_lib("ex", "lib").with_extension("d").is_file()); +} + +#[cargo_test] +fn build_dep_info_rlib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["rlib"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + p.cargo("build --example=ex").run(); + assert!(p.example_lib("ex", "rlib").with_extension("d").is_file()); +} + +#[cargo_test] +fn build_dep_info_dylib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["dylib"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + p.cargo("build --example=ex").run(); + assert!(p.example_lib("ex", "dylib").with_extension("d").is_file()); +} + +#[cargo_test] +fn dep_path_inside_target_has_correct_path() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("a")) + .file("target/debug/blah", "") + .file( + "src/main.rs", + r#" + fn main() { + let x = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/target/debug/blah")); + } + "#, + ) + .build(); + + p.cargo("build").run(); + + let depinfo_path = &p.bin("a").with_extension("d"); + + assert!(depinfo_path.is_file(), "{:?}", depinfo_path); + + let depinfo = p.read_file(depinfo_path.to_str().unwrap()); + + let bin_path = p.bin("a"); + let target_debug_blah = Path::new("target").join("debug").join("blah"); + if !depinfo.lines().any(|line| { + line.starts_with(&format!("{}:", bin_path.display())) + && line.contains(target_debug_blah.to_str().unwrap()) + }) { + panic!( + "Could not find {:?}: {:?} in {:?}", + bin_path, target_debug_blah, depinfo_path + ); + } +} + +#[cargo_test] +fn no_rewrite_if_no_change() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("build").run(); + let dep_info = p.root().join("target/debug/libfoo.d"); + let metadata1 = dep_info.metadata().unwrap(); + p.cargo("build").run(); + let metadata2 = dep_info.metadata().unwrap(); + + assert_eq!( + FileTime::from_last_modification_time(&metadata1), + FileTime::from_last_modification_time(&metadata2), + ); +} + +#[cargo_test(nightly, reason = "-Z binary-dep-depinfo is unstable")] +fn relative_depinfo_paths_ws() { + // Test relative dep-info paths in a workspace with --target with + // proc-macros and other dependency kinds. + Package::new("regdep", "0.1.0") + .file("src/lib.rs", "pub fn f() {}") + .publish(); + Package::new("pmdep", "0.1.0") + .file("src/lib.rs", "pub fn f() {}") + .publish(); + Package::new("bdep", "0.1.0") + .file("src/lib.rs", "pub fn f() {}") + .publish(); + + let p = project() + /*********** Workspace ***********/ + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + "#, + ) + /*********** Main Project ***********/ + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + pm = {path = "../pm"} + bar = {path = "../bar"} + regdep = "0.1" + + [build-dependencies] + bdep = "0.1" + bar = {path = "../bar"} + "#, + ) + .file( + "foo/src/main.rs", + r#" + pm::noop!{} + + fn main() { + bar::f(); + regdep::f(); + } + "#, + ) + .file("foo/build.rs", "fn main() { bdep::f(); }") + /*********** Proc Macro ***********/ + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + edition = "2018" + + [lib] + proc-macro = true + + [dependencies] + pmdep = "0.1" + "#, + ) + .file( + "pm/src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro] + pub fn noop(_item: TokenStream) -> TokenStream { + pmdep::f(); + "".parse().unwrap() + } + "#, + ) + /*********** Path Dependency `bar` ***********/ + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn f() {}") + .build(); + + let host = rustc_host(); + p.cargo("build -Z binary-dep-depinfo --target") + .arg(&host) + .masquerade_as_nightly_cargo(&["binary-dep-depinfo"]) + .with_stderr_contains("[COMPILING] foo [..]") + .run(); + + assert_deps_contains( + &p, + "target/debug/.fingerprint/pm-*/dep-lib-pm", + &[(0, "src/lib.rs"), (1, "debug/deps/libpmdep-*.rlib")], + ); + + assert_deps_contains( + &p, + &format!("target/{}/debug/.fingerprint/foo-*/dep-bin-foo", host), + &[ + (0, "src/main.rs"), + ( + 1, + &format!( + "debug/deps/{}pm-*.{}", + paths::get_lib_prefix("proc-macro"), + paths::get_lib_extension("proc-macro") + ), + ), + (1, &format!("{}/debug/deps/libbar-*.rlib", host)), + (1, &format!("{}/debug/deps/libregdep-*.rlib", host)), + ], + ); + + assert_deps_contains( + &p, + "target/debug/.fingerprint/foo-*/dep-build-script-build-script-build", + &[(0, "build.rs"), (1, "debug/deps/libbdep-*.rlib")], + ); + + // Make sure it stays fresh. + p.cargo("build -Z binary-dep-depinfo --target") + .arg(&host) + .masquerade_as_nightly_cargo(&["binary-dep-depinfo"]) + .with_stderr("[FINISHED] dev [..]") + .run(); +} + +#[cargo_test(nightly, reason = "-Z binary-dep-depinfo is unstable")] +fn relative_depinfo_paths_no_ws() { + // Test relative dep-info paths without a workspace with proc-macros and + // other dependency kinds. + Package::new("regdep", "0.1.0") + .file("src/lib.rs", "pub fn f() {}") + .publish(); + Package::new("pmdep", "0.1.0") + .file("src/lib.rs", "pub fn f() {}") + .publish(); + Package::new("bdep", "0.1.0") + .file("src/lib.rs", "pub fn f() {}") + .publish(); + + let p = project() + /*********** Main Project ***********/ + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + pm = {path = "pm"} + bar = {path = "bar"} + regdep = "0.1" + + [build-dependencies] + bdep = "0.1" + bar = {path = "bar"} + "#, + ) + .file( + "src/main.rs", + r#" + pm::noop!{} + + fn main() { + bar::f(); + regdep::f(); + } + "#, + ) + .file("build.rs", "fn main() { bdep::f(); }") + /*********** Proc Macro ***********/ + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + edition = "2018" + + [lib] + proc-macro = true + + [dependencies] + pmdep = "0.1" + "#, + ) + .file( + "pm/src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro] + pub fn noop(_item: TokenStream) -> TokenStream { + pmdep::f(); + "".parse().unwrap() + } + "#, + ) + /*********** Path Dependency `bar` ***********/ + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn f() {}") + .build(); + + p.cargo("build -Z binary-dep-depinfo") + .masquerade_as_nightly_cargo(&["binary-dep-depinfo"]) + .with_stderr_contains("[COMPILING] foo [..]") + .run(); + + assert_deps_contains( + &p, + "target/debug/.fingerprint/pm-*/dep-lib-pm", + &[(0, "src/lib.rs"), (1, "debug/deps/libpmdep-*.rlib")], + ); + + assert_deps_contains( + &p, + "target/debug/.fingerprint/foo-*/dep-bin-foo", + &[ + (0, "src/main.rs"), + ( + 1, + &format!( + "debug/deps/{}pm-*.{}", + paths::get_lib_prefix("proc-macro"), + paths::get_lib_extension("proc-macro") + ), + ), + (1, "debug/deps/libbar-*.rlib"), + (1, "debug/deps/libregdep-*.rlib"), + ], + ); + + assert_deps_contains( + &p, + "target/debug/.fingerprint/foo-*/dep-build-script-build-script-build", + &[(0, "build.rs"), (1, "debug/deps/libbdep-*.rlib")], + ); + + // Make sure it stays fresh. + p.cargo("build -Z binary-dep-depinfo") + .masquerade_as_nightly_cargo(&["binary-dep-depinfo"]) + .with_stderr("[FINISHED] dev [..]") + .run(); +} + +#[cargo_test] +fn reg_dep_source_not_tracked() { + // Make sure source files in dep-info file are not tracked for registry dependencies. + Package::new("regdep", "0.1.0") + .file("src/lib.rs", "pub fn f() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + regdep = "0.1" + "#, + ) + .file("src/lib.rs", "pub fn f() { regdep::f(); }") + .build(); + + p.cargo("check").run(); + + assert_deps( + &p, + "target/debug/.fingerprint/regdep-*/dep-lib-regdep", + |info_path, entries| { + for (kind, path) in entries { + if *kind == 1 { + panic!( + "Did not expect package root relative path type: {:?} in {:?}", + path, info_path + ); + } + } + }, + ); +} + +#[cargo_test(nightly, reason = "-Z binary-dep-depinfo is unstable")] +fn canonical_path() { + if !cargo_test_support::symlink_supported() { + return; + } + Package::new("regdep", "0.1.0") + .file("src/lib.rs", "pub fn f() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + regdep = "0.1" + "#, + ) + .file("src/lib.rs", "pub fn f() { regdep::f(); }") + .build(); + + let real = p.root().join("real_target"); + real.mkdir_p(); + p.symlink(real, "target"); + + p.cargo("check -Z binary-dep-depinfo") + .masquerade_as_nightly_cargo(&["binary-dep-depinfo"]) + .run(); + + assert_deps_contains( + &p, + "target/debug/.fingerprint/foo-*/dep-lib-foo", + &[(0, "src/lib.rs"), (1, "debug/deps/libregdep-*.rmeta")], + ); +} + +#[cargo_test] +fn non_local_build_script() { + // Non-local build script information is not included. + Package::new("bar", "1.0.0") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-changed=build.rs"); + } + "#, + ) + .file("src/lib.rs", "") + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build").run(); + let contents = p.read_file("target/debug/foo.d"); + assert_match_exact( + "[ROOT]/foo/target/debug/foo[EXE]: [ROOT]/foo/src/main.rs", + &contents, + ); +} diff --git a/tests/testsuite/directory.rs b/tests/testsuite/directory.rs new file mode 100644 index 0000000..0e28de0 --- /dev/null +++ b/tests/testsuite/directory.rs @@ -0,0 +1,774 @@ +//! Tests for directory sources. + +use std::collections::HashMap; +use std::fs; +use std::str; + +use serde::Serialize; + +use cargo_test_support::cargo_process; +use cargo_test_support::git; +use cargo_test_support::paths; +use cargo_test_support::registry::{cksum, Package}; +use cargo_test_support::{basic_manifest, project, t, ProjectBuilder}; + +fn setup() { + let root = paths::root(); + t!(fs::create_dir(&root.join(".cargo"))); + t!(fs::write( + root.join(".cargo/config"), + r#" + [source.crates-io] + replace-with = 'my-awesome-local-registry' + + [source.my-awesome-local-registry] + directory = 'index' + "# + )); +} + +struct VendorPackage { + p: Option, + cksum: Checksum, +} + +#[derive(Serialize)] +struct Checksum { + package: Option, + files: HashMap, +} + +impl VendorPackage { + fn new(name: &str) -> VendorPackage { + VendorPackage { + p: Some(project().at(&format!("index/{}", name))), + cksum: Checksum { + package: Some(String::new()), + files: HashMap::new(), + }, + } + } + + fn file(&mut self, name: &str, contents: &str) -> &mut VendorPackage { + self.p = Some(self.p.take().unwrap().file(name, contents)); + self.cksum + .files + .insert(name.to_string(), cksum(contents.as_bytes())); + self + } + + fn disable_checksum(&mut self) -> &mut VendorPackage { + self.cksum.package = None; + self + } + + fn no_manifest(mut self) -> Self { + self.p = self.p.map(|pb| pb.no_manifest()); + self + } + + fn build(&mut self) { + let p = self.p.take().unwrap(); + let json = serde_json::to_string(&self.cksum).unwrap(); + let p = p.file(".cargo-checksum.json", &json); + let _ = p.build(); + } +} + +#[cargo_test] +fn simple() { + setup(); + + VendorPackage::new("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.1.0 ([CWD]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn simple_install() { + setup(); + + VendorPackage::new("foo") + .file("src/lib.rs", "pub fn foo() {}") + .build(); + + VendorPackage::new("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + foo = "0.0.1" + "#, + ) + .file( + "src/main.rs", + "extern crate foo; pub fn main() { foo::foo(); }", + ) + .build(); + + cargo_process("install bar") + .with_stderr( + "\ +[INSTALLING] bar v0.1.0 +[COMPILING] foo v0.0.1 +[COMPILING] bar v0.1.0 +[FINISHED] release [optimized] target(s) in [..]s +[INSTALLING] [..]bar[..] +[INSTALLED] package `bar v0.1.0` (executable `bar[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); +} + +#[cargo_test] +fn simple_install_fail() { + setup(); + + VendorPackage::new("foo") + .file("src/lib.rs", "pub fn foo() {}") + .build(); + + VendorPackage::new("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + foo = "0.1.0" + baz = "9.8.7" + "#, + ) + .file( + "src/main.rs", + "extern crate foo; pub fn main() { foo::foo(); }", + ) + .build(); + + cargo_process("install bar") + .with_status(101) + .with_stderr( + " Installing bar v0.1.0 +error: failed to compile `bar v0.1.0`, intermediate artifacts can be found at `[..]` + +Caused by: + no matching package found + searched package name: `baz` + perhaps you meant: bar or foo + location searched: registry `crates-io` + required by package `bar v0.1.0` +", + ) + .run(); +} + +#[cargo_test] +fn install_without_feature_dep() { + setup(); + + VendorPackage::new("foo") + .file("src/lib.rs", "pub fn foo() {}") + .build(); + + VendorPackage::new("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + foo = "0.0.1" + baz = { version = "9.8.7", optional = true } + + [features] + wantbaz = ["baz"] + "#, + ) + .file( + "src/main.rs", + "extern crate foo; pub fn main() { foo::foo(); }", + ) + .build(); + + cargo_process("install bar") + .with_stderr( + "\ +[INSTALLING] bar v0.1.0 +[COMPILING] foo v0.0.1 +[COMPILING] bar v0.1.0 +[FINISHED] release [optimized] target(s) in [..]s +[INSTALLING] [..]bar[..] +[INSTALLED] package `bar v0.1.0` (executable `bar[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); +} + +#[cargo_test] +fn not_there() { + setup(); + + let _ = project().at("index").build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: no matching package named `bar` found +location searched: [..] +required by package `foo v0.1.0 ([..])` +", + ) + .run(); +} + +#[cargo_test] +fn multiple() { + setup(); + + VendorPackage::new("bar-0.1.0") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .file(".cargo-checksum", "") + .build(); + + VendorPackage::new("bar-0.2.0") + .file("Cargo.toml", &basic_manifest("bar", "0.2.0")) + .file("src/lib.rs", "pub fn bar() {}") + .file(".cargo-checksum", "") + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.1.0 ([CWD]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn crates_io_then_directory() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + let cksum = Package::new("bar", "0.1.0") + .file("src/lib.rs", "pub fn bar() -> u32 { 0 }") + .publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 ([..]) +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.1.0 ([CWD]) +[FINISHED] [..] +", + ) + .run(); + + setup(); + + let mut v = VendorPackage::new("bar"); + v.file("Cargo.toml", &basic_manifest("bar", "0.1.0")); + v.file("src/lib.rs", "pub fn bar() -> u32 { 1 }"); + v.cksum.package = Some(cksum); + v.build(); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.1.0 ([CWD]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn crates_io_then_bad_checksum() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("bar", "0.1.0").publish(); + + p.cargo("check").run(); + setup(); + + VendorPackage::new("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: checksum for `bar v0.1.0` changed between lock files + +this could be indicative of a few possible errors: + + * the lock file is corrupt + * a replacement source in use (e.g., a mirror) returned a different checksum + * the source itself may be corrupt in one way or another + +unable to verify that `bar v0.1.0` is the same as when the lockfile was generated + +", + ) + .run(); +} + +#[cargo_test] +fn bad_file_checksum() { + setup(); + + VendorPackage::new("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + .build(); + + t!(fs::write( + paths::root().join("index/bar/src/lib.rs"), + "fn bar() -> u32 { 0 }" + )); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: the listed checksum of `[..]lib.rs` has changed: +expected: [..] +actual: [..] + +directory sources are not intended to be edited, if modifications are \ +required then it is recommended that `[patch]` is used with a forked copy of \ +the source +", + ) + .run(); +} + +#[cargo_test] +fn only_dot_files_ok() { + setup(); + + VendorPackage::new("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + .build(); + VendorPackage::new("foo") + .no_manifest() + .file(".bar", "") + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn random_files_ok() { + setup(); + + VendorPackage::new("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + .build(); + VendorPackage::new("foo") + .no_manifest() + .file("bar", "") + .file("../test", "") + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn git_lock_file_doesnt_change() { + let git = git::new("git", |p| { + p.file("Cargo.toml", &basic_manifest("git", "0.5.0")) + .file("src/lib.rs", "") + }); + + VendorPackage::new("git") + .file("Cargo.toml", &basic_manifest("git", "0.5.0")) + .file("src/lib.rs", "") + .disable_checksum() + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + git = {{ git = '{0}' }} + "#, + git.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + let lock1 = p.read_lockfile(); + + let root = paths::root(); + t!(fs::create_dir(&root.join(".cargo"))); + t!(fs::write( + root.join(".cargo/config"), + format!( + r#" + [source.my-git-repo] + git = '{}' + replace-with = 'my-awesome-local-registry' + + [source.my-awesome-local-registry] + directory = 'index' + "#, + git.url() + ) + )); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] [..] +[CHECKING] [..] +[FINISHED] [..] +", + ) + .run(); + + let lock2 = p.read_lockfile(); + assert_eq!(lock1, lock2, "lock files changed"); +} + +#[cargo_test] +fn git_override_requires_lockfile() { + VendorPackage::new("git") + .file("Cargo.toml", &basic_manifest("git", "0.5.0")) + .file("src/lib.rs", "") + .disable_checksum() + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + git = { git = 'https://example.com/' } + "#, + ) + .file("src/lib.rs", "") + .build(); + + let root = paths::root(); + t!(fs::create_dir(&root.join(".cargo"))); + t!(fs::write( + root.join(".cargo/config"), + r#" + [source.my-git-repo] + git = 'https://example.com/' + replace-with = 'my-awesome-local-registry' + + [source.my-awesome-local-registry] + directory = 'index' + "# + )); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to get `git` as a dependency of package `foo v0.0.1 ([..])` + +Caused by: + failed to load source for dependency `git` + +Caused by: + Unable to update [..] + +Caused by: + the source my-git-repo requires a lock file to be present first before it can be + used against vendored source code + + remove the source replacement configuration, generate a lock file, and then + restore the source replacement configuration to continue the build +", + ) + .run(); +} + +#[cargo_test] +fn workspace_different_locations() { + let p = project() + .no_manifest() + .file( + "foo/Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + + [dependencies] + baz = "*" + "#, + ) + .file("foo/src/lib.rs", "") + .file("foo/vendor/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("foo/vendor/baz/src/lib.rs", "") + .file("foo/vendor/baz/.cargo-checksum.json", "{\"files\":{}}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = 'bar' + version = '0.1.0' + + [dependencies] + baz = "*" + "#, + ) + .file("bar/src/lib.rs", "") + .file( + ".cargo/config", + r#" + [build] + target-dir = './target' + + [source.crates-io] + replace-with = 'my-awesome-local-registry' + + [source.my-awesome-local-registry] + directory = 'foo/vendor' + "#, + ) + .build(); + + p.cargo("check").cwd("foo").run(); + p.cargo("check") + .cwd("bar") + .with_stderr( + "\ +[CHECKING] bar [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn version_missing() { + setup(); + + VendorPackage::new("foo") + .file("src/lib.rs", "pub fn foo() {}") + .build(); + + VendorPackage::new("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + foo = "2" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + cargo_process("install bar") + .with_stderr( + "\ +[INSTALLING] bar v0.1.0 +error: failed to compile [..] + +Caused by: + failed to select a version for the requirement `foo = \"^2\"` + candidate versions found which didn't match: 0.0.1 + location searched: directory source `[..] (which is replacing registry `[..]`) + required by package `bar v0.1.0` + perhaps a crate was updated and forgotten to be re-vendored? +", + ) + .with_status(101) + .run(); +} diff --git a/tests/testsuite/doc.rs b/tests/testsuite/doc.rs new file mode 100644 index 0000000..739bcf3 --- /dev/null +++ b/tests/testsuite/doc.rs @@ -0,0 +1,2503 @@ +//! Tests for the `cargo doc` command. + +use cargo::core::compiler::RustDocFingerprint; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project}; +use cargo_test_support::{rustc_host, symlink_supported, tools}; +use std::fs; +use std::str; + +#[cargo_test] +fn simple() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("doc") + .with_stderr( + "\ +[..] foo v0.0.1 ([CWD]) +[..] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/foo/index.html").is_file()); +} + +#[cargo_test] +fn doc_no_libs() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name = "foo" + doc = false + "#, + ) + .file("src/main.rs", "bad code") + .build(); + + p.cargo("doc").run(); +} + +#[cargo_test] +fn doc_twice() { + let p = project().file("src/lib.rs", "pub fn foo() {}").build(); + + p.cargo("doc") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("doc").with_stdout("").run(); +} + +#[cargo_test] +fn doc_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/lib.rs", "extern crate bar; pub fn foo() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("doc") + .with_stderr( + "\ +[..] bar v0.0.1 ([CWD]/bar) +[..] bar v0.0.1 ([CWD]/bar) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/foo/index.html").is_file()); + assert!(p.root().join("target/doc/bar/index.html").is_file()); + + // Verify that it only emits rmeta for the dependency. + assert_eq!(p.glob("target/debug/**/*.rlib").count(), 0); + assert_eq!(p.glob("target/debug/deps/libbar-*.rmeta").count(), 1); + + p.cargo("doc") + .env("CARGO_LOG", "cargo::ops::cargo_rustc::fingerprint") + .with_stdout("") + .run(); + + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/foo/index.html").is_file()); + assert!(p.root().join("target/doc/bar/index.html").is_file()); +} + +#[cargo_test] +fn doc_no_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/lib.rs", "extern crate bar; pub fn foo() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("doc --no-deps") + .with_stderr( + "\ +[CHECKING] bar v0.0.1 ([CWD]/bar) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/foo/index.html").is_file()); + assert!(!p.root().join("target/doc/bar/index.html").is_file()); +} + +#[cargo_test] +fn doc_only_bin() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "extern crate bar; pub fn foo() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("doc -v").run(); + + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/bar/index.html").is_file()); + assert!(p.root().join("target/doc/foo/index.html").is_file()); +} + +#[cargo_test] +fn doc_multiple_targets_same_name_lib() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [lib] + name = "foo_lib" + "#, + ) + .file("foo/src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + [lib] + name = "foo_lib" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("doc --workspace") + .with_status(101) + .with_stderr( + "\ +error: document output filename collision +The lib `foo_lib` in package `foo v0.1.0 ([ROOT]/foo/foo)` has the same name as \ +the lib `foo_lib` in package `bar v0.1.0 ([ROOT]/foo/bar)`. +Only one may be documented at once since they output to the same path. +Consider documenting only one, renaming one, or marking one with `doc = false` in Cargo.toml. +", + ) + .run(); +} + +#[cargo_test] +fn doc_multiple_targets_same_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [[bin]] + name = "foo_lib" + path = "src/foo_lib.rs" + "#, + ) + .file("foo/src/foo_lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + [lib] + name = "foo_lib" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("doc --workspace") + .with_stderr_unordered( + "\ +warning: output filename collision. +The bin target `foo_lib` in package `foo v0.1.0 ([ROOT]/foo/foo)` \ +has the same output filename as the lib target `foo_lib` in package \ +`bar v0.1.0 ([ROOT]/foo/bar)`. +Colliding filename is: [ROOT]/foo/target/doc/foo_lib/index.html +The targets should have unique names. +This is a known bug where multiple crates with the same name use +the same path; see . +[DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar) +[DOCUMENTING] foo v0.1.0 ([ROOT]/foo/foo) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn doc_multiple_targets_same_name_bin() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("foo/src/bin/foo-cli.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + "#, + ) + .file("bar/src/bin/foo-cli.rs", "") + .build(); + + p.cargo("doc --workspace") + .with_status(101) + .with_stderr( + "\ +error: document output filename collision +The bin `foo-cli` in package `foo v0.1.0 ([ROOT]/foo/foo)` has the same name as \ +the bin `foo-cli` in package `bar v0.1.0 ([ROOT]/foo/bar)`. +Only one may be documented at once since they output to the same path. +Consider documenting only one, renaming one, or marking one with `doc = false` in Cargo.toml. +", + ) + .run(); +} + +#[cargo_test] +fn doc_multiple_targets_same_name_undoced() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [[bin]] + name = "foo-cli" + "#, + ) + .file("foo/src/foo-cli.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + [[bin]] + name = "foo-cli" + doc = false + "#, + ) + .file("bar/src/foo-cli.rs", "") + .build(); + + p.cargo("doc --workspace").run(); +} + +#[cargo_test] +fn doc_lib_bin_same_name_documents_lib() { + let p = project() + .file( + "src/main.rs", + r#" + //! Binary documentation + extern crate foo; + fn main() { + foo::foo(); + } + "#, + ) + .file( + "src/lib.rs", + r#" + //! Library documentation + pub fn foo() {} + "#, + ) + .build(); + + p.cargo("doc") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + let doc_html = p.read_file("target/doc/foo/index.html"); + assert!(doc_html.contains("Library")); + assert!(!doc_html.contains("Binary")); +} + +#[cargo_test] +fn doc_lib_bin_same_name_documents_lib_when_requested() { + let p = project() + .file( + "src/main.rs", + r#" + //! Binary documentation + extern crate foo; + fn main() { + foo::foo(); + } + "#, + ) + .file( + "src/lib.rs", + r#" + //! Library documentation + pub fn foo() {} + "#, + ) + .build(); + + p.cargo("doc --lib") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + let doc_html = p.read_file("target/doc/foo/index.html"); + assert!(doc_html.contains("Library")); + assert!(!doc_html.contains("Binary")); +} + +#[cargo_test] +fn doc_lib_bin_same_name_documents_named_bin_when_requested() { + let p = project() + .file( + "src/main.rs", + r#" + //! Binary documentation + extern crate foo; + fn main() { + foo::foo(); + } + "#, + ) + .file( + "src/lib.rs", + r#" + //! Library documentation + pub fn foo() {} + "#, + ) + .build(); + + p.cargo("doc --bin foo") + // The checking/documenting lines are sometimes swapped since they run + // concurrently. + .with_stderr_unordered( + "\ +warning: output filename collision. +The bin target `foo` in package `foo v0.0.1 ([ROOT]/foo)` \ +has the same output filename as the lib target `foo` in package `foo v0.0.1 ([ROOT]/foo)`. +Colliding filename is: [ROOT]/foo/target/doc/foo/index.html +The targets should have unique names. +This is a known bug where multiple crates with the same name use +the same path; see . +[CHECKING] foo v0.0.1 ([CWD]) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + let doc_html = p.read_file("target/doc/foo/index.html"); + assert!(!doc_html.contains("Library")); + assert!(doc_html.contains("Binary")); +} + +#[cargo_test] +fn doc_lib_bin_same_name_documents_bins_when_requested() { + let p = project() + .file( + "src/main.rs", + r#" + //! Binary documentation + extern crate foo; + fn main() { + foo::foo(); + } + "#, + ) + .file( + "src/lib.rs", + r#" + //! Library documentation + pub fn foo() {} + "#, + ) + .build(); + + p.cargo("doc --bins") + // The checking/documenting lines are sometimes swapped since they run + // concurrently. + .with_stderr_unordered( + "\ +warning: output filename collision. +The bin target `foo` in package `foo v0.0.1 ([ROOT]/foo)` \ +has the same output filename as the lib target `foo` in package `foo v0.0.1 ([ROOT]/foo)`. +Colliding filename is: [ROOT]/foo/target/doc/foo/index.html +The targets should have unique names. +This is a known bug where multiple crates with the same name use +the same path; see . +[CHECKING] foo v0.0.1 ([CWD]) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + let doc_html = p.read_file("target/doc/foo/index.html"); + assert!(!doc_html.contains("Library")); + assert!(doc_html.contains("Binary")); +} + +#[cargo_test] +fn doc_lib_bin_example_same_name_documents_named_example_when_requested() { + let p = project() + .file( + "src/main.rs", + r#" + //! Binary documentation + extern crate foo; + fn main() { + foo::foo(); + } + "#, + ) + .file( + "src/lib.rs", + r#" + //! Library documentation + pub fn foo() {} + "#, + ) + .file( + "examples/ex1.rs", + r#" + //! Example1 documentation + pub fn x() { f(); } + "#, + ) + .build(); + + p.cargo("doc --example ex1") + // The checking/documenting lines are sometimes swapped since they run + // concurrently. + .with_stderr_unordered( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + + let doc_html = p.read_file("target/doc/ex1/index.html"); + assert!(!doc_html.contains("Library")); + assert!(!doc_html.contains("Binary")); + assert!(doc_html.contains("Example1")); +} + +#[cargo_test] +fn doc_lib_bin_example_same_name_documents_examples_when_requested() { + let p = project() + .file( + "src/main.rs", + r#" + //! Binary documentation + extern crate foo; + fn main() { + foo::foo(); + } + "#, + ) + .file( + "src/lib.rs", + r#" + //! Library documentation + pub fn foo() {} + "#, + ) + .file( + "examples/ex1.rs", + r#" + //! Example1 documentation + pub fn example1() { f(); } + "#, + ) + .file( + "examples/ex2.rs", + r#" + //! Example2 documentation + pub fn example2() { f(); } + "#, + ) + .build(); + + p.cargo("doc --examples") + // The checking/documenting lines are sometimes swapped since they run + // concurrently. + .with_stderr_unordered( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + + let example_doc_html_1 = p.read_file("target/doc/ex1/index.html"); + let example_doc_html_2 = p.read_file("target/doc/ex2/index.html"); + + assert!(!example_doc_html_1.contains("Library")); + assert!(!example_doc_html_1.contains("Binary")); + + assert!(!example_doc_html_2.contains("Library")); + assert!(!example_doc_html_2.contains("Binary")); + + assert!(example_doc_html_1.contains("Example1")); + assert!(example_doc_html_2.contains("Example2")); +} + +#[cargo_test] +fn doc_dash_p() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "extern crate a;") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [dependencies.b] + path = "../b" + "#, + ) + .file("a/src/lib.rs", "extern crate b;") + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("doc -p a") + .with_stderr( + "\ +[..] b v0.0.1 ([CWD]/b) +[..] b v0.0.1 ([CWD]/b) +[DOCUMENTING] a v0.0.1 ([CWD]/a) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn doc_all_exclude() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") + .build(); + + p.cargo("doc --workspace --exclude baz") + .with_stderr_does_not_contain("[DOCUMENTING] baz v0.1.0 [..]") + .with_stderr( + "\ +[DOCUMENTING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn doc_all_exclude_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") + .build(); + + p.cargo("doc --workspace --exclude '*z'") + .with_stderr_does_not_contain("[DOCUMENTING] baz v0.1.0 [..]") + .with_stderr( + "\ +[DOCUMENTING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn doc_same_name() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/main.rs", "fn main() {}") + .file("examples/main.rs", "fn main() {}") + .file("tests/main.rs", "fn main() {}") + .build(); + + p.cargo("doc").run(); +} + +#[cargo_test(nightly, reason = "no_core, lang_items requires nightly")] +fn doc_target() { + const TARGET: &str = "arm-unknown-linux-gnueabihf"; + + let p = project() + .file( + "src/lib.rs", + r#" + #![feature(no_core, lang_items)] + #![no_core] + + #[lang = "sized"] + trait Sized {} + + extern { + pub static A: u32; + } + "#, + ) + .build(); + + p.cargo("doc --verbose --target").arg(TARGET).run(); + assert!(p.root().join(&format!("target/{}/doc", TARGET)).is_dir()); + assert!(p + .root() + .join(&format!("target/{}/doc/foo/index.html", TARGET)) + .is_file()); +} + +#[cargo_test] +fn target_specific_not_documented() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [target.foo.dependencies] + a = { path = "a" } + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "not rust") + .build(); + + p.cargo("doc").run(); +} + +#[cargo_test] +fn output_not_captured() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file( + "a/src/lib.rs", + " + /// ``` + /// ` + /// ``` + pub fn foo() {} + ", + ) + .build(); + + p.cargo("doc") + .with_stderr_contains("[..]unknown start of token: `") + .run(); +} + +#[cargo_test] +fn target_specific_documented() { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [target.foo.dependencies] + a = {{ path = "a" }} + [target.{}.dependencies] + a = {{ path = "a" }} + "#, + rustc_host() + ), + ) + .file( + "src/lib.rs", + " + extern crate a; + + /// test + pub fn foo() {} + ", + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file( + "a/src/lib.rs", + " + /// test + pub fn foo() {} + ", + ) + .build(); + + p.cargo("doc").run(); +} + +#[cargo_test] +fn no_document_build_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [build-dependencies] + a = { path = "a" } + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file( + "a/src/lib.rs", + " + /// ``` + /// ☃ + /// ``` + pub fn foo() {} + ", + ) + .build(); + + p.cargo("doc").run(); +} + +#[cargo_test] +fn doc_release() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("check --release").run(); + p.cargo("doc --release -v") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([..]) +[RUNNING] `rustdoc [..] src/lib.rs [..]` +[FINISHED] release [optimized] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn doc_multiple_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + + [dependencies.baz] + path = "baz" + "#, + ) + .file("src/lib.rs", "extern crate bar; pub fn foo() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("doc -p bar -p baz -v").run(); + + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/bar/index.html").is_file()); + assert!(p.root().join("target/doc/baz/index.html").is_file()); +} + +#[cargo_test] +fn features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + + [features] + foo = ["bar/bar"] + "#, + ) + .file("src/lib.rs", r#"#[cfg(feature = "foo")] pub fn foo() {}"#) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + bar = [] + "#, + ) + .file( + "bar/build.rs", + r#" + fn main() { + println!("cargo:rustc-cfg=bar"); + } + "#, + ) + .file( + "bar/src/lib.rs", + r#"#[cfg(feature = "bar")] pub fn bar() {}"#, + ) + .build(); + p.cargo("doc --features foo") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 [..] +[DOCUMENTING] bar v0.0.1 [..] +[DOCUMENTING] foo v0.0.1 [..] +[FINISHED] [..] +", + ) + .run(); + assert!(p.root().join("target/doc").is_dir()); + assert!(p.root().join("target/doc/foo/fn.foo.html").is_file()); + assert!(p.root().join("target/doc/bar/fn.bar.html").is_file()); + // Check that turning the feature off will remove the files. + p.cargo("doc") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 [..] +[DOCUMENTING] bar v0.0.1 [..] +[DOCUMENTING] foo v0.0.1 [..] +[FINISHED] [..] +", + ) + .run(); + assert!(!p.root().join("target/doc/foo/fn.foo.html").is_file()); + assert!(!p.root().join("target/doc/bar/fn.bar.html").is_file()); + // And switching back will rebuild and bring them back. + p.cargo("doc --features foo") + .with_stderr( + "\ +[DOCUMENTING] bar v0.0.1 [..] +[DOCUMENTING] foo v0.0.1 [..] +[FINISHED] [..] +", + ) + .run(); + assert!(p.root().join("target/doc/foo/fn.foo.html").is_file()); + assert!(p.root().join("target/doc/bar/fn.bar.html").is_file()); +} + +#[cargo_test] +fn rerun_when_dir_removed() { + let p = project() + .file( + "src/lib.rs", + r#" + /// dox + pub fn foo() {} + "#, + ) + .build(); + + p.cargo("doc").run(); + assert!(p.root().join("target/doc/foo/index.html").is_file()); + + fs::remove_dir_all(p.root().join("target/doc/foo")).unwrap(); + + p.cargo("doc").run(); + assert!(p.root().join("target/doc/foo/index.html").is_file()); +} + +#[cargo_test] +fn document_only_lib() { + let p = project() + .file( + "src/lib.rs", + r#" + /// dox + pub fn foo() {} + "#, + ) + .file( + "src/bin/bar.rs", + r#" + /// ``` + /// ☃ + /// ``` + pub fn foo() {} + fn main() { foo(); } + "#, + ) + .build(); + p.cargo("doc --lib").run(); + assert!(p.root().join("target/doc/foo/index.html").is_file()); +} + +#[cargo_test] +fn plugins_no_use_target() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + proc-macro = true + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("doc --target=x86_64-unknown-openbsd -v").run(); +} + +#[cargo_test] +fn doc_all_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + // The order in which bar is compiled or documented is not deterministic + p.cargo("doc --workspace") + .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") + .with_stderr_contains("[..] Checking bar v0.1.0 ([..])") + .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") + .run(); +} + +#[cargo_test] +fn doc_all_virtual_manifest() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + // The order in which bar and baz are documented is not guaranteed + p.cargo("doc --workspace") + .with_stderr_contains("[..] Documenting baz v0.1.0 ([..])") + .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") + .run(); +} + +#[cargo_test] +fn doc_virtual_manifest_all_implied() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + // The order in which bar and baz are documented is not guaranteed + p.cargo("doc") + .with_stderr_contains("[..] Documenting baz v0.1.0 ([..])") + .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") + .run(); +} + +#[cargo_test] +fn doc_virtual_manifest_one_project() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }") + .build(); + + p.cargo("doc -p bar") + .with_stderr_does_not_contain("[DOCUMENTING] baz v0.1.0 [..]") + .with_stderr( + "\ +[DOCUMENTING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn doc_virtual_manifest_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("doc -p '*z'") + .with_stderr_does_not_contain("[DOCUMENTING] bar v0.1.0 [..]") + .with_stderr( + "\ +[DOCUMENTING] baz v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn doc_all_member_dependency_same_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + Package::new("bar", "0.1.0").publish(); + + p.cargo("doc --workspace") + .with_stderr_unordered( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) +warning: output filename collision. +The lib target `bar` in package `bar v0.1.0` has the same output filename as \ +the lib target `bar` in package `bar v0.1.0 ([ROOT]/foo/bar)`. +Colliding filename is: [ROOT]/foo/target/doc/bar/index.html +The targets should have unique names. +This is a known bug where multiple crates with the same name use +the same path; see . +[DOCUMENTING] bar v0.1.0 +[CHECKING] bar v0.1.0 +[DOCUMENTING] bar v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn doc_workspace_open_help_message() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .build(); + + // The order in which bar is compiled or documented is not deterministic + p.cargo("doc --workspace --open") + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") + .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") + .with_stderr_contains("[..] Opening [..]/bar/index.html") + .run(); +} + +#[cargo_test(nightly, reason = "-Zextern-html-root-url is unstable")] +fn doc_extern_map_local() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file(".cargo/config.toml", "doc.extern-map.std = 'local'") + .build(); + + p.cargo("doc -v --no-deps -Zrustdoc-map --open") + .env("BROWSER", tools::echo()) + .masquerade_as_nightly_cargo(&["rustdoc-map"]) + .with_stderr( + "\ +[DOCUMENTING] foo v0.1.0 [..] +[RUNNING] `rustdoc --crate-type lib --crate-name foo src/lib.rs [..]--crate-version 0.1.0` +[FINISHED] [..] + Opening [CWD]/target/doc/foo/index.html +", + ) + .run(); +} + +#[cargo_test] +fn open_no_doc_crate() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [lib] + doc = false + "#, + ) + .file("src/lib.rs", "#[cfg(feature)] pub fn f();") + .build(); + + p.cargo("doc --open") + .env("BROWSER", "do_not_run_me") + .with_status(101) + .with_stderr_contains("error: no crates with documentation") + .run(); +} + +#[cargo_test] +fn doc_workspace_open_different_library_and_package_names() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [lib] + name = "foolib" + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("doc --open") + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") + .with_stderr_contains("[..] [CWD]/target/doc/foolib/index.html") + .with_stdout_contains("[CWD]/target/doc/foolib/index.html") + .run(); + + p.change_file( + ".cargo/config.toml", + &format!( + r#" + [doc] + browser = ["{}", "a"] + "#, + tools::echo().display().to_string().replace('\\', "\\\\") + ), + ); + + // check that the cargo config overrides the browser env var + p.cargo("doc --open") + .env("BROWSER", "do_not_run_me") + .with_stdout_contains("a [CWD]/target/doc/foolib/index.html") + .run(); +} + +#[cargo_test] +fn doc_workspace_open_binary() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [[bin]] + name = "foobin" + path = "src/main.rs" + "#, + ) + .file("foo/src/main.rs", "") + .build(); + + p.cargo("doc --open") + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") + .with_stderr_contains("[..] Opening [CWD]/target/doc/foobin/index.html") + .run(); +} + +#[cargo_test] +fn doc_workspace_open_binary_and_library() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [lib] + name = "foolib" + [[bin]] + name = "foobin" + path = "src/main.rs" + "#, + ) + .file("foo/src/lib.rs", "") + .file("foo/src/main.rs", "") + .build(); + + p.cargo("doc --open") + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") + .with_stderr_contains("[..] Opening [CWD]/target/doc/foolib/index.html") + .run(); +} + +#[cargo_test] +fn doc_edition() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + edition = "2018" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("doc -v") + .with_stderr_contains("[RUNNING] `rustdoc [..]--edition=2018[..]") + .run(); + + p.cargo("test -v") + .with_stderr_contains("[RUNNING] `rustdoc [..]--edition=2018[..]") + .run(); +} + +#[cargo_test] +fn doc_target_edition() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + edition = "2018" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("doc -v") + .with_stderr_contains("[RUNNING] `rustdoc [..]--edition=2018[..]") + .run(); + + p.cargo("test -v") + .with_stderr_contains("[RUNNING] `rustdoc [..]--edition=2018[..]") + .run(); +} + +// Tests an issue where depending on different versions of the same crate depending on `cfg`s +// caused `cargo doc` to fail. +#[cargo_test] +fn issue_5345() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [target.'cfg(all(windows, target_arch = "x86"))'.dependencies] + bar = "0.1" + + [target.'cfg(not(all(windows, target_arch = "x86")))'.dependencies] + bar = "0.2" + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .build(); + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.2.0").publish(); + + foo.cargo("check").run(); + foo.cargo("doc").run(); +} + +#[cargo_test] +fn doc_private_items() { + let foo = project() + .file("src/lib.rs", "mod private { fn private_item() {} }") + .build(); + foo.cargo("doc --document-private-items").run(); + + assert!(foo.root().join("target/doc").is_dir()); + assert!(foo + .root() + .join("target/doc/foo/private/index.html") + .is_file()); +} + +#[cargo_test] +fn doc_private_ws() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "fn p() {}") + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "fn p2() {}") + .file("b/src/bin/b-cli.rs", "fn main() {}") + .build(); + p.cargo("doc --workspace --bins --lib --document-private-items -v") + .with_stderr_contains( + "[RUNNING] `rustdoc [..] a/src/lib.rs [..]--document-private-items[..]", + ) + .with_stderr_contains( + "[RUNNING] `rustdoc [..] b/src/lib.rs [..]--document-private-items[..]", + ) + .with_stderr_contains( + "[RUNNING] `rustdoc [..] b/src/bin/b-cli.rs [..]--document-private-items[..]", + ) + .run(); +} + +const BAD_INTRA_LINK_LIB: &str = r#" +#![deny(broken_intra_doc_links)] + +/// [bad_link] +pub fn foo() {} +"#; + +#[cargo_test] +fn doc_cap_lints() { + let a = git::new("a", |p| { + p.file("Cargo.toml", &basic_lib_manifest("a")) + .file("src/lib.rs", BAD_INTRA_LINK_LIB) + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = {{ git = '{}' }} + "#, + a.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("doc") + .with_stderr_unordered( + "\ +[UPDATING] git repository `[..]` +[DOCUMENTING] a v0.5.0 ([..]) +[CHECKING] a v0.5.0 ([..]) +[DOCUMENTING] foo v0.0.1 ([..]) +[FINISHED] dev [..] +", + ) + .run(); + + p.root().join("target").rm_rf(); + + p.cargo("doc -vv") + .with_stderr_contains("[WARNING] [..]`bad_link`[..]") + .run(); +} + +#[cargo_test] +fn doc_message_format() { + let p = project().file("src/lib.rs", BAD_INTRA_LINK_LIB).build(); + + p.cargo("doc --message-format=json") + .with_status(101) + .with_json_contains_unordered( + r#" + { + "message": { + "children": "{...}", + "code": "{...}", + "level": "error", + "message": "{...}", + "rendered": "{...}", + "spans": "{...}" + }, + "package_id": "foo [..]", + "manifest_path": "[..]", + "reason": "compiler-message", + "target": "{...}" + } + "#, + ) + .run(); +} + +#[cargo_test] +fn doc_json_artifacts() { + // Checks the output of json artifact messages. + let p = project() + .file("src/lib.rs", "") + .file("src/bin/somebin.rs", "fn main() {}") + .build(); + + p.cargo("doc --message-format=json") + .with_json_contains_unordered( + r#" +{ + "reason": "compiler-artifact", + "package_id": "foo 0.0.1 [..]", + "manifest_path": "[ROOT]/foo/Cargo.toml", + "target": + { + "kind": ["lib"], + "crate_types": ["lib"], + "name": "foo", + "src_path": "[ROOT]/foo/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + "profile": "{...}", + "features": [], + "filenames": ["[ROOT]/foo/target/debug/deps/libfoo-[..].rmeta"], + "executable": null, + "fresh": false +} + +{ + "reason": "compiler-artifact", + "package_id": "foo 0.0.1 [..]", + "manifest_path": "[ROOT]/foo/Cargo.toml", + "target": + { + "kind": ["lib"], + "crate_types": ["lib"], + "name": "foo", + "src_path": "[ROOT]/foo/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + "profile": "{...}", + "features": [], + "filenames": ["[ROOT]/foo/target/doc/foo/index.html"], + "executable": null, + "fresh": false +} + +{ + "reason": "compiler-artifact", + "package_id": "foo 0.0.1 [..]", + "manifest_path": "[ROOT]/foo/Cargo.toml", + "target": + { + "kind": ["bin"], + "crate_types": ["bin"], + "name": "somebin", + "src_path": "[ROOT]/foo/src/bin/somebin.rs", + "edition": "2015", + "doc": true, + "doctest": false, + "test": true + }, + "profile": "{...}", + "features": [], + "filenames": ["[ROOT]/foo/target/doc/somebin/index.html"], + "executable": null, + "fresh": false +} + +{"reason":"build-finished","success":true} +"#, + ) + .run(); +} + +#[cargo_test] +fn short_message_format() { + let p = project().file("src/lib.rs", BAD_INTRA_LINK_LIB).build(); + p.cargo("doc --message-format=short") + .with_status(101) + .with_stderr_contains("src/lib.rs:4:6: error: [..]`bad_link`[..]") + .run(); +} + +#[cargo_test] +fn doc_example() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [[example]] + crate-type = ["lib"] + name = "ex1" + doc = true + "#, + ) + .file("src/lib.rs", "pub fn f() {}") + .file( + "examples/ex1.rs", + r#" + use foo::f; + + /// Example + pub fn x() { f(); } + "#, + ) + .build(); + + p.cargo("doc").run(); + assert!(p + .build_dir() + .join("doc") + .join("ex1") + .join("fn.x.html") + .exists()); +} + +#[cargo_test] +fn doc_example_with_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [[example]] + crate-type = ["lib"] + name = "ex" + doc = true + + [dev-dependencies] + a = {path = "a"} + b = {path = "b"} + "#, + ) + .file("src/lib.rs", "") + .file( + "examples/ex.rs", + r#" + use a::fun; + + /// Example + pub fn x() { fun(); } + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + + [dependencies] + b = {path = "../b"} + "#, + ) + .file("a/src/fun.rs", "pub fn fun() {}") + .file("a/src/lib.rs", "pub mod fun;") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.0.1" + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("doc --examples").run(); + assert!(p + .build_dir() + .join("doc") + .join("ex") + .join("fn.x.html") + .exists()); +} + +#[cargo_test] +fn bin_private_items() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#, + ) + .file( + "src/main.rs", + " + pub fn foo_pub() {} + fn foo_priv() {} + struct FooStruct; + enum FooEnum {} + trait FooTrait {} + type FooType = u32; + mod foo_mod {} + + ", + ) + .build(); + + p.cargo("doc") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + assert!(p.root().join("target/doc/foo/index.html").is_file()); + assert!(p.root().join("target/doc/foo/fn.foo_pub.html").is_file()); + assert!(p.root().join("target/doc/foo/fn.foo_priv.html").is_file()); + assert!(p + .root() + .join("target/doc/foo/struct.FooStruct.html") + .is_file()); + assert!(p.root().join("target/doc/foo/enum.FooEnum.html").is_file()); + assert!(p + .root() + .join("target/doc/foo/trait.FooTrait.html") + .is_file()); + assert!(p.root().join("target/doc/foo/type.FooType.html").is_file()); + assert!(p.root().join("target/doc/foo/foo_mod/index.html").is_file()); +} + +#[cargo_test] +fn bin_private_items_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file( + "src/main.rs", + " + fn foo_priv() {} + pub fn foo_pub() {} + ", + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file( + "bar/src/lib.rs", + " + #[allow(dead_code)] + fn bar_priv() {} + pub fn bar_pub() {} + ", + ) + .build(); + + p.cargo("doc") + .with_stderr_unordered( + "\ +[DOCUMENTING] bar v0.0.1 ([..]) +[CHECKING] bar v0.0.1 ([..]) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + assert!(p.root().join("target/doc/foo/index.html").is_file()); + assert!(p.root().join("target/doc/foo/fn.foo_pub.html").is_file()); + assert!(p.root().join("target/doc/foo/fn.foo_priv.html").is_file()); + + assert!(p.root().join("target/doc/bar/index.html").is_file()); + assert!(p.root().join("target/doc/bar/fn.bar_pub.html").is_file()); + assert!(!p.root().join("target/doc/bar/fn.bar_priv.html").exists()); +} + +#[cargo_test] +fn crate_versions() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.2.4" + authors = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("doc -v") + .with_stderr( + "\ +[DOCUMENTING] foo v1.2.4 [..] +[RUNNING] `rustdoc --crate-type lib --crate-name foo src/lib.rs [..]--crate-version 1.2.4` +[FINISHED] [..] +", + ) + .run(); + + let output_path = p.root().join("target/doc/foo/index.html"); + let output_documentation = fs::read_to_string(&output_path).unwrap(); + + assert!(output_documentation.contains("Version 1.2.4")); +} + +#[cargo_test] +fn crate_versions_flag_is_overridden() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.2.4" + authors = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + + let output_documentation = || { + let output_path = p.root().join("target/doc/foo/index.html"); + fs::read_to_string(&output_path).unwrap() + }; + let asserts = |html: String| { + assert!(!html.contains("1.2.4")); + assert!(html.contains("Version 2.0.3")); + }; + + p.cargo("doc") + .env("RUSTDOCFLAGS", "--crate-version 2.0.3") + .run(); + asserts(output_documentation()); + + p.build_dir().rm_rf(); + + p.cargo("rustdoc -- --crate-version 2.0.3").run(); + asserts(output_documentation()); +} + +#[cargo_test(nightly, reason = "-Zdoctest-in-workspace is unstable")] +fn doc_test_in_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [ + "crate-a", + "crate-b", + ] + "#, + ) + .file( + "crate-a/Cargo.toml", + r#" + [package] + name = "crate-a" + version = "0.1.0" + "#, + ) + .file( + "crate-a/src/lib.rs", + "\ + //! ``` + //! assert_eq!(1, 1); + //! ``` + ", + ) + .file( + "crate-b/Cargo.toml", + r#" + [package] + name = "crate-b" + version = "0.1.0" + "#, + ) + .file( + "crate-b/src/lib.rs", + "\ + //! ``` + //! assert_eq!(1, 1); + //! ``` + ", + ) + .build(); + p.cargo("test -Zdoctest-in-workspace --doc -vv") + .masquerade_as_nightly_cargo(&["doctest-in-workspace"]) + .with_stderr_contains("[DOCTEST] crate-a") + .with_stdout_contains( + " +running 1 test +test crate-a/src/lib.rs - (line 1) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] + +", + ) + .with_stderr_contains("[DOCTEST] crate-b") + .with_stdout_contains( + " +running 1 test +test crate-b/src/lib.rs - (line 1) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] + +", + ) + .run(); +} + +#[cargo_test] +fn doc_fingerprint_is_versioning_consistent() { + // Random rustc verbose version + let old_rustc_verbose_version = format!( + "\ +rustc 1.41.1 (f3e1a954d 2020-02-24) +binary: rustc +commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196 +commit-date: 2020-02-24 +host: {} +release: 1.41.1 +LLVM version: 9.0 +", + rustc_host() + ); + + // Create the dummy project. + let dummy_project = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.2.4" + authors = [] + "#, + ) + .file("src/lib.rs", "//! These are the docs!") + .build(); + + dummy_project.cargo("doc").run(); + + let fingerprint: RustDocFingerprint = + serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json")) + .expect("JSON Serde fail"); + + // Check that the fingerprint contains the actual rustc version + // which has been used to compile the docs. + let output = std::process::Command::new("rustc") + .arg("-vV") + .output() + .expect("Failed to get actual rustc verbose version"); + assert_eq!( + fingerprint.rustc_vv, + (String::from_utf8_lossy(&output.stdout).as_ref()) + ); + + // As the test shows above. Now we have generated the `doc/` folder and inside + // the rustdoc fingerprint file is located with the correct rustc version. + // So we will remove it and create a new fingerprint with an old rustc version + // inside it. We will also place a bogus file inside of the `doc/` folder to ensure + // it gets removed as we expect on the next doc compilation. + dummy_project.change_file( + "target/.rustdoc_fingerprint.json", + &old_rustc_verbose_version, + ); + + fs::write( + dummy_project.build_dir().join("doc/bogus_file"), + String::from("This is a bogus file and should be removed!"), + ) + .expect("Error writing test bogus file"); + + // Now if we trigger another compilation, since the fingerprint contains an old version + // of rustc, cargo should remove the entire `/doc` folder (including the fingerprint) + // and generating another one with the actual version. + // It should also remove the bogus file we created above. + dummy_project.cargo("doc").run(); + + assert!(!dummy_project.build_dir().join("doc/bogus_file").exists()); + + let fingerprint: RustDocFingerprint = + serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json")) + .expect("JSON Serde fail"); + + // Check that the fingerprint contains the actual rustc version + // which has been used to compile the docs. + assert_eq!( + fingerprint.rustc_vv, + (String::from_utf8_lossy(&output.stdout).as_ref()) + ); +} + +#[cargo_test] +fn doc_fingerprint_respects_target_paths() { + // Random rustc verbose version + let old_rustc_verbose_version = format!( + "\ +rustc 1.41.1 (f3e1a954d 2020-02-24) +binary: rustc +commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196 +commit-date: 2020-02-24 +host: {} +release: 1.41.1 +LLVM version: 9.0 +", + rustc_host() + ); + + // Create the dummy project. + let dummy_project = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.2.4" + authors = [] + "#, + ) + .file("src/lib.rs", "//! These are the docs!") + .build(); + + dummy_project.cargo("doc --target").arg(rustc_host()).run(); + + let fingerprint: RustDocFingerprint = + serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json")) + .expect("JSON Serde fail"); + + // Check that the fingerprint contains the actual rustc version + // which has been used to compile the docs. + let output = std::process::Command::new("rustc") + .arg("-vV") + .output() + .expect("Failed to get actual rustc verbose version"); + assert_eq!( + fingerprint.rustc_vv, + (String::from_utf8_lossy(&output.stdout).as_ref()) + ); + + // As the test shows above. Now we have generated the `doc/` folder and inside + // the rustdoc fingerprint file is located with the correct rustc version. + // So we will remove it and create a new fingerprint with an old rustc version + // inside it. We will also place a bogus file inside of the `doc/` folder to ensure + // it gets removed as we expect on the next doc compilation. + dummy_project.change_file( + "target/.rustdoc_fingerprint.json", + &old_rustc_verbose_version, + ); + + fs::write( + dummy_project + .build_dir() + .join(rustc_host()) + .join("doc/bogus_file"), + String::from("This is a bogus file and should be removed!"), + ) + .expect("Error writing test bogus file"); + + // Now if we trigger another compilation, since the fingerprint contains an old version + // of rustc, cargo should remove the entire `/doc` folder (including the fingerprint) + // and generating another one with the actual version. + // It should also remove the bogus file we created above. + dummy_project.cargo("doc --target").arg(rustc_host()).run(); + + assert!(!dummy_project + .build_dir() + .join(rustc_host()) + .join("doc/bogus_file") + .exists()); + + let fingerprint: RustDocFingerprint = + serde_json::from_str(&dummy_project.read_file("target/.rustdoc_fingerprint.json")) + .expect("JSON Serde fail"); + + // Check that the fingerprint contains the actual rustc version + // which has been used to compile the docs. + assert_eq!( + fingerprint.rustc_vv, + (String::from_utf8_lossy(&output.stdout).as_ref()) + ); +} + +#[cargo_test] +fn doc_fingerprint_unusual_behavior() { + // Checks for some unusual circumstances with clearing the doc directory. + if !symlink_supported() { + return; + } + let p = project().file("src/lib.rs", "").build(); + p.build_dir().mkdir_p(); + let real_doc = p.root().join("doc"); + real_doc.mkdir_p(); + let build_doc = p.build_dir().join("doc"); + p.symlink(&real_doc, &build_doc); + fs::write(real_doc.join("somefile"), "test").unwrap(); + fs::write(real_doc.join(".hidden"), "test").unwrap(); + p.cargo("doc").run(); + // Make sure for the first run, it does not delete any files and does not + // break the symlink. + assert!(build_doc.join("somefile").exists()); + assert!(real_doc.join("somefile").exists()); + assert!(real_doc.join(".hidden").exists()); + assert!(real_doc.join("foo/index.html").exists()); + // Pretend that the last build was generated by an older version. + p.change_file( + "target/.rustdoc_fingerprint.json", + "{\"rustc_vv\": \"I am old\"}", + ); + // Change file to trigger a new build. + p.change_file("src/lib.rs", "// changed"); + p.cargo("doc") + .with_stderr( + "[DOCUMENTING] foo [..]\n\ + [FINISHED] [..]", + ) + .run(); + // This will delete somefile, but not .hidden. + assert!(!real_doc.join("somefile").exists()); + assert!(real_doc.join(".hidden").exists()); + assert!(real_doc.join("foo/index.html").exists()); + // And also check the -Z flag behavior. + p.change_file( + "target/.rustdoc_fingerprint.json", + "{\"rustc_vv\": \"I am old\"}", + ); + // Change file to trigger a new build. + p.change_file("src/lib.rs", "// changed2"); + fs::write(real_doc.join("somefile"), "test").unwrap(); + p.cargo("doc -Z skip-rustdoc-fingerprint") + .masquerade_as_nightly_cargo(&["skip-rustdoc-fingerprint"]) + .with_stderr( + "[DOCUMENTING] foo [..]\n\ + [FINISHED] [..]", + ) + .run(); + // Should not have deleted anything. + assert!(build_doc.join("somefile").exists()); + assert!(real_doc.join("somefile").exists()); +} + +#[cargo_test] +fn lib_before_bin() { + // Checks that the library is documented before the binary. + // Previously they were built concurrently, which can cause issues + // if the bin has intra-doc links to the lib. + let p = project() + .file( + "src/lib.rs", + r#" + /// Hi + pub fn abc() {} + "#, + ) + .file( + "src/bin/somebin.rs", + r#" + //! See [`foo::abc`] + fn main() {} + "#, + ) + .build(); + + // Run check first. This just helps ensure that the test clearly shows the + // order of the rustdoc commands. + p.cargo("check").run(); + + // The order of output here should be deterministic. + p.cargo("doc -v") + .with_stderr( + "\ +[DOCUMENTING] foo [..] +[RUNNING] `rustdoc --crate-type lib --crate-name foo src/lib.rs [..] +[RUNNING] `rustdoc --crate-type bin --crate-name somebin src/bin/somebin.rs [..] +[FINISHED] [..] +", + ) + .run(); + + // And the link should exist. + let bin_html = p.read_file("target/doc/somebin/index.html"); + assert!(bin_html.contains("../foo/fn.abc.html")); +} + +#[cargo_test] +fn doc_lib_false() { + // doc = false for a library + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [lib] + doc = false + + [dependencies] + bar = {path = "bar"} + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file("src/bin/some-bin.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [lib] + doc = false + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("doc") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 [..] +[CHECKING] foo v0.1.0 [..] +[DOCUMENTING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + assert!(!p.build_dir().join("doc/foo").exists()); + assert!(!p.build_dir().join("doc/bar").exists()); + assert!(p.build_dir().join("doc/some_bin").exists()); +} + +#[cargo_test] +fn doc_lib_false_dep() { + // doc = false for a dependency + // Ensures that the rmeta gets produced + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [lib] + doc = false + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("doc") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 [..] +[DOCUMENTING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + assert!(p.build_dir().join("doc/foo").exists()); + assert!(!p.build_dir().join("doc/bar").exists()); +} + +#[cargo_test] +fn link_to_private_item() { + let main = r#" + //! [bar] + #[allow(dead_code)] + fn bar() {} + "#; + let p = project().file("src/lib.rs", main).build(); + p.cargo("doc") + .with_stderr_contains("[..] documentation for `foo` links to private item `bar`") + .run(); + // Check that binaries don't emit a private_intra_doc_links warning. + fs::rename(p.root().join("src/lib.rs"), p.root().join("src/main.rs")).unwrap(); + p.cargo("doc") + .with_stderr( + "[DOCUMENTING] foo [..]\n\ + [FINISHED] [..]", + ) + .run(); +} diff --git a/tests/testsuite/docscrape.rs b/tests/testsuite/docscrape.rs new file mode 100644 index 0000000..c536a67 --- /dev/null +++ b/tests/testsuite/docscrape.rs @@ -0,0 +1,637 @@ +//! Tests for the `cargo doc` command with `-Zrustdoc-scrape-examples`. + +use cargo_test_support::project; + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn basic() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#, + ) + .file("examples/ex.rs", "fn main() { foo::foo(); }") + .file("src/lib.rs", "pub fn foo() {}\npub fn bar() { foo(); }") + .build(); + + p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[SCRAPING] foo v0.0.1 ([CWD]) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_stderr("[FINISHED] [..]") + .run(); + + let doc_html = p.read_file("target/doc/foo/fn.foo.html"); + assert!(doc_html.contains("Examples found in repository")); + assert!(!doc_html.contains("More examples")); + + // Ensure that the reverse-dependency has its sources generated + assert!(p.build_dir().join("doc/src/ex/ex.rs.html").exists()); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn avoid_build_script_cycle() { + let p = project() + // package with build dependency + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + links = "foo" + + [workspace] + members = ["bar"] + + [build-dependencies] + bar = {path = "bar"} + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main(){}") + // dependency + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + links = "bar" + "#, + ) + .file("bar/src/lib.rs", "") + .file("bar/build.rs", "fn main(){}") + .build(); + + p.cargo("doc --workspace -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .run(); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn complex_reverse_dependencies() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dev-dependencies] + a = {path = "a", features = ["feature"]} + b = {path = "b"} + + [workspace] + members = ["b"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "fn main() {}") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [lib] + proc-macro = true + + [dependencies] + b = {path = "../b"} + + [features] + feature = [] + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.0.1" + authors = [] + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("doc --workspace --examples -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .run(); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn crate_with_dash() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "da-sh" + version = "0.0.1" + authors = [] + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file("examples/a.rs", "fn main() { da_sh::foo(); }") + .build(); + + p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .run(); + + let doc_html = p.read_file("target/doc/da_sh/fn.foo.html"); + assert!(doc_html.contains("Examples found in repository")); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn configure_target() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + doc-scrape-examples = true + + [[bin]] + name = "a_bin" + doc-scrape-examples = true + + [[example]] + name = "a" + doc-scrape-examples = false + "#, + ) + .file( + "src/lib.rs", + "pub fn foo() {} fn lib_must_appear() { foo(); }", + ) + .file( + "examples/a.rs", + "fn example_must_not_appear() { foo::foo(); }", + ) + .file( + "src/bin/a_bin.rs", + "fn bin_must_appear() { foo::foo(); } fn main(){}", + ) + .build(); + + p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .run(); + + let doc_html = p.read_file("target/doc/foo/fn.foo.html"); + assert!(doc_html.contains("lib_must_appear")); + assert!(doc_html.contains("bin_must_appear")); + assert!(!doc_html.contains("example_must_not_appear")); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn configure_profile_issue_10500() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.dev] + panic = "abort" + "#, + ) + .file("examples/ex.rs", "fn main() { foo::foo(); }") + .file("src/lib.rs", "pub fn foo() {}\npub fn bar() { foo(); }") + .build(); + + p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .run(); + + let doc_html = p.read_file("target/doc/foo/fn.foo.html"); + assert!(doc_html.contains("Examples found in repository")); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn issue_10545() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + resolver = "2" + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + edition = "2021" + + [features] + default = ["foo"] + foo = [] + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.0.1" + authors = [] + edition = "2021" + + [lib] + proc-macro = true + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("doc --workspace -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .run(); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn cache() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#, + ) + .file("examples/ex.rs", "fn main() { foo::foo(); }") + .file("src/lib.rs", "pub fn foo() {}\npub fn bar() { foo(); }") + .build(); + + p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[SCRAPING] foo v0.0.1 ([CWD]) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_stderr( + "\ +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn no_fail_bad_lib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#, + ) + .file("src/lib.rs", "pub fn foo() { CRASH_THE_BUILD() }") + .file("examples/ex.rs", "fn main() { foo::foo(); }") + .file("examples/ex2.rs", "fn main() { foo::foo(); }") + .build(); + + p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_stderr_unordered( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[SCRAPING] foo v0.0.1 ([CWD]) +warning: failed to check lib in package `foo` as a prerequisite for scraping examples from: example \"ex\", example \"ex2\" + Try running with `--verbose` to see the error message. + If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml +warning: `foo` (lib) generated 1 warning +warning: failed to scan example \"ex\" in package `foo` for example code usage + Try running with `--verbose` to see the error message. + If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml +warning: `foo` (example \"ex\") generated 1 warning +warning: failed to scan example \"ex2\" in package `foo` for example code usage + Try running with `--verbose` to see the error message. + If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml +warning: `foo` (example \"ex2\") generated 1 warning +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn fail_bad_build_script() { + // See rust-lang/cargo#11623 + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() { panic!(\"You shall not pass\")}") + .file("examples/ex.rs", "fn main() {}") + .build(); + + // `cargo doc` fails + p.cargo("doc") + .with_status(101) + .with_stderr_contains("[..]You shall not pass[..]") + .run(); + + // scrape examples should fail whenever `cargo doc` fails. + p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_status(101) + .with_stderr_contains("[..]You shall not pass[..]") + .run(); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn no_fail_bad_example() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#, + ) + .file("examples/ex1.rs", "DOES NOT COMPILE") + .file("examples/ex2.rs", "fn main() { foo::foo(); }") + .file("src/lib.rs", "pub fn foo(){}") + .build(); + + p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[SCRAPING] foo v0.0.1 ([CWD]) +warning: failed to scan example \"ex1\" in package `foo` for example code usage + Try running with `--verbose` to see the error message. + If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml +warning: `foo` (example \"ex1\") generated 1 warning +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + + p.cargo("clean").run(); + + p.cargo("doc -v -Zunstable-options -Z rustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_stderr_unordered( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo[..] +[SCRAPING] foo v0.0.1 ([CWD]) +[RUNNING] `rustdoc[..] --crate-name ex1[..] +[RUNNING] `rustdoc[..] --crate-name ex2[..] +[RUNNING] `rustdoc[..] --crate-name foo[..] +error: expected one of `!` or `::`, found `NOT` + --> examples/ex1.rs:1:6 + | +1 | DOES NOT COMPILE + | ^^^ expected one of `!` or `::` + +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + + let doc_html = p.read_file("target/doc/foo/fn.foo.html"); + assert!(doc_html.contains("Examples found in repository")); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn no_scrape_with_dev_deps() { + // Tests that a crate with dev-dependencies does not have its examples + // scraped unless explicitly prompted to check them. See + // `UnitGenerator::create_docscrape_proposals` for details on why. + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dev-dependencies] + a = {path = "a"} + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "fn main() { a::f(); }") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + "#, + ) + .file("a/src/lib.rs", "pub fn f() {}") + .build(); + + // If --examples is not provided, then the example is not scanned, and a warning + // should be raised. + p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_stderr( + "\ +warning: Rustdoc did not scrape the following examples because they require dev-dependencies: ex + If you want Rustdoc to scrape these examples, then add `doc-scrape-examples = true` + to the [[example]] target configuration of at least one example. +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + + // If --examples is provided, then the example is scanned. + p.cargo("doc --examples -Zunstable-options -Z rustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_stderr_unordered( + "\ +[CHECKING] a v0.0.1 ([CWD]/a) +[CHECKING] foo v0.0.1 ([CWD]) +[DOCUMENTING] a v0.0.1 ([CWD]/a) +[SCRAPING] foo v0.0.1 ([CWD]) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn use_dev_deps_if_explicitly_enabled() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + doc-scrape-examples = true + + [dev-dependencies] + a = {path = "a"} + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "fn main() { a::f(); }") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + "#, + ) + .file("a/src/lib.rs", "pub fn f() {}") + .build(); + + // If --examples is not provided, then the example is never scanned. + p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .with_stderr_unordered( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[CHECKING] a v0.0.1 ([CWD]/a) +[SCRAPING] foo v0.0.1 ([CWD]) +[DOCUMENTING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn only_scrape_documented_targets() { + // package bar has doc = false and should not be eligible for documtation. + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + doc = false + + [workspace] + members = ["foo"] + + [dependencies] + foo = {{ path = "foo" }} + "# + ), + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "pub fn main() { foo::foo(); }") + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#, + ) + .file("foo/src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("doc --workspace -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .run(); + + let doc_html = p.read_file("target/doc/foo/fn.foo.html"); + let example_found = doc_html.contains("Examples found in repository"); + assert!(!example_found); +} + +#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")] +fn issue_11496() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "repro" + version = "0.1.0" + edition = "2021" + + [lib] + proc-macro = true + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "fn main(){}") + .build(); + + p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples") + .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"]) + .run(); +} diff --git a/tests/testsuite/edition.rs b/tests/testsuite/edition.rs new file mode 100644 index 0000000..377a86e --- /dev/null +++ b/tests/testsuite/edition.rs @@ -0,0 +1,124 @@ +//! Tests for edition setting. + +use cargo::core::Edition; +use cargo_test_support::{basic_lib_manifest, project}; + +#[cargo_test] +fn edition_works_for_build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + edition = '2018' + + [build-dependencies] + a = { path = 'a' } + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + a::foo(); + } + "#, + ) + .file("a/Cargo.toml", &basic_lib_manifest("a")) + .file("a/src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("check -v").run(); +} + +#[cargo_test] +fn edition_unstable_gated() { + // During the period where a new edition is coming up, but not yet stable, + // this test will verify that it cannot be used on stable. If there is no + // next edition, it does nothing. + let next = match Edition::LATEST_UNSTABLE { + Some(next) => next, + None => { + eprintln!("Next edition is currently not available, skipping test."); + return; + } + }; + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "{}" + "#, + next + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr(&format!( + "\ +[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + feature `edition{next}` is required + + The package requires the Cargo feature called `edition{next}`, \ + but that feature is not stabilized in this version of Cargo (1.[..]). + Consider trying a newer version of Cargo (this may require the nightly release). + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#edition-{next} \ + for more information about the status of this feature. +", + next = next + )) + .run(); +} + +#[cargo_test(nightly, reason = "fundamentally always nightly")] +fn edition_unstable() { + // During the period where a new edition is coming up, but not yet stable, + // this test will verify that it can be used with `cargo-features`. If + // there is no next edition, it does nothing. + let next = match Edition::LATEST_UNSTABLE { + Some(next) => next, + None => { + eprintln!("Next edition is currently not available, skipping test."); + return; + } + }; + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + cargo-features = ["edition{next}"] + + [package] + name = "foo" + version = "0.1.0" + edition = "{next}" + "#, + next = next + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .masquerade_as_nightly_cargo(&["always_nightly"]) + .with_stderr( + "\ +[CHECKING] foo [..] +[FINISHED] [..] +", + ) + .run(); +} diff --git a/tests/testsuite/error.rs b/tests/testsuite/error.rs new file mode 100644 index 0000000..410902c --- /dev/null +++ b/tests/testsuite/error.rs @@ -0,0 +1,19 @@ +//! General error tests that don't belong anywhere else. + +use cargo_test_support::cargo_process; + +#[cargo_test] +fn internal_error() { + cargo_process("init") + .env("__CARGO_TEST_INTERNAL_ERROR", "1") + .with_status(101) + .with_stderr( + "\ +[ERROR] internal error test +[NOTE] this is an unexpected cargo internal error +[NOTE] we would appreciate a bug report: https://github.com/rust-lang/cargo/issues/ +[NOTE] cargo [..] +", + ) + .run(); +} diff --git a/tests/testsuite/features.rs b/tests/testsuite/features.rs new file mode 100644 index 0000000..848e056 --- /dev/null +++ b/tests/testsuite/features.rs @@ -0,0 +1,2084 @@ +//! Tests for `[features]` table. + +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::{Dependency, Package}; +use cargo_test_support::{basic_manifest, project}; + +#[cargo_test] +fn invalid1() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + bar = ["baz"] + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + feature `bar` includes `baz` which is neither a dependency nor another feature +", + ) + .run(); +} + +#[cargo_test] +fn same_name() { + // Feature with the same name as a dependency. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + bar = ["baz"] + baz = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("tree -f") + .arg("{p} [{f}]") + .with_stderr("") + .with_stdout( + "\ +foo v0.0.1 ([..]) [] +└── bar v1.0.0 ([..]) [] +", + ) + .run(); + + p.cargo("tree --features bar -f") + .arg("{p} [{f}]") + .with_stderr("") + .with_stdout( + "\ +foo v0.0.1 ([..]) [bar,baz] +└── bar v1.0.0 ([..]) [] +", + ) + .run(); +} + +#[cargo_test] +fn invalid3() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + bar = ["baz"] + + [dependencies.baz] + path = "foo" + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + feature `bar` includes `baz`, but `baz` is not an optional dependency + A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition. +", + ) + .run(); +} + +#[cargo_test] +fn invalid4() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + features = ["bar"] + "#, + ) + .file("src/main.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to select a version for `bar`. + ... required by package `foo v0.0.1 ([..])` +versions that meet the requirements `*` are: 0.0.1 + +the package `foo` depends on `bar`, with features: `bar` but `bar` does not have these features. + + +failed to select a version for `bar` which could resolve this conflict", + ) + .run(); + + p.change_file("Cargo.toml", &basic_manifest("foo", "0.0.1")); + + p.cargo("check --features test") + .with_status(101) + .with_stderr("error: Package `foo v0.0.1 ([..])` does not have the feature `test`") + .run(); +} + +#[cargo_test] +fn invalid5() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dev-dependencies.bar] + path = "bar" + optional = true + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + dev-dependencies are not allowed to be optional: `bar` +", + ) + .run(); +} + +#[cargo_test] +fn invalid6() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + foo = ["bar/baz"] + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check --features foo") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + feature `foo` includes `bar/baz`, but `bar` is not a dependency +", + ) + .run(); +} + +#[cargo_test] +fn invalid7() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + foo = ["bar/baz"] + bar = [] + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check --features foo") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + feature `foo` includes `bar/baz`, but `bar` is not a dependency +", + ) + .run(); +} + +#[cargo_test] +fn invalid8() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + features = ["foo/bar"] + "#, + ) + .file("src/main.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check --features foo") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + feature `foo/bar` in dependency `bar` is not allowed to contain slashes + If you want to enable features [..] +", + ) + .run(); +} + +#[cargo_test] +fn invalid9() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check --features bar") + .with_stderr( + "\ +error: Package `foo v0.0.1 ([..])` does not have feature `bar`. It has a required dependency with that name, but only optional dependencies can be used as features. +", + ).with_status(101).run(); +} + +#[cargo_test] +fn invalid10() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + features = ["baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies.baz] + path = "baz" + "#, + ) + .file("bar/src/lib.rs", "") + .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) + .file("bar/baz/src/lib.rs", "") + .build(); + + p.cargo("check").with_stderr("\ +error: failed to select a version for `bar`. + ... required by package `foo v0.0.1 ([..])` +versions that meet the requirements `*` are: 0.0.1 + +the package `foo` depends on `bar`, with features: `baz` but `bar` does not have these features. + It has a required dependency with that name, but only optional dependencies can be used as features. + + +failed to select a version for `bar` which could resolve this conflict +").with_status(101) + .run(); +} + +#[cargo_test] +fn no_transitive_dep_feature_requirement() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.derived] + path = "derived" + + [features] + default = ["derived/bar/qux"] + "#, + ) + .file( + "src/main.rs", + r#" + extern crate derived; + fn main() { derived::test(); } + "#, + ) + .file( + "derived/Cargo.toml", + r#" + [package] + name = "derived" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file("derived/src/lib.rs", "extern crate bar; pub use bar::test;") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + qux = [] + "#, + ) + .file( + "bar/src/lib.rs", + r#" + #[cfg(feature = "qux")] + pub fn test() { print!("test"); } + "#, + ) + .build(); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + multiple slashes in feature `derived/bar/qux` (included by feature `default`) are not allowed +", + ) + .run(); +} + +#[cargo_test] +fn no_feature_doesnt_build() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + optional = true + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(feature = "bar")] + extern crate bar; + #[cfg(feature = "bar")] + fn main() { bar::bar(); println!("bar") } + #[cfg(not(feature = "bar"))] + fn main() {} + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("build") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.process(&p.bin("foo")).with_stdout("").run(); + + p.cargo("build --features bar -v") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[RUNNING] `rustc --crate-name bar [..] +[DIRTY-MSVC] foo v0.0.1 ([CWD]): the list of features changed +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.process(&p.bin("foo")).with_stdout("bar\n").run(); +} + +#[cargo_test] +fn default_feature_pulled_in() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["bar"] + + [dependencies.bar] + path = "bar" + optional = true + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(feature = "bar")] + extern crate bar; + #[cfg(feature = "bar")] + fn main() { bar::bar(); println!("bar") } + #[cfg(not(feature = "bar"))] + fn main() {} + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("build") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.process(&p.bin("foo")).with_stdout("bar\n").run(); + + p.cargo("build --no-default-features -v") + .with_stderr( + "\ +[DIRTY-MSVC] foo v0.0.1 ([CWD]): the list of features changed +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.process(&p.bin("foo")).with_stdout("").run(); +} + +#[cargo_test] +fn cyclic_feature() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["default"] + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr("[ERROR] cyclic feature dependency: feature `default` depends on itself") + .run(); +} + +#[cargo_test] +fn cyclic_feature2() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + foo = ["bar"] + bar = ["foo"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn groups_on_groups_on_groups() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["f1"] + f1 = ["f2", "bar"] + f2 = ["f3", "f4"] + f3 = ["f5", "f6", "baz"] + f4 = ["f5", "f7"] + f5 = ["f6"] + f6 = ["f7"] + f7 = ["bar"] + + [dependencies.bar] + path = "bar" + optional = true + + [dependencies.baz] + path = "baz" + optional = true + "#, + ) + .file( + "src/main.rs", + r#" + #[allow(unused_extern_crates)] + extern crate bar; + #[allow(unused_extern_crates)] + extern crate baz; + fn main() {} + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..]) +[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn many_cli_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + optional = true + + [dependencies.baz] + path = "baz" + optional = true + "#, + ) + .file( + "src/main.rs", + r#" + #[allow(unused_extern_crates)] + extern crate bar; + #[allow(unused_extern_crates)] + extern crate baz; + fn main() {} + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check --features") + .arg("bar baz") + .with_stderr( + "\ +[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..]) +[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn union_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.d1] + path = "d1" + features = ["f1"] + [dependencies.d2] + path = "d2" + features = ["f2"] + "#, + ) + .file( + "src/main.rs", + r#" + #[allow(unused_extern_crates)] + extern crate d1; + extern crate d2; + fn main() { + d2::f1(); + d2::f2(); + } + "#, + ) + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [features] + f1 = ["d2"] + + [dependencies.d2] + path = "../d2" + features = ["f1"] + optional = true + "#, + ) + .file("d1/src/lib.rs", "") + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.1" + authors = [] + + [features] + f1 = [] + f2 = [] + "#, + ) + .file( + "d2/src/lib.rs", + r#" + #[cfg(feature = "f1")] pub fn f1() {} + #[cfg(feature = "f2")] pub fn f2() {} + "#, + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] d2 v0.0.1 ([CWD]/d2) +[CHECKING] d1 v0.0.1 ([CWD]/d1) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn many_features_no_rebuilds() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + authors = [] + + [dependencies.a] + path = "a" + features = ["fall"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + authors = [] + + [features] + ftest = [] + ftest2 = [] + fall = ["ftest", "ftest2"] + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] a v0.1.0 ([CWD]/a) +[CHECKING] b v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.root().move_into_the_past(); + + p.cargo("check -v") + .with_stderr( + "\ +[FRESH] a v0.1.0 ([..]/a) +[FRESH] b v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +// Tests that all cmd lines work with `--features ""` +#[cargo_test] +fn empty_features() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + p.cargo("check --features").arg("").run(); +} + +// Tests that all cmd lines work with `--features ""` +#[cargo_test] +fn transitive_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + foo = ["bar/baz"] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() { bar::baz(); }") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + baz = [] + "#, + ) + .file( + "bar/src/lib.rs", + r#"#[cfg(feature = "baz")] pub fn baz() {}"#, + ) + .build(); + + p.cargo("check --features foo").run(); +} + +#[cargo_test] +fn everything_in_the_lockfile() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + f1 = ["d1/f1"] + f2 = ["d2"] + + [dependencies.d1] + path = "d1" + [dependencies.d2] + path = "d2" + optional = true + [dependencies.d3] + path = "d3" + optional = true + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [features] + f1 = [] + "#, + ) + .file("d1/src/lib.rs", "") + .file("d2/Cargo.toml", &basic_manifest("d2", "0.0.2")) + .file("d2/src/lib.rs", "") + .file( + "d3/Cargo.toml", + r#" + [package] + name = "d3" + version = "0.0.3" + authors = [] + + [features] + f3 = [] + "#, + ) + .file("d3/src/lib.rs", "") + .build(); + + p.cargo("fetch").run(); + let lockfile = p.read_lockfile(); + assert!( + lockfile.contains(r#"name = "d1""#), + "d1 not found\n{}", + lockfile + ); + assert!( + lockfile.contains(r#"name = "d2""#), + "d2 not found\n{}", + lockfile + ); + assert!( + lockfile.contains(r#"name = "d3""#), + "d3 not found\n{}", + lockfile + ); +} + +#[cargo_test] +fn no_rebuild_when_frobbing_default_feature() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + a = { path = "a" } + b = { path = "b" } + "#, + ) + .file("src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + authors = [] + + [dependencies] + a = { path = "../a", features = ["f1"], default-features = false } + "#, + ) + .file("b/src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + authors = [] + + [features] + default = ["f1"] + f1 = [] + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("check").run(); + p.cargo("check").with_stdout("").run(); + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn unions_work_with_no_default_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + a = { path = "a" } + b = { path = "b" } + "#, + ) + .file("src/lib.rs", "extern crate a; pub fn foo() { a::a(); }") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + authors = [] + + [dependencies] + a = { path = "../a", features = [], default-features = false } + "#, + ) + .file("b/src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + authors = [] + + [features] + default = ["f1"] + f1 = [] + "#, + ) + .file("a/src/lib.rs", r#"#[cfg(feature = "f1")] pub fn a() {}"#) + .build(); + + p.cargo("check").run(); + p.cargo("check").with_stdout("").run(); + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn optional_and_dev_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.1.0" + authors = [] + + [dependencies] + foo = { path = "foo", optional = true } + [dev-dependencies] + foo = { path = "foo" } + "#, + ) + .file("src/lib.rs", "") + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] test v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn activating_feature_activates_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.1.0" + authors = [] + + [dependencies] + foo = { path = "foo", optional = true } + + [features] + a = ["foo/a"] + "#, + ) + .file( + "src/lib.rs", + "extern crate foo; pub fn bar() { foo::bar(); }", + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [features] + a = [] + "#, + ) + .file("foo/src/lib.rs", r#"#[cfg(feature = "a")] pub fn bar() {}"#) + .build(); + + p.cargo("check --features a -v").run(); +} + +#[cargo_test] +fn dep_feature_in_cmd_line() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.derived] + path = "derived" + "#, + ) + .file( + "src/main.rs", + r#" + extern crate derived; + fn main() { derived::test(); } + "#, + ) + .file( + "derived/Cargo.toml", + r#" + [package] + name = "derived" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + + [features] + default = [] + derived-feat = ["bar/some-feat"] + "#, + ) + .file("derived/src/lib.rs", "extern crate bar; pub use bar::test;") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + some-feat = [] + "#, + ) + .file( + "bar/src/lib.rs", + r#" + #[cfg(feature = "some-feat")] + pub fn test() { print!("test"); } + "#, + ) + .build(); + + // The foo project requires that feature "some-feat" in "bar" is enabled. + // Building without any features enabled should fail: + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]unresolved import `bar::test`") + .run(); + + // We should be able to enable the feature "derived-feat", which enables "some-feat", + // on the command line. The feature is enabled, thus building should be successful: + p.cargo("check --features derived/derived-feat").run(); + + // Trying to enable features of transitive dependencies is an error + p.cargo("check --features bar/some-feat") + .with_status(101) + .with_stderr("error: package `foo v0.0.1 ([..])` does not have a dependency named `bar`") + .run(); + + // Hierarchical feature specification should still be disallowed + p.cargo("check --features derived/bar/some-feat") + .with_status(101) + .with_stderr("[ERROR] multiple slashes in feature `derived/bar/some-feat` is not allowed") + .run(); +} + +#[cargo_test] +fn all_features_flag_enables_all_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + foo = [] + bar = [] + + [dependencies.baz] + path = "baz" + optional = true + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(feature = "foo")] + pub fn foo() {} + + #[cfg(feature = "bar")] + pub fn bar() { + extern crate baz; + baz::baz(); + } + + fn main() { + foo(); + bar(); + } + "#, + ) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check --all-features").run(); +} + +#[cargo_test] +fn many_cli_features_comma_delimited() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + optional = true + + [dependencies.baz] + path = "baz" + optional = true + "#, + ) + .file( + "src/main.rs", + r#" + #[allow(unused_extern_crates)] + extern crate bar; + #[allow(unused_extern_crates)] + extern crate baz; + fn main() {} + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check --features bar,baz") + .with_stderr( + "\ +[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..]) +[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn many_cli_features_comma_and_space_delimited() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + optional = true + + [dependencies.baz] + path = "baz" + optional = true + + [dependencies.bam] + path = "bam" + optional = true + + [dependencies.bap] + path = "bap" + optional = true + "#, + ) + .file( + "src/main.rs", + r#" + #[allow(unused_extern_crates)] + extern crate bar; + #[allow(unused_extern_crates)] + extern crate baz; + #[allow(unused_extern_crates)] + extern crate bam; + #[allow(unused_extern_crates)] + extern crate bap; + fn main() {} + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .file("bam/Cargo.toml", &basic_manifest("bam", "0.0.1")) + .file("bam/src/lib.rs", "pub fn bam() {}") + .file("bap/Cargo.toml", &basic_manifest("bap", "0.0.1")) + .file("bap/src/lib.rs", "pub fn bap() {}") + .build(); + + p.cargo("check --features") + .arg("bar,baz bam bap") + .with_stderr( + "\ +[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..]) +[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..]) +[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..]) +[CHECKING] ba[..] v0.0.1 ([CWD]/ba[..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn only_dep_is_optional() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + foo = ['bar'] + + [dependencies] + bar = { version = "0.1", optional = true } + + [dev-dependencies] + bar = "0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn all_features_all_crates() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [workspace] + members = ['bar'] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + foo = [] + "#, + ) + .file("bar/src/main.rs", "#[cfg(feature = \"foo\")] fn main() {}") + .build(); + + p.cargo("check --all-features --workspace").run(); +} + +#[cargo_test] +fn feature_off_dylib() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + + [package] + name = "foo" + version = "0.0.1" + + [lib] + crate-type = ["dylib"] + + [features] + f1 = [] + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn hello() -> &'static str { + if cfg!(feature = "f1") { + "f1" + } else { + "no f1" + } + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + + [dependencies] + foo = { path = ".." } + "#, + ) + .file( + "bar/src/main.rs", + r#" + extern crate foo; + + fn main() { + assert_eq!(foo::hello(), "no f1"); + } + "#, + ) + .build(); + + // Build the dylib with `f1` feature. + p.cargo("check --features f1").run(); + // Check that building without `f1` uses a dylib without `f1`. + p.cargo("run -p bar").run(); +} + +#[cargo_test] +fn warn_if_default_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + optional = true + + [features] + default-features = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("check") + .with_stderr( + r#" +[WARNING] `default-features = [".."]` was found in [features]. Did you mean to use `default = [".."]`? +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + "#.trim(), + ).run(); +} + +#[cargo_test] +fn no_feature_for_non_optional_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(not(feature = "bar"))] + fn main() { + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + a = [] + "#, + ) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("check --features bar/a").run(); +} + +#[cargo_test] +fn features_option_given_twice() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + b = [] + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(all(feature = "a", feature = "b"))] + fn main() {} + "#, + ) + .build(); + + p.cargo("check --features a --features b").run(); +} + +#[cargo_test] +fn multi_multi_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + b = [] + c = [] + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(all(feature = "a", feature = "b", feature = "c"))] + fn main() {} + "#, + ) + .build(); + + p.cargo("check --features a --features").arg("b c").run(); +} + +#[cargo_test] +fn cli_parse_ok() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(feature = "a")] + fn main() { + assert_eq!(std::env::args().nth(1).unwrap(), "b"); + } + "#, + ) + .build(); + + p.cargo("run --features a b").run(); +} + +#[cargo_test] +fn all_features_virtual_ws() { + // What happens with `--all-features` in the root of a virtual workspace. + // Some of this behavior is a little strange (member dependencies also + // have all features enabled, one might expect `f4` to be disabled). + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2018" + + [dependencies] + b = {path="../b", optional=true} + + [features] + default = ["f1"] + f1 = [] + f2 = [] + "#, + ) + .file( + "a/src/main.rs", + r#" + fn main() { + if cfg!(feature="f1") { + println!("f1"); + } + if cfg!(feature="f2") { + println!("f2"); + } + #[cfg(feature="b")] + b::f(); + } + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [features] + default = ["f3"] + f3 = [] + f4 = [] + "#, + ) + .file( + "b/src/lib.rs", + r#" + pub fn f() { + if cfg!(feature="f3") { + println!("f3"); + } + if cfg!(feature="f4") { + println!("f4"); + } + } + "#, + ) + .build(); + + p.cargo("run").with_stdout("f1\n").run(); + p.cargo("run --all-features") + .with_stdout("f1\nf2\nf3\nf4\n") + .run(); + // In `a`, it behaves differently. :( + p.cargo("run --all-features") + .cwd("a") + .with_stdout("f1\nf2\nf3\n") + .run(); +} + +#[cargo_test] +fn slash_optional_enables() { + // --features dep/feat will enable `dep` and set its feature. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = {path="dep", optional=true} + "#, + ) + .file( + "src/lib.rs", + r#" + #[cfg(not(feature="dep"))] + compile_error!("dep not set"); + "#, + ) + .file( + "dep/Cargo.toml", + r#" + [package] + name = "dep" + version = "0.1.0" + + [features] + feat = [] + "#, + ) + .file( + "dep/src/lib.rs", + r#" + #[cfg(not(feature="feat"))] + compile_error!("feat not set"); + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]dep not set[..]") + .run(); + + p.cargo("check --features dep/feat").run(); +} + +#[cargo_test] +fn registry_summary_order_doesnt_matter() { + // Checks for an issue where the resolver depended on the order of entries + // in the registry summary. If there was a non-optional dev-dependency + // that appeared before an optional normal dependency, then the resolver + // would not activate the optional dependency with a pkg/featname feature + // syntax. + Package::new("dep", "0.1.0") + .feature("feat1", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(feature="feat1")] + pub fn work() { + println!("it works"); + } + "#, + ) + .publish(); + Package::new("bar", "0.1.0") + .feature("bar_feat", &["dep/feat1"]) + .add_dep(Dependency::new("dep", "0.1.0").dev()) + .add_dep(Dependency::new("dep", "0.1.0").optional(true)) + .file( + "src/lib.rs", + r#" + // This will fail to compile without `dep` optional dep activated. + extern crate dep; + + pub fn doit() { + dep::work(); + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + bar = { version="0.1", features = ["bar_feat"] } + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + bar::doit(); + } + "#, + ) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] [..] +[DOWNLOADED] [..] +[COMPILING] dep v0.1.0 +[COMPILING] bar v0.1.0 +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +[RUNNING] `target/debug/foo[EXE]` +", + ) + .with_stdout("it works") + .run(); +} + +#[cargo_test] +fn nonexistent_required_features() { + Package::new("required_dependency", "0.1.0") + .feature("simple", &[]) + .publish(); + Package::new("optional_dependency", "0.2.0") + .feature("optional", &[]) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [features] + existing = [] + fancy = ["optional_dependency"] + [dependencies] + required_dependency = { version = "0.1", optional = false} + optional_dependency = { version = "0.2", optional = true} + [[example]] + name = "ololo" + required-features = ["not_present", + "existing", + "fancy", + "required_dependency/not_existing", + "required_dependency/simple", + "optional_dependency/optional", + "not_specified_dependency/some_feature"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("examples/ololo.rs", "fn main() {}") + .build(); + + p.cargo("check --examples") + .with_stderr_contains( + "\ +[WARNING] invalid feature `not_present` in required-features of target `ololo`: \ + `not_present` is not present in [features] section +[WARNING] invalid feature `required_dependency/not_existing` in required-features \ + of target `ololo`: feature `not_existing` does not exist in package \ + `required_dependency v0.1.0` +[WARNING] invalid feature `not_specified_dependency/some_feature` in required-features \ + of target `ololo`: dependency `not_specified_dependency` does not exist +", + ) + .run(); +} + +#[cargo_test] +fn invalid_feature_names_warning() { + // Warnings for more restricted feature syntax. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + # Some valid, but unusual names, shouldn't warn. + "c++17" = [] + "128bit" = [] + "_foo" = [] + "feat-name" = [] + "feat_name" = [] + "foo.bar" = [] + + # Invalid names. + "+foo" = [] + "-foo" = [] + ".foo" = [] + "foo:bar" = [] + "foo?" = [] + "?foo" = [] + "ⒶⒷⒸ" = [] + "a¼" = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Unfortunately the warnings are duplicated due to the Summary being + // loaded twice (once in the Workspace, and once in PackageRegistry) and + // Cargo does not have a de-duplication system. This should probably be + // OK, since I'm not expecting this to affect anyone. + p.cargo("check") + .with_stderr("\ +[WARNING] invalid character `+` in feature `+foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #8813 , and please leave a comment if this will be a problem for your project. +[WARNING] invalid character `-` in feature `-foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #8813 , and please leave a comment if this will be a problem for your project. +[WARNING] invalid character `.` in feature `.foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #8813 , and please leave a comment if this will be a problem for your project. +[WARNING] invalid character `?` in feature `?foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #8813 , and please leave a comment if this will be a problem for your project. +[WARNING] invalid character `¼` in feature `a¼` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #8813 , and please leave a comment if this will be a problem for your project. +[WARNING] invalid character `:` in feature `foo:bar` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #8813 , and please leave a comment if this will be a problem for your project. +[WARNING] invalid character `?` in feature `foo?` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #8813 , and please leave a comment if this will be a problem for your project. +[WARNING] invalid character `Ⓐ` in feature `ⒶⒷⒸ` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #8813 , and please leave a comment if this will be a problem for your project. +[WARNING] invalid character `Ⓑ` in feature `ⒶⒷⒸ` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #8813 , and please leave a comment if this will be a problem for your project. +[WARNING] invalid character `Ⓒ` in feature `ⒶⒷⒸ` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) +This was previously accepted but is being phased out; it will become a hard error in a future release. +For more information, see issue #8813 , and please leave a comment if this will be a problem for your project. +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +") + .run(); +} + +#[cargo_test] +fn invalid_feature_names_error() { + // Errors for more restricted feature syntax. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + "foo/bar" = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + feature named `foo/bar` is not allowed to contain slashes +", + ) + .run(); +} + +#[cargo_test] +fn default_features_conflicting_warning() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + a = { path = "a", features = ["f1"], default-features = false, default_features = false } + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + authors = [] + + [features] + default = ["f1"] + f1 = [] + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr_contains( +"[WARNING] conflicting between `default-features` and `default_features` in the `a` dependency.\n + `default_features` is ignored and not recommended for use in the future" + ) + .run(); +} diff --git a/tests/testsuite/features2.rs b/tests/testsuite/features2.rs new file mode 100644 index 0000000..494c83f --- /dev/null +++ b/tests/testsuite/features2.rs @@ -0,0 +1,2553 @@ +//! Tests for the new feature resolver. + +use cargo_test_support::cross_compile::{self, alternate}; +use cargo_test_support::install::cargo_home; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::publish::validate_crate_contents; +use cargo_test_support::registry::{Dependency, Package}; +use cargo_test_support::{basic_manifest, cargo_process, project, rustc_host, Project}; +use std::fs::File; + +/// Switches Cargo.toml to use `resolver = "2"`. +pub fn switch_to_resolver_2(p: &Project) { + let mut manifest = p.read_file("Cargo.toml"); + if manifest.contains("resolver =") { + panic!("did not expect manifest to already contain a resolver setting"); + } + if let Some(index) = manifest.find("[workspace]\n") { + manifest.insert_str(index + 12, "resolver = \"2\"\n"); + } else if let Some(index) = manifest.find("[package]\n") { + manifest.insert_str(index + 10, "resolver = \"2\"\n"); + } else { + panic!("expected [package] or [workspace] in manifest"); + } + p.change_file("Cargo.toml", &manifest); +} + +#[cargo_test] +fn inactivate_targets() { + // Basic test of `itarget`. A shared dependency where an inactive [target] + // changes the features. + Package::new("common", "1.0.0") + .feature("f1", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(feature = "f1")] + compile_error!("f1 should not activate"); + "#, + ) + .publish(); + + Package::new("bar", "1.0.0") + .add_dep( + Dependency::new("common", "1.0") + .target("cfg(whatever)") + .enable_features(&["f1"]), + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + common = "1.0" + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]f1 should not activate[..]") + .run(); + + switch_to_resolver_2(&p); + p.cargo("check").run(); +} + +#[cargo_test] +fn inactive_target_optional() { + // Activating optional [target] dependencies for inactivate target. + Package::new("common", "1.0.0") + .feature("f1", &[]) + .feature("f2", &[]) + .feature("f3", &[]) + .feature("f4", &[]) + .file( + "src/lib.rs", + r#" + pub fn f() { + if cfg!(feature="f1") { println!("f1"); } + if cfg!(feature="f2") { println!("f2"); } + if cfg!(feature="f3") { println!("f3"); } + if cfg!(feature="f4") { println!("f4"); } + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + common = "1.0" + + [target.'cfg(whatever)'.dependencies] + dep1 = {path='dep1', optional=true} + dep2 = {path='dep2', optional=true, features=["f3"]} + common = {version="1.0", optional=true, features=["f4"]} + + [features] + foo1 = ["dep1/f2"] + foo2 = ["dep2"] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + if cfg!(feature="foo1") { println!("foo1"); } + if cfg!(feature="foo2") { println!("foo2"); } + if cfg!(feature="dep1") { println!("dep1"); } + if cfg!(feature="dep2") { println!("dep2"); } + if cfg!(feature="common") { println!("common"); } + common::f(); + } + "#, + ) + .file( + "dep1/Cargo.toml", + r#" + [package] + name = "dep1" + version = "0.1.0" + + [dependencies] + common = {version="1.0", features=["f1"]} + + [features] + f2 = ["common/f2"] + "#, + ) + .file( + "dep1/src/lib.rs", + r#"compile_error!("dep1 should not build");"#, + ) + .file( + "dep2/Cargo.toml", + r#" + [package] + name = "dep2" + version = "0.1.0" + + [dependencies] + common = "1.0" + + [features] + f3 = ["common/f3"] + "#, + ) + .file( + "dep2/src/lib.rs", + r#"compile_error!("dep2 should not build");"#, + ) + .build(); + + p.cargo("run --all-features") + .with_stdout("foo1\nfoo2\ndep1\ndep2\ncommon\nf1\nf2\nf3\nf4\n") + .run(); + p.cargo("run --features dep1") + .with_stdout("dep1\nf1\n") + .run(); + p.cargo("run --features foo1") + .with_stdout("foo1\ndep1\nf1\nf2\n") + .run(); + p.cargo("run --features dep2") + .with_stdout("dep2\nf3\n") + .run(); + p.cargo("run --features common") + .with_stdout("common\nf4\n") + .run(); + + switch_to_resolver_2(&p); + p.cargo("run --all-features") + .with_stdout("foo1\nfoo2\ndep1\ndep2\ncommon") + .run(); + p.cargo("run --features dep1").with_stdout("dep1\n").run(); + p.cargo("run --features foo1").with_stdout("foo1\n").run(); + p.cargo("run --features dep2").with_stdout("dep2\n").run(); + p.cargo("run --features common").with_stdout("common").run(); +} + +#[cargo_test] +fn itarget_proc_macro() { + // itarget inside a proc-macro while cross-compiling + if cross_compile::disabled() { + return; + } + Package::new("hostdep", "1.0.0").publish(); + Package::new("pm", "1.0.0") + .proc_macro(true) + .target_dep("hostdep", "1.0", rustc_host()) + .file("src/lib.rs", "extern crate hostdep;") + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + pm = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Old behavior + p.cargo("check").run(); + p.cargo("check --target").arg(alternate()).run(); + + // New behavior + switch_to_resolver_2(&p); + p.cargo("check").run(); + p.cargo("check --target").arg(alternate()).run(); + // For good measure, just make sure things don't break. + p.cargo("check --target").arg(alternate()).run(); +} + +#[cargo_test] +fn decouple_host_deps() { + // Basic test for `host_dep` decouple. + Package::new("common", "1.0.0") + .feature("f1", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(feature = "f1")] + pub fn foo() {} + #[cfg(not(feature = "f1"))] + pub fn bar() {} + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [build-dependencies] + common = {version="1.0", features=["f1"]} + + [dependencies] + common = "1.0" + "#, + ) + .file( + "build.rs", + r#" + use common::foo; + fn main() {} + "#, + ) + .file("src/lib.rs", "use common::bar;") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]unresolved import `common::bar`[..]") + .run(); + + switch_to_resolver_2(&p); + p.cargo("check").run(); +} + +#[cargo_test] +fn decouple_host_deps_nested() { + // `host_dep` decouple of transitive dependencies. + Package::new("common", "1.0.0") + .feature("f1", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(feature = "f1")] + pub fn foo() {} + #[cfg(not(feature = "f1"))] + pub fn bar() {} + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [build-dependencies] + bdep = {path="bdep"} + + [dependencies] + common = "1.0" + "#, + ) + .file( + "build.rs", + r#" + use bdep::foo; + fn main() {} + "#, + ) + .file("src/lib.rs", "use common::bar;") + .file( + "bdep/Cargo.toml", + r#" + [package] + name = "bdep" + version = "0.1.0" + edition = "2018" + + [dependencies] + common = {version="1.0", features=["f1"]} + "#, + ) + .file("bdep/src/lib.rs", "pub use common::foo;") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]unresolved import `common::bar`[..]") + .run(); + + switch_to_resolver_2(&p); + p.cargo("check").run(); +} + +#[cargo_test] +fn decouple_dev_deps() { + // Basic test for `dev_dep` decouple. + Package::new("common", "1.0.0") + .feature("f1", &[]) + .feature("f2", &[]) + .file( + "src/lib.rs", + r#" + // const ensures it uses the correct dependency at *build time* + // compared to *link time*. + #[cfg(all(feature="f1", not(feature="f2")))] + pub const X: u32 = 1; + + #[cfg(all(feature="f1", feature="f2"))] + pub const X: u32 = 3; + + pub fn foo() -> u32 { + let mut res = 0; + if cfg!(feature = "f1") { + res |= 1; + } + if cfg!(feature = "f2") { + res |= 2; + } + res + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + common = {version="1.0", features=["f1"]} + + [dev-dependencies] + common = {version="1.0", features=["f2"]} + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + let expected: u32 = std::env::args().skip(1).next().unwrap().parse().unwrap(); + assert_eq!(foo::foo(), expected); + assert_eq!(foo::build_time(), expected); + assert_eq!(common::foo(), expected); + assert_eq!(common::X, expected); + } + + #[test] + fn test_bin() { + assert_eq!(foo::foo(), 3); + assert_eq!(common::foo(), 3); + assert_eq!(common::X, 3); + assert_eq!(foo::build_time(), 3); + } + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn foo() -> u32 { + common::foo() + } + + pub fn build_time() -> u32 { + common::X + } + + #[test] + fn test_lib() { + assert_eq!(foo(), 3); + assert_eq!(common::foo(), 3); + assert_eq!(common::X, 3); + } + "#, + ) + .file( + "tests/t1.rs", + r#" + #[test] + fn test_t1() { + assert_eq!(foo::foo(), 3); + assert_eq!(common::foo(), 3); + assert_eq!(common::X, 3); + assert_eq!(foo::build_time(), 3); + } + + #[test] + fn test_main() { + // Features are unified for main when run with `cargo test`, + // even with the new resolver. + let s = std::process::Command::new("target/debug/foo") + .arg("3") + .status().unwrap(); + assert!(s.success()); + } + "#, + ) + .build(); + + // Old behavior + p.cargo("run 3").run(); + p.cargo("test").run(); + + // New behavior + switch_to_resolver_2(&p); + p.cargo("run 1").run(); + p.cargo("test").run(); +} + +#[cargo_test] +fn build_script_runtime_features() { + // Check that the CARGO_FEATURE_* environment variable is set correctly. + // + // This has a common dependency between build/normal/dev-deps, and it + // queries which features it was built with in different circumstances. + Package::new("common", "1.0.0") + .feature("normal", &[]) + .feature("dev", &[]) + .feature("build", &[]) + .file( + "build.rs", + r#" + fn is_set(name: &str) -> bool { + std::env::var(name) == Ok("1".to_string()) + } + + fn main() { + let mut res = 0; + if is_set("CARGO_FEATURE_NORMAL") { + res |= 1; + } + if is_set("CARGO_FEATURE_DEV") { + res |= 2; + } + if is_set("CARGO_FEATURE_BUILD") { + res |= 4; + } + println!("cargo:rustc-cfg=RunCustomBuild=\"{}\"", res); + + let mut res = 0; + if cfg!(feature = "normal") { + res |= 1; + } + if cfg!(feature = "dev") { + res |= 2; + } + if cfg!(feature = "build") { + res |= 4; + } + println!("cargo:rustc-cfg=CustomBuild=\"{}\"", res); + } + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn foo() -> u32 { + let mut res = 0; + if cfg!(feature = "normal") { + res |= 1; + } + if cfg!(feature = "dev") { + res |= 2; + } + if cfg!(feature = "build") { + res |= 4; + } + res + } + + pub fn build_time() -> u32 { + #[cfg(RunCustomBuild="1")] return 1; + #[cfg(RunCustomBuild="3")] return 3; + #[cfg(RunCustomBuild="4")] return 4; + #[cfg(RunCustomBuild="5")] return 5; + #[cfg(RunCustomBuild="7")] return 7; + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [build-dependencies] + common = {version="1.0", features=["build"]} + + [dependencies] + common = {version="1.0", features=["normal"]} + + [dev-dependencies] + common = {version="1.0", features=["dev"]} + "#, + ) + .file( + "build.rs", + r#" + fn main() { + assert_eq!(common::foo(), common::build_time()); + println!("cargo:rustc-cfg=from_build=\"{}\"", common::foo()); + } + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn foo() -> u32 { + common::foo() + } + + pub fn build_time() -> u32 { + common::build_time() + } + + #[test] + fn test_lib() { + assert_eq!(common::foo(), common::build_time()); + assert_eq!(common::foo(), + std::env::var("CARGO_FEATURE_EXPECT").unwrap().parse().unwrap()); + } + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + assert_eq!(common::foo(), common::build_time()); + assert_eq!(common::foo(), + std::env::var("CARGO_FEATURE_EXPECT").unwrap().parse().unwrap()); + } + + #[test] + fn test_bin() { + assert_eq!(common::foo(), common::build_time()); + assert_eq!(common::foo(), + std::env::var("CARGO_FEATURE_EXPECT").unwrap().parse().unwrap()); + } + "#, + ) + .file( + "tests/t1.rs", + r#" + #[test] + fn test_t1() { + assert_eq!(common::foo(), common::build_time()); + assert_eq!(common::foo(), + std::env::var("CARGO_FEATURE_EXPECT").unwrap().parse().unwrap()); + } + + #[test] + fn test_main() { + // Features are unified for main when run with `cargo test`, + // even with the new resolver. + let s = std::process::Command::new("target/debug/foo") + .status().unwrap(); + assert!(s.success()); + } + "#, + ) + .build(); + + // Old way, unifies all 3. + p.cargo("run").env("CARGO_FEATURE_EXPECT", "7").run(); + p.cargo("test").env("CARGO_FEATURE_EXPECT", "7").run(); + + // New behavior. + switch_to_resolver_2(&p); + + // normal + build unify + p.cargo("run").env("CARGO_FEATURE_EXPECT", "1").run(); + + // dev_deps are still unified with `cargo test` + p.cargo("test").env("CARGO_FEATURE_EXPECT", "3").run(); +} + +#[cargo_test] +fn cyclical_dev_dep() { + // Check how a cyclical dev-dependency will work. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [features] + dev = [] + + [dev-dependencies] + foo = { path = '.', features = ["dev"] } + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn assert_dev(enabled: bool) { + assert_eq!(enabled, cfg!(feature="dev")); + } + + #[test] + fn test_in_lib() { + assert_dev(true); + } + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + let expected: bool = std::env::args().skip(1).next().unwrap().parse().unwrap(); + foo::assert_dev(expected); + } + "#, + ) + .file( + "tests/t1.rs", + r#" + #[test] + fn integration_links() { + foo::assert_dev(true); + // The lib linked with main.rs will also be unified. + let s = std::process::Command::new("target/debug/foo") + .arg("true") + .status().unwrap(); + assert!(s.success()); + } + "#, + ) + .build(); + + // Old way unifies features. + p.cargo("run true").run(); + // dev feature should always be enabled in tests. + p.cargo("test").run(); + + // New behavior. + switch_to_resolver_2(&p); + // Should decouple main. + p.cargo("run false").run(); + + // And this should be no different. + p.cargo("test").run(); +} + +#[cargo_test] +fn all_feature_opts() { + // All feature options at once. + Package::new("common", "1.0.0") + .feature("normal", &[]) + .feature("build", &[]) + .feature("dev", &[]) + .feature("itarget", &[]) + .file( + "src/lib.rs", + r#" + pub fn feats() -> u32 { + let mut res = 0; + if cfg!(feature="normal") { res |= 1; } + if cfg!(feature="build") { res |= 2; } + if cfg!(feature="dev") { res |= 4; } + if cfg!(feature="itarget") { res |= 8; } + res + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + common = {version = "1.0", features=["normal"]} + + [dev-dependencies] + common = {version = "1.0", features=["dev"]} + + [build-dependencies] + common = {version = "1.0", features=["build"]} + + [target.'cfg(whatever)'.dependencies] + common = {version = "1.0", features=["itarget"]} + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + expect(); + } + + fn expect() { + let expected: u32 = std::env::var("EXPECTED_FEATS").unwrap().parse().unwrap(); + assert_eq!(expected, common::feats()); + } + + #[test] + fn from_test() { + expect(); + } + "#, + ) + .build(); + + p.cargo("run").env("EXPECTED_FEATS", "15").run(); + p.cargo("test").env("EXPECTED_FEATS", "15").run(); + + // New behavior. + switch_to_resolver_2(&p); + // Only normal feature. + p.cargo("run").env("EXPECTED_FEATS", "1").run(); + + // only normal+dev + p.cargo("test").env("EXPECTED_FEATS", "5").run(); +} + +#[cargo_test] +fn required_features_host_dep() { + // Check that required-features handles build-dependencies correctly. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [[bin]] + name = "x" + required-features = ["bdep/f1"] + + [build-dependencies] + bdep = {path="bdep"} + "#, + ) + .file("build.rs", "fn main() {}") + .file( + "src/bin/x.rs", + r#" + fn main() {} + "#, + ) + .file( + "bdep/Cargo.toml", + r#" + [package] + name = "bdep" + version = "0.1.0" + + [features] + f1 = [] + "#, + ) + .file("bdep/src/lib.rs", "") + .build(); + + p.cargo("run") + .with_status(101) + .with_stderr( + "\ +[ERROR] target `x` in package `foo` requires the features: `bdep/f1` +Consider enabling them by passing, e.g., `--features=\"bdep/f1\"` +", + ) + .run(); + + // New behavior. + switch_to_resolver_2(&p); + p.cargo("run --features bdep/f1").run(); +} + +#[cargo_test] +fn disabled_shared_host_dep() { + // Check for situation where an optional dep of a shared dep is enabled in + // a normal dependency, but disabled in an optional one. The unit tree is: + // foo + // ├── foo build.rs + // | └── common (BUILD dependency, NO FEATURES) + // └── common (Normal dependency, default features) + // └── somedep + Package::new("somedep", "1.0.0") + .file( + "src/lib.rs", + r#" + pub fn f() { println!("hello from somedep"); } + "#, + ) + .publish(); + Package::new("common", "1.0.0") + .feature("default", &["somedep"]) + .add_dep(Dependency::new("somedep", "1.0").optional(true)) + .file( + "src/lib.rs", + r#" + pub fn check_somedep() -> bool { + #[cfg(feature="somedep")] + { + extern crate somedep; + somedep::f(); + true + } + #[cfg(not(feature="somedep"))] + { + println!("no somedep"); + false + } + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + edition = "2018" + resolver = "2" + + [dependencies] + common = "1.0" + + [build-dependencies] + common = {version = "1.0", default-features = false} + "#, + ) + .file( + "src/main.rs", + "fn main() { assert!(common::check_somedep()); }", + ) + .file( + "build.rs", + "fn main() { assert!(!common::check_somedep()); }", + ) + .build(); + + p.cargo("run -v").with_stdout("hello from somedep").run(); +} + +#[cargo_test] +fn required_features_inactive_dep() { + // required-features with an inactivated dep. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "2" + + [target.'cfg(whatever)'.dependencies] + bar = {path="bar"} + + [[bin]] + name = "foo" + required-features = ["feat1"] + + [features] + feat1 = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + + p.cargo("check --features=feat1") + .with_stderr("[CHECKING] foo[..]\n[FINISHED] [..]") + .run(); +} + +#[cargo_test] +fn decouple_proc_macro() { + // proc macro features are not shared + Package::new("common", "1.0.0") + .feature("somefeat", &[]) + .file( + "src/lib.rs", + r#" + pub const fn foo() -> bool { cfg!(feature="somefeat") } + #[cfg(feature="somefeat")] + pub const FEAT_ONLY_CONST: bool = true; + "#, + ) + .publish(); + Package::new("pm", "1.0.0") + .proc_macro(true) + .feature_dep("common", "1.0", &["somefeat"]) + .file( + "src/lib.rs", + r#" + extern crate proc_macro; + extern crate common; + #[proc_macro] + pub fn foo(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + assert!(common::foo()); + "".parse().unwrap() + } + "#, + ) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + edition = "2018" + + [dependencies] + pm = "1.0" + common = "1.0" + "#, + ) + .file( + "src/lib.rs", + r#" + //! Test with docs. + //! + //! ```rust + //! pm::foo!{} + //! fn main() { + //! let expected = std::env::var_os("TEST_EXPECTS_ENABLED").is_some(); + //! assert_eq!(expected, common::foo(), "common is wrong"); + //! } + //! ``` + "#, + ) + .file( + "src/main.rs", + r#" + pm::foo!{} + fn main() { + println!("it is {}", common::foo()); + } + "#, + ) + .build(); + + p.cargo("run") + .env("TEST_EXPECTS_ENABLED", "1") + .with_stdout("it is true") + .run(); + // Make sure the test is fallible. + p.cargo("test --doc") + .with_status(101) + .with_stdout_contains("[..]common is wrong[..]") + .run(); + p.cargo("test --doc").env("TEST_EXPECTS_ENABLED", "1").run(); + p.cargo("doc").run(); + assert!(p + .build_dir() + .join("doc/common/constant.FEAT_ONLY_CONST.html") + .exists()); + // cargo doc should clean in-between runs, but it doesn't, and leaves stale files. + // https://github.com/rust-lang/cargo/issues/6783 (same for removed items) + p.build_dir().join("doc").rm_rf(); + + // New behavior. + switch_to_resolver_2(&p); + p.cargo("run").with_stdout("it is false").run(); + + p.cargo("test --doc").run(); + p.cargo("doc").run(); + assert!(!p + .build_dir() + .join("doc/common/constant.FEAT_ONLY_CONST.html") + .exists()); +} + +#[cargo_test] +fn proc_macro_ws() { + // Checks for bug with proc-macro in a workspace with dependency (shouldn't panic). + // + // Note, debuginfo is explicitly requested here to preserve the intent of this non-regression + // test: that will disable the debuginfo build dependencies optimization. Otherwise, it would + // initially trigger when the crates are built independently, but rebuild them with debuginfo + // when it sees the shared build/runtime dependency when checking the complete workspace. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "pm"] + resolver = "2" + + [profile.dev.build-override] + debug = true + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + feat1 = [] + "#, + ) + .file("foo/src/lib.rs", "") + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + + [dependencies] + foo = { path = "../foo", features=["feat1"] } + "#, + ) + .file("pm/src/lib.rs", "") + .build(); + + p.cargo("check -p pm -v") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]--cfg[..]feat1[..]") + .run(); + // This may be surprising that `foo` doesn't get built separately. It is + // because pm might have other units (binaries, tests, etc.), and so the + // feature resolver must assume that normal deps get unified with it. This + // is related to the bigger issue where the features selected in a + // workspace depend on which packages are selected. + p.cargo("check --workspace -v") + .with_stderr( + "\ +[FRESH] foo v0.1.0 [..] +[FRESH] pm v0.1.0 [..] +[FINISHED] dev [..] +", + ) + .run(); + // Selecting just foo will build without unification. + p.cargo("check -p foo -v") + // Make sure `foo` is built without feat1 + .with_stderr_line_without(&["[RUNNING] `rustc --crate-name foo"], &["--cfg[..]feat1"]) + .run(); +} + +#[cargo_test] +fn has_dev_dep_for_test() { + // Check for a bug where the decision on whether or not "dev dependencies" + // should be used did not consider `check --profile=test`. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dev-dependencies] + dep = { path = 'dep', features = ['f1'] } + "#, + ) + .file( + "src/lib.rs", + r#" + #[test] + fn t1() { + dep::f(); + } + "#, + ) + .file( + "dep/Cargo.toml", + r#" + [package] + name = "dep" + version = "0.1.0" + + [features] + f1 = [] + "#, + ) + .file( + "dep/src/lib.rs", + r#" + #[cfg(feature = "f1")] + pub fn f() {} + "#, + ) + .build(); + + p.cargo("check -v") + .with_stderr( + "\ +[CHECKING] foo v0.1.0 [..] +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("check -v --profile=test") + .with_stderr( + "\ +[CHECKING] dep v0.1.0 [..] +[RUNNING] `rustc --crate-name dep [..] +[CHECKING] foo v0.1.0 [..] +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] [..] +", + ) + .run(); + + // New resolver should not be any different. + switch_to_resolver_2(&p); + p.cargo("check -v --profile=test") + .with_stderr( + "\ +[FRESH] dep [..] +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_dep_activated() { + // Build dependencies always match the host for [target.*.build-dependencies]. + if cross_compile::disabled() { + return; + } + Package::new("somedep", "1.0.0") + .file("src/lib.rs", "") + .publish(); + Package::new("targetdep", "1.0.0").publish(); + Package::new("hostdep", "1.0.0") + // Check that "for_host" is sticky. + .target_dep("somedep", "1.0", rustc_host()) + .feature("feat1", &[]) + .file( + "src/lib.rs", + r#" + extern crate somedep; + + #[cfg(not(feature="feat1"))] + compile_error!{"feat1 missing"} + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + # This should never be selected. + [target.'{}'.build-dependencies] + targetdep = "1.0" + + [target.'{}'.build-dependencies] + hostdep = {{version="1.0", features=["feat1"]}} + "#, + alternate(), + rustc_host() + ), + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + p.cargo("check").run(); + p.cargo("check --target").arg(alternate()).run(); + + // New behavior. + switch_to_resolver_2(&p); + p.cargo("check").run(); + p.cargo("check --target").arg(alternate()).run(); +} + +#[cargo_test] +fn resolver_bad_setting() { + // Unknown setting in `resolver` + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + `resolver` setting `foo` is not valid, valid options are \"1\" or \"2\" +", + ) + .run(); +} + +#[cargo_test] +fn resolver_original() { + // resolver="1" uses old unification behavior. + Package::new("common", "1.0.0") + .feature("f1", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(feature = "f1")] + compile_error!("f1 should not activate"); + "#, + ) + .publish(); + + Package::new("bar", "1.0.0") + .add_dep( + Dependency::new("common", "1.0") + .target("cfg(whatever)") + .enable_features(&["f1"]), + ) + .publish(); + + let manifest = |resolver| { + format!( + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "{}" + + [dependencies] + common = "1.0" + bar = "1.0" + "#, + resolver + ) + }; + + let p = project() + .file("Cargo.toml", &manifest("1")) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]f1 should not activate[..]") + .run(); + + p.change_file("Cargo.toml", &manifest("2")); + + p.cargo("check").run(); +} + +#[cargo_test] +fn resolver_not_both() { + // Can't specify resolver in both workspace and package. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + resolver = "2" + [package] + name = "foo" + version = "0.1.0" + resolver = "2" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + cannot specify `resolver` field in both `[workspace]` and `[package]` +", + ) + .run(); +} + +#[cargo_test] +fn resolver_ws_member() { + // Can't specify `resolver` in a ws member. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + resolver = "2" + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +warning: resolver for the non root package will be ignored, specify resolver at the workspace root: +package: [..]/foo/a/Cargo.toml +workspace: [..]/foo/Cargo.toml +[CHECKING] a v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn resolver_ws_root_and_member() { + // Check when specified in both ws root and member. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + resolver = "2" + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + resolver = "2" + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + // Ignores if they are the same. + p.cargo("check") + .with_stderr( + "\ +[CHECKING] a v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn resolver_enables_new_features() { + // resolver="2" enables all the things. + Package::new("common", "1.0.0") + .feature("normal", &[]) + .feature("build", &[]) + .feature("dev", &[]) + .feature("itarget", &[]) + .file( + "src/lib.rs", + r#" + pub fn feats() -> u32 { + let mut res = 0; + if cfg!(feature="normal") { res |= 1; } + if cfg!(feature="build") { res |= 2; } + if cfg!(feature="dev") { res |= 4; } + if cfg!(feature="itarget") { res |= 8; } + res + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + resolver = "2" + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2018" + + [dependencies] + common = {version = "1.0", features=["normal"]} + + [dev-dependencies] + common = {version = "1.0", features=["dev"]} + + [build-dependencies] + common = {version = "1.0", features=["build"]} + + [target.'cfg(whatever)'.dependencies] + common = {version = "1.0", features=["itarget"]} + "#, + ) + .file( + "a/src/main.rs", + r#" + fn main() { + expect(); + } + + fn expect() { + let expected: u32 = std::env::var("EXPECTED_FEATS").unwrap().parse().unwrap(); + assert_eq!(expected, common::feats()); + } + + #[test] + fn from_test() { + expect(); + } + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [features] + ping = [] + "#, + ) + .file( + "b/src/main.rs", + r#" + fn main() { + if cfg!(feature="ping") { + println!("pong"); + } + } + "#, + ) + .build(); + + // Only normal. + p.cargo("run --bin a") + .env("EXPECTED_FEATS", "1") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] common [..] +[COMPILING] common v1.0.0 +[COMPILING] a v0.1.0 [..] +[FINISHED] [..] +[RUNNING] `target/debug/a[EXE]` +", + ) + .run(); + + // only normal+dev + p.cargo("test").cwd("a").env("EXPECTED_FEATS", "5").run(); + + // Can specify features of packages from a different directory. + p.cargo("run -p b --features=ping") + .cwd("a") + .with_stdout("pong") + .run(); +} + +#[cargo_test] +fn install_resolve_behavior() { + // install honors the resolver behavior. + Package::new("common", "1.0.0") + .feature("f1", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(feature = "f1")] + compile_error!("f1 should not activate"); + "#, + ) + .publish(); + + Package::new("bar", "1.0.0").dep("common", "1.0").publish(); + + Package::new("foo", "1.0.0") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + resolver = "2" + + [target.'cfg(whatever)'.dependencies] + common = {version="1.0", features=["f1"]} + + [dependencies] + bar = "1.0" + + "#, + ) + .file("src/main.rs", "fn main() {}") + .publish(); + + cargo_process("install foo").run(); +} + +#[cargo_test] +fn package_includes_resolve_behavior() { + // `cargo package` will inherit the correct resolve behavior. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + resolver = "2" + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + authors = ["Zzz"] + description = "foo" + license = "MIT" + homepage = "https://example.com/" + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("package").cwd("a").run(); + + let rewritten_toml = format!( + r#"{} +[package] +name = "a" +version = "0.1.0" +authors = ["Zzz"] +description = "foo" +homepage = "https://example.com/" +license = "MIT" +resolver = "2" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ); + + let f = File::open(&p.root().join("target/package/a-0.1.0.crate")).unwrap(); + validate_crate_contents( + f, + "a-0.1.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[("Cargo.toml", &rewritten_toml)], + ); +} + +#[cargo_test] +fn tree_all() { + // `cargo tree` with the new feature resolver. + Package::new("log", "0.4.8").feature("serde", &[]).publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "2" + + [target.'cfg(whatever)'.dependencies] + log = {version="*", features=["serde"]} + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("tree --target=all") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── log v0.4.8 +", + ) + .run(); +} + +#[cargo_test] +fn shared_dep_same_but_dependencies() { + // Checks for a bug of nondeterminism. This scenario creates a shared + // dependency `dep` which needs to be built twice (once as normal, and + // once as a build dep). However, in both cases the flags to `dep` are the + // same, the only difference is what it links to. The normal dependency + // should link to `subdep` with the feature disabled, and the build + // dependency should link to it with it enabled. Crucially, the `--target` + // flag should not be specified, otherwise Unit.kind would be different + // and avoid the collision, and this bug won't manifest. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bin1", "bin2"] + resolver = "2" + "#, + ) + .file( + "bin1/Cargo.toml", + r#" + [package] + name = "bin1" + version = "0.1.0" + + [dependencies] + dep = { path = "../dep" } + "#, + ) + .file("bin1/src/main.rs", "fn main() { dep::feat_func(); }") + .file( + "bin2/Cargo.toml", + r#" + [package] + name = "bin2" + version = "0.1.0" + + [build-dependencies] + dep = { path = "../dep" } + subdep = { path = "../subdep", features = ["feat"] } + "#, + ) + .file("bin2/build.rs", "fn main() { dep::feat_func(); }") + .file("bin2/src/main.rs", "fn main() {}") + .file( + "dep/Cargo.toml", + r#" + [package] + name = "dep" + version = "0.1.0" + + [dependencies] + subdep = { path = "../subdep" } + "#, + ) + .file( + "dep/src/lib.rs", + "pub fn feat_func() { subdep::feat_func(); }", + ) + .file( + "subdep/Cargo.toml", + r#" + [package] + name = "subdep" + version = "0.1.0" + + [features] + feat = [] + "#, + ) + .file( + "subdep/src/lib.rs", + r#" + pub fn feat_func() { + #[cfg(feature = "feat")] println!("cargo:warning=feat: enabled"); + #[cfg(not(feature = "feat"))] println!("cargo:warning=feat: not enabled"); + } + "#, + ) + .build(); + + p.cargo("build --bin bin1 --bin bin2") + // unordered because bin1 and bin2 build at the same time + .with_stderr_unordered( + "\ +[COMPILING] subdep [..] +[COMPILING] dep [..] +[COMPILING] bin2 [..] +[COMPILING] bin1 [..] +warning: feat: enabled +[FINISHED] [..] +", + ) + .run(); + p.process(p.bin("bin1")) + .with_stdout("cargo:warning=feat: not enabled") + .run(); + + // Make sure everything stays cached. + p.cargo("build -v --bin bin1 --bin bin2") + .with_stderr_unordered( + "\ +[FRESH] subdep [..] +[FRESH] dep [..] +[FRESH] bin1 [..] +warning: feat: enabled +[FRESH] bin2 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn test_proc_macro() { + // Running `cargo test` on a proc-macro, with a shared dependency that has + // different features. + // + // There was a bug where `shared` was built twice (once with feature "B" + // and once without), and both copies linked into the unit test. This + // would cause a type failure when used in an intermediate dependency + // (the-macro-support). + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "runtime" + version = "0.1.0" + resolver = "2" + + [dependencies] + the-macro = { path = "the-macro", features = ['a'] } + [build-dependencies] + shared = { path = "shared", features = ['b'] } + "#, + ) + .file("src/lib.rs", "") + .file( + "the-macro/Cargo.toml", + r#" + [package] + name = "the-macro" + version = "0.1.0" + [lib] + proc-macro = true + test = false + [dependencies] + the-macro-support = { path = "../the-macro-support" } + shared = { path = "../shared" } + [dev-dependencies] + runtime = { path = ".." } + [features] + a = [] + "#, + ) + .file( + "the-macro/src/lib.rs", + " + fn _test() { + the_macro_support::foo(shared::Foo); + } + ", + ) + .file( + "the-macro-support/Cargo.toml", + r#" + [package] + name = "the-macro-support" + version = "0.1.0" + [dependencies] + shared = { path = "../shared" } + "#, + ) + .file( + "the-macro-support/src/lib.rs", + " + pub fn foo(_: shared::Foo) {} + ", + ) + .file( + "shared/Cargo.toml", + r#" + [package] + name = "shared" + version = "0.1.0" + [features] + b = [] + "#, + ) + .file("shared/src/lib.rs", "pub struct Foo;") + .build(); + p.cargo("test --manifest-path the-macro/Cargo.toml").run(); +} + +#[cargo_test] +fn doc_optional() { + // Checks for a bug where `cargo doc` was failing with an inactive target + // that enables a shared optional dependency. + Package::new("spin", "1.0.0").publish(); + Package::new("bar", "1.0.0") + .add_dep(Dependency::new("spin", "1.0").optional(true)) + .publish(); + // The enabler package enables the `spin` feature, which we don't want. + Package::new("enabler", "1.0.0") + .feature_dep("bar", "1.0", &["spin"]) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "2" + + [target.'cfg(whatever)'.dependencies] + enabler = "1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("doc") + .with_stderr_unordered( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] spin v1.0.0 [..] +[DOWNLOADED] bar v1.0.0 [..] +[DOCUMENTING] bar v1.0.0 +[CHECKING] bar v1.0.0 +[DOCUMENTING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn minimal_download() { + // Various checks that it only downloads the minimum set of dependencies + // needed in various situations. + // + // This checks several permutations of the different + // host_dep/dev_dep/itarget settings. These 3 are planned to be stabilized + // together, so there isn't much need to be concerned about how the behave + // independently. However, there are some cases where they do behave + // independently. Specifically: + // + // * `cargo test` forces dev_dep decoupling to be disabled. + // * `cargo tree --target=all` forces ignore_inactive_targets off and decouple_dev_deps off. + // * `cargo tree --target=all -e normal` forces ignore_inactive_targets off. + // + // However, `cargo tree` is a little weird because it downloads everything + // anyways. + // + // So to summarize the different permutations: + // + // dev_dep | host_dep | itarget | Notes + // --------|----------|---------|---------------------------- + // | | | -Zfeatures=compare (new resolver should behave same as old) + // | | ✓ | This scenario should not happen. + // | ✓ | | `cargo tree --target=all -Zfeatures=all`† + // | ✓ | ✓ | `cargo test` + // ✓ | | | This scenario should not happen. + // ✓ | | ✓ | This scenario should not happen. + // ✓ | ✓ | | `cargo tree --target=all -e normal -Z features=all`† + // ✓ | ✓ | ✓ | A normal build. + // + // † — However, `cargo tree` downloads everything. + Package::new("normal", "1.0.0").publish(); + Package::new("normal_pm", "1.0.0").publish(); + Package::new("normal_opt", "1.0.0").publish(); + Package::new("dev_dep", "1.0.0").publish(); + Package::new("dev_dep_pm", "1.0.0").publish(); + Package::new("build_dep", "1.0.0").publish(); + Package::new("build_dep_pm", "1.0.0").publish(); + Package::new("build_dep_opt", "1.0.0").publish(); + + Package::new("itarget_normal", "1.0.0").publish(); + Package::new("itarget_normal_pm", "1.0.0").publish(); + Package::new("itarget_dev_dep", "1.0.0").publish(); + Package::new("itarget_dev_dep_pm", "1.0.0").publish(); + Package::new("itarget_build_dep", "1.0.0").publish(); + Package::new("itarget_build_dep_pm", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + normal = "1.0" + normal_pm = "1.0" + normal_opt = { version = "1.0", optional = true } + + [dev-dependencies] + dev_dep = "1.0" + dev_dep_pm = "1.0" + + [build-dependencies] + build_dep = "1.0" + build_dep_pm = "1.0" + build_dep_opt = { version = "1.0", optional = true } + + [target.'cfg(whatever)'.dependencies] + itarget_normal = "1.0" + itarget_normal_pm = "1.0" + + [target.'cfg(whatever)'.dev-dependencies] + itarget_dev_dep = "1.0" + itarget_dev_dep_pm = "1.0" + + [target.'cfg(whatever)'.build-dependencies] + itarget_build_dep = "1.0" + itarget_build_dep_pm = "1.0" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + let clear = || { + cargo_home().join("registry/cache").rm_rf(); + cargo_home().join("registry/src").rm_rf(); + p.build_dir().rm_rf(); + }; + + // none + // Should be the same as `-Zfeatures=all` + p.cargo("check -Zfeatures=compare") + .masquerade_as_nightly_cargo(&["features=compare"]) + .with_stderr_unordered( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] normal_pm v1.0.0 [..] +[DOWNLOADED] normal v1.0.0 [..] +[DOWNLOADED] build_dep_pm v1.0.0 [..] +[DOWNLOADED] build_dep v1.0.0 [..] +[COMPILING] build_dep v1.0.0 +[COMPILING] build_dep_pm v1.0.0 +[CHECKING] normal_pm v1.0.0 +[CHECKING] normal v1.0.0 +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + clear(); + + // New behavior + switch_to_resolver_2(&p); + + // all + p.cargo("check") + .with_stderr_unordered( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] normal_pm v1.0.0 [..] +[DOWNLOADED] normal v1.0.0 [..] +[DOWNLOADED] build_dep_pm v1.0.0 [..] +[DOWNLOADED] build_dep v1.0.0 [..] +[COMPILING] build_dep v1.0.0 +[COMPILING] build_dep_pm v1.0.0 +[CHECKING] normal v1.0.0 +[CHECKING] normal_pm v1.0.0 +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + clear(); + + // This disables decouple_dev_deps. + p.cargo("test --no-run") + .with_stderr_unordered( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] normal_pm v1.0.0 [..] +[DOWNLOADED] normal v1.0.0 [..] +[DOWNLOADED] dev_dep_pm v1.0.0 [..] +[DOWNLOADED] dev_dep v1.0.0 [..] +[DOWNLOADED] build_dep_pm v1.0.0 [..] +[DOWNLOADED] build_dep v1.0.0 [..] +[COMPILING] build_dep v1.0.0 +[COMPILING] build_dep_pm v1.0.0 +[COMPILING] normal_pm v1.0.0 +[COMPILING] normal v1.0.0 +[COMPILING] dev_dep_pm v1.0.0 +[COMPILING] dev_dep v1.0.0 +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +[EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[..][EXE]) +", + ) + .run(); + clear(); + + // This disables itarget, but leaves decouple_dev_deps enabled. + p.cargo("tree -e normal --target=all") + .with_stderr_unordered( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] normal v1.0.0 [..] +[DOWNLOADED] normal_pm v1.0.0 [..] +[DOWNLOADED] build_dep v1.0.0 [..] +[DOWNLOADED] build_dep_pm v1.0.0 [..] +[DOWNLOADED] itarget_normal v1.0.0 [..] +[DOWNLOADED] itarget_normal_pm v1.0.0 [..] +[DOWNLOADED] itarget_build_dep v1.0.0 [..] +[DOWNLOADED] itarget_build_dep_pm v1.0.0 [..] +", + ) + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +├── itarget_normal v1.0.0 +├── itarget_normal_pm v1.0.0 +├── normal v1.0.0 +└── normal_pm v1.0.0 +", + ) + .run(); + clear(); + + // This disables itarget and decouple_dev_deps. + p.cargo("tree --target=all") + .with_stderr_unordered( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] normal_pm v1.0.0 [..] +[DOWNLOADED] normal v1.0.0 [..] +[DOWNLOADED] itarget_normal_pm v1.0.0 [..] +[DOWNLOADED] itarget_normal v1.0.0 [..] +[DOWNLOADED] itarget_dev_dep_pm v1.0.0 [..] +[DOWNLOADED] itarget_dev_dep v1.0.0 [..] +[DOWNLOADED] itarget_build_dep_pm v1.0.0 [..] +[DOWNLOADED] itarget_build_dep v1.0.0 [..] +[DOWNLOADED] dev_dep_pm v1.0.0 [..] +[DOWNLOADED] dev_dep v1.0.0 [..] +[DOWNLOADED] build_dep_pm v1.0.0 [..] +[DOWNLOADED] build_dep v1.0.0 [..] +", + ) + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +├── itarget_normal v1.0.0 +├── itarget_normal_pm v1.0.0 +├── normal v1.0.0 +└── normal_pm v1.0.0 +[build-dependencies] +├── build_dep v1.0.0 +├── build_dep_pm v1.0.0 +├── itarget_build_dep v1.0.0 +└── itarget_build_dep_pm v1.0.0 +[dev-dependencies] +├── dev_dep v1.0.0 +├── dev_dep_pm v1.0.0 +├── itarget_dev_dep v1.0.0 +└── itarget_dev_dep_pm v1.0.0 +", + ) + .run(); + clear(); +} + +#[cargo_test] +fn pm_with_int_shared() { + // This is a somewhat complex scenario of a proc-macro in a workspace with + // an integration test where the proc-macro is used for other things, and + // *everything* is built at once (`--workspace --all-targets + // --all-features`). There was a bug where the UnitFor settings were being + // incorrectly computed based on the order that the graph was traversed. + // + // There are some uncertainties about exactly how proc-macros should behave + // with `--workspace`, see https://github.com/rust-lang/cargo/issues/8312. + // + // This uses a const-eval hack to do compile-time feature checking. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "pm", "shared"] + resolver = "2" + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + pm = { path = "../pm" } + shared = { path = "../shared", features = ["norm-feat"] } + "#, + ) + .file( + "foo/src/lib.rs", + r#" + // foo->shared always has both features set + const _CHECK: [(); 0] = [(); 0-!(shared::FEATS==3) as usize]; + "#, + ) + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + + [dependencies] + shared = { path = "../shared", features = ["host-feat"] } + "#, + ) + .file( + "pm/src/lib.rs", + r#" + // pm->shared always has just host + const _CHECK: [(); 0] = [(); 0-!(shared::FEATS==1) as usize]; + "#, + ) + .file( + "pm/tests/pm_test.rs", + r#" + // integration test gets both set + const _CHECK: [(); 0] = [(); 0-!(shared::FEATS==3) as usize]; + "#, + ) + .file( + "shared/Cargo.toml", + r#" + [package] + name = "shared" + version = "0.1.0" + + [features] + norm-feat = [] + host-feat = [] + "#, + ) + .file( + "shared/src/lib.rs", + r#" + pub const FEATS: u32 = { + if cfg!(feature="norm-feat") && cfg!(feature="host-feat") { + 3 + } else if cfg!(feature="norm-feat") { + 2 + } else if cfg!(feature="host-feat") { + 1 + } else { + 0 + } + }; + "#, + ) + .build(); + + p.cargo("build --workspace --all-targets --all-features -v") + .with_stderr_unordered( + "\ +[COMPILING] shared [..] +[RUNNING] `rustc --crate-name shared [..]--crate-type lib [..] +[RUNNING] `rustc --crate-name shared [..]--crate-type lib [..] +[RUNNING] `rustc --crate-name shared [..]--test[..] +[COMPILING] pm [..] +[RUNNING] `rustc --crate-name pm [..]--crate-type proc-macro[..] +[RUNNING] `rustc --crate-name pm [..]--test[..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..]--test[..] +[RUNNING] `rustc --crate-name pm_test [..]--test[..] +[RUNNING] `rustc --crate-name foo [..]--crate-type lib[..] +[FINISHED] [..] +", + ) + .run(); + + // And again, should stay fresh. + p.cargo("build --workspace --all-targets --all-features -v") + .with_stderr_unordered( + "\ +[FRESH] shared [..] +[FRESH] pm [..] +[FRESH] foo [..] +[FINISHED] [..]", + ) + .run(); +} + +#[cargo_test] +fn doc_proc_macro() { + // Checks for a bug when documenting a proc-macro with a dependency. The + // doc unit builder was not carrying the "for host" setting through the + // dependencies, and the `pm-dep` dependency was causing a panic because + // it was looking for target features instead of host features. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "2" + + [dependencies] + pm = { path = "pm" } + "#, + ) + .file("src/lib.rs", "") + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + + [dependencies] + pm-dep = { path = "../pm-dep" } + "#, + ) + .file("pm/src/lib.rs", "") + .file("pm-dep/Cargo.toml", &basic_manifest("pm-dep", "0.1.0")) + .file("pm-dep/src/lib.rs", "") + .build(); + + // Unfortunately this cannot check the output because what it prints is + // nondeterministic. Sometimes it says "Compiling pm-dep" and sometimes + // "Checking pm-dep". This is because it is both building it and checking + // it in parallel (building so it can build the proc-macro, and checking + // so rustdoc can load it). + p.cargo("doc").run(); +} + +#[cargo_test] +fn edition_2021_default_2() { + // edition = 2021 defaults to v2 resolver. + Package::new("common", "1.0.0") + .feature("f1", &[]) + .file("src/lib.rs", "") + .publish(); + + Package::new("bar", "1.0.0") + .add_dep( + Dependency::new("common", "1.0") + .target("cfg(whatever)") + .enable_features(&["f1"]), + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + common = "1.0" + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // First without edition. + p.cargo("tree -f") + .arg("{p} feats:{f}") + .with_stdout( + "\ +foo v0.1.0 [..] +├── bar v1.0.0 feats: +└── common v1.0.0 feats:f1 +", + ) + .run(); + + p.change_file( + "Cargo.toml", + r#" + cargo-features = ["edition2021"] + + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + + [dependencies] + common = "1.0" + bar = "1.0" + "#, + ); + + // Importantly, this does not include `f1` on `common`. + p.cargo("tree -f") + .arg("{p} feats:{f}") + .with_stdout( + "\ +foo v0.1.0 [..] +├── bar v1.0.0 feats: +└── common v1.0.0 feats: +", + ) + .run(); +} + +#[cargo_test] +fn all_features_merges_with_features() { + Package::new("dep", "0.1.0") + .feature("feat1", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(feature="feat1")] + pub fn work() { + println!("it works"); + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [features] + a = [] + + [dependencies] + dep = "0.1" + + [[example]] + name = "ex" + required-features = ["a", "dep/feat1"] + "#, + ) + .file( + "examples/ex.rs", + r#" + fn main() { + dep::work(); + } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("run --example ex --all-features --features dep/feat1") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] [..] +[COMPILING] dep v0.1.0 +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +[RUNNING] `target/debug/examples/ex[EXE]` +", + ) + .with_stdout("it works") + .run(); + + switch_to_resolver_2(&p); + + p.cargo("run --example ex --all-features --features dep/feat1") + .with_stderr( + "\ +[FINISHED] [..] +[RUNNING] `target/debug/examples/ex[EXE]` +", + ) + .with_stdout("it works") + .run(); +} + +#[cargo_test] +fn dep_with_optional_host_deps_activated() { + // To prevent regression like rust-lang/cargo#11330 + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + + [dependencies] + serde = { path = "serde", features = ["derive", "build"] } + "#, + ) + .file("src/lib.rs", "") + .file( + "serde/Cargo.toml", + r#" + [package] + name = "serde" + version = "0.1.0" + edition = "2021" + + [dependencies] + serde_derive = { path = "../serde_derive", optional = true } + + [build-dependencies] + serde_build = { path = "../serde_build", optional = true } + + [features] + derive = ["dep:serde_derive"] + build = ["dep:serde_build"] + "#, + ) + .file("serde/src/lib.rs", "") + .file("serde/build.rs", "fn main() {}") + .file( + "serde_derive/Cargo.toml", + r#" + [package] + name = "serde_derive" + version = "0.1.0" + edition = "2021" + + [lib] + proc-macro = true + "#, + ) + .file("serde_derive/src/lib.rs", "") + .file( + "serde_build/Cargo.toml", + &basic_manifest("serde_build", "0.1.0"), + ) + .file("serde_build/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[COMPILING] serde_build v0.1.0 ([CWD]/serde_build) +[COMPILING] serde_derive v0.1.0 ([CWD]/serde_derive) +[COMPILING] serde v0.1.0 ([CWD]/serde) +[CHECKING] foo v0.1.0 ([CWD]) +[FINISHED] dev [..] +", + ) + .run(); +} diff --git a/tests/testsuite/features_namespaced.rs b/tests/testsuite/features_namespaced.rs new file mode 100644 index 0000000..26c4d0a --- /dev/null +++ b/tests/testsuite/features_namespaced.rs @@ -0,0 +1,1209 @@ +//! Tests for namespaced features. + +use super::features2::switch_to_resolver_2; +use cargo_test_support::registry::{Dependency, Package, RegistryBuilder}; +use cargo_test_support::{project, publish}; + +#[cargo_test] +fn dependency_with_crate_syntax() { + // Registry dependency uses dep: syntax. + Package::new("baz", "1.0.0").publish(); + Package::new("bar", "1.0.0") + .add_dep(Dependency::new("baz", "1.0").optional(true)) + .feature("feat", &["dep:baz"]) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {version="1.0", features=["feat"]} + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] [..] +[DOWNLOADED] [..] +[CHECKING] baz v1.0.0 +[CHECKING] bar v1.0.0 +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn namespaced_invalid_feature() { + // Specifies a feature that doesn't exist. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + bar = ["baz"] + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + feature `bar` includes `baz` which is neither a dependency nor another feature +", + ) + .run(); +} + +#[cargo_test] +fn namespaced_invalid_dependency() { + // Specifies a dep:name that doesn't exist. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [features] + bar = ["dep:baz"] + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + feature `bar` includes `dep:baz`, but `baz` is not listed as a dependency +", + ) + .run(); +} + +#[cargo_test] +fn namespaced_non_optional_dependency() { + // Specifies a dep:name for a dependency that is not optional. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [features] + bar = ["dep:baz"] + + [dependencies] + baz = "0.1" + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check") + + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + feature `bar` includes `dep:baz`, but `baz` is not an optional dependency + A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition. +", + ) + .run(); +} + +#[cargo_test] +fn namespaced_implicit_feature() { + // Backwards-compatible with old syntax. + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [features] + bar = ["baz"] + + [dependencies] + baz = { version = "0.1", optional = true } + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] +[CHECKING] foo v0.0.1 [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("check --features baz") + .with_stderr( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] baz v0.1.0 [..] +[CHECKING] baz v0.1.0 +[CHECKING] foo v0.0.1 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn namespaced_shadowed_dep() { + // An optional dependency is not listed in the features table, and its + // implicit feature is overridden. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [features] + baz = [] + + [dependencies] + baz = { version = "0.1", optional = true } + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + optional dependency `baz` is not included in any feature + Make sure that `dep:baz` is included in one of features in the [features] table. +", + ) + .run(); +} + +#[cargo_test] +fn namespaced_shadowed_non_optional() { + // Able to specify a feature with the same name as a required dependency. + Package::new("baz", "0.1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [features] + baz = [] + + [dependencies] + baz = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn namespaced_implicit_non_optional() { + // Includes a non-optional dependency in [features] table. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [features] + bar = ["baz"] + + [dependencies] + baz = "0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check").with_status(101).with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + feature `bar` includes `baz`, but `baz` is not an optional dependency + A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition. +", + ).run(); +} + +#[cargo_test] +fn namespaced_same_name() { + // Explicitly listing an optional dependency in the [features] table. + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [features] + baz = ["dep:baz"] + + [dependencies] + baz = { version = "0.1", optional = true } + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + if cfg!(feature="baz") { println!("baz"); } + } + "#, + ) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[UPDATING] [..] +[COMPILING] foo v0.0.1 [..] +[FINISHED] [..] +[RUNNING] [..] +", + ) + .with_stdout("") + .run(); + + p.cargo("run --features baz") + .with_stderr( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] baz v0.1.0 [..] +[COMPILING] baz v0.1.0 +[COMPILING] foo v0.0.1 [..] +[FINISHED] [..] +[RUNNING] [..] +", + ) + .with_stdout("baz") + .run(); +} + +#[cargo_test] +fn no_implicit_feature() { + // Using `dep:` will not create an implicit feature. + Package::new("regex", "1.0.0").publish(); + Package::new("lazy_static", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + regex = { version = "1.0", optional = true } + lazy_static = { version = "1.0", optional = true } + + [features] + regex = ["dep:regex", "dep:lazy_static"] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + if cfg!(feature = "regex") { println!("regex"); } + if cfg!(feature = "lazy_static") { println!("lazy_static"); } + } + "#, + ) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[UPDATING] [..] +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +[RUNNING] `target/debug/foo[EXE]` +", + ) + .with_stdout("") + .run(); + + p.cargo("run --features regex") + .with_stderr_unordered( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] regex v1.0.0 [..] +[DOWNLOADED] lazy_static v1.0.0 [..] +[COMPILING] regex v1.0.0 +[COMPILING] lazy_static v1.0.0 +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +[RUNNING] `target/debug/foo[EXE]` +", + ) + .with_stdout("regex") + .run(); + + p.cargo("run --features lazy_static") + .with_stderr( + "\ +[ERROR] Package `foo v0.1.0 [..]` does not have feature `lazy_static`. \ +It has an optional dependency with that name, but that dependency uses the \"dep:\" \ +syntax in the features table, so it does not have an implicit feature with that name. +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn crate_syntax_bad_name() { + // "dep:bar" = [] + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version="1.0", optional=true } + + [features] + "dep:bar" = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check --features dep:bar") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at [..]/foo/Cargo.toml` + +Caused by: + feature named `dep:bar` is not allowed to start with `dep:` +", + ) + .run(); +} + +#[cargo_test] +fn crate_syntax_in_dep() { + // features = ["dep:baz"] + Package::new("baz", "1.0.0").publish(); + Package::new("bar", "1.0.0") + .add_dep(Dependency::new("baz", "1.0").optional(true)) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", features = ["dep:baz"] } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + feature `dep:baz` in dependency `bar` is not allowed to use explicit `dep:` syntax + If you want to enable [..] +", + ) + .run(); +} + +#[cargo_test] +fn crate_syntax_cli() { + // --features dep:bar + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", optional=true } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check --features dep:bar") + .with_status(101) + .with_stderr( + "\ +[ERROR] feature `dep:bar` is not allowed to use explicit `dep:` syntax +", + ) + .run(); + + switch_to_resolver_2(&p); + p.cargo("check --features dep:bar") + .with_status(101) + .with_stderr( + "\ +[ERROR] feature `dep:bar` is not allowed to use explicit `dep:` syntax +", + ) + .run(); +} + +#[cargo_test] +fn crate_required_features() { + // required-features = ["dep:bar"] + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", optional=true } + + [[bin]] + name = "foo" + required-features = ["dep:bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] invalid feature `dep:bar` in required-features of target `foo`: \ +`dep:` prefixed feature values are not allowed in required-features +", + ) + .run(); +} + +#[cargo_test] +fn json_exposed() { + // Checks that the implicit dep: values are exposed in JSON. + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", optional=true } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("metadata --no-deps") + .with_json( + r#" + { + "packages": [ + { + "name": "foo", + "version": "0.1.0", + "id": "foo 0.1.0 [..]", + "license": null, + "license_file": null, + "description": null, + "homepage": null, + "documentation": null, + "source": null, + "dependencies": "{...}", + "targets": "{...}", + "features": { + "bar": ["dep:bar"] + }, + "manifest_path": "[..]foo/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "edition": "2015", + "links": null + } + ], + "workspace_members": "{...}", + "resolve": null, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]foo", + "metadata": null + } + "#, + ) + .run(); +} + +#[cargo_test] +fn crate_feature_with_explicit() { + // crate_name/feat_name syntax where crate_name already has a feature defined. + // NOTE: I don't know if this is actually ideal behavior. + Package::new("bar", "1.0.0") + .feature("bar_feat", &[]) + .file( + "src/lib.rs", + r#" + #[cfg(not(feature="bar_feat"))] + compile_error!("bar_feat is not enabled"); + "#, + ) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version="1.0", optional = true } + + [features] + f1 = ["bar/bar_feat"] + bar = ["dep:bar", "f2"] + f2 = [] + "#, + ) + .file( + "src/lib.rs", + r#" + #[cfg(not(feature="bar"))] + compile_error!("bar should be enabled"); + + #[cfg(not(feature="f2"))] + compile_error!("f2 should be enabled"); + "#, + ) + .build(); + + p.cargo("check --features f1") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 [..] +[CHECKING] bar v1.0.0 +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn optional_explicit_without_crate() { + // "feat" syntax when there is no implicit "feat" feature because it is + // explicitly listed elsewhere. + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", optional = true } + + [features] + feat1 = ["dep:bar"] + feat2 = ["bar"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at [..] + +Caused by: + feature `feat2` includes `bar`, but `bar` is an optional dependency without an implicit feature + Use `dep:bar` to enable the dependency. +", + ) + .run(); +} + +#[cargo_test] +fn tree() { + Package::new("baz", "1.0.0").publish(); + Package::new("bar", "1.0.0") + .add_dep(Dependency::new("baz", "1.0").optional(true)) + .feature("feat1", &["dep:baz"]) + .feature("feat2", &[]) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", features = ["feat1"], optional=true } + + [features] + a = ["bar/feat2"] + bar = ["dep:bar"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree -e features") + .with_stdout("foo v0.1.0 ([ROOT]/foo)") + .run(); + + p.cargo("tree -e features --features a") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +├── bar feature \"default\" +│ └── bar v1.0.0 +│ └── baz feature \"default\" +│ └── baz v1.0.0 +└── bar feature \"feat1\" + └── bar v1.0.0 (*) +", + ) + .run(); + + p.cargo("tree -e features --features a -i bar") + .with_stdout( + "\ +bar v1.0.0 +├── bar feature \"default\" +│ └── foo v0.1.0 ([ROOT]/foo) +│ ├── foo feature \"a\" (command-line) +│ ├── foo feature \"bar\" +│ │ └── foo feature \"a\" (command-line) +│ └── foo feature \"default\" (command-line) +├── bar feature \"feat1\" +│ └── foo v0.1.0 ([ROOT]/foo) (*) +└── bar feature \"feat2\" + └── foo feature \"a\" (command-line) +", + ) + .run(); + + p.cargo("tree -e features --features bar") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +├── bar feature \"default\" +│ └── bar v1.0.0 +│ └── baz feature \"default\" +│ └── baz v1.0.0 +└── bar feature \"feat1\" + └── bar v1.0.0 (*) +", + ) + .run(); + + p.cargo("tree -e features --features bar -i bar") + .with_stdout( + "\ +bar v1.0.0 +├── bar feature \"default\" +│ └── foo v0.1.0 ([ROOT]/foo) +│ ├── foo feature \"bar\" (command-line) +│ └── foo feature \"default\" (command-line) +└── bar feature \"feat1\" + └── foo v0.1.0 ([ROOT]/foo) (*) +", + ) + .run(); +} + +#[cargo_test] +fn tree_no_implicit() { + // tree without an implicit feature + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", optional=true } + + [features] + a = ["dep:bar"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree -e features") + .with_stdout("foo v0.1.0 ([ROOT]/foo)") + .run(); + + p.cargo("tree -e features --all-features") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +└── bar feature \"default\" + └── bar v1.0.0 +", + ) + .run(); + + p.cargo("tree -e features -i bar --all-features") + .with_stdout( + "\ +bar v1.0.0 +└── bar feature \"default\" + └── foo v0.1.0 ([ROOT]/foo) + ├── foo feature \"a\" (command-line) + └── foo feature \"default\" (command-line) +", + ) + .run(); +} + +#[cargo_test] +fn publish_no_implicit() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + // Does not include implicit features or dep: syntax on publish. + Package::new("opt-dep1", "1.0.0").publish(); + Package::new("opt-dep2", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + description = "foo" + license = "MIT" + homepage = "https://example.com/" + + [dependencies] + opt-dep1 = { version = "1.0", optional = true } + opt-dep2 = { version = "1.0", optional = true } + + [features] + feat = ["opt-dep1"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] +[UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [ + { + "default_features": true, + "features": [], + "kind": "normal", + "name": "opt-dep1", + "optional": true, + "target": null, + "version_req": "^1.0" + }, + { + "default_features": true, + "features": [], + "kind": "normal", + "name": "opt-dep2", + "optional": true, + "target": null, + "version_req": "^1.0" + } + ], + "description": "foo", + "documentation": null, + "features": { + "feat": ["opt-dep1"] + }, + "homepage": "https://example.com/", + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": null, + "vers": "0.1.0" + } + "#, + "foo-0.1.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[( + "Cargo.toml", + &format!( + r#"{} +[package] +name = "foo" +version = "0.1.0" +description = "foo" +homepage = "https://example.com/" +license = "MIT" + +[dependencies.opt-dep1] +version = "1.0" +optional = true + +[dependencies.opt-dep2] +version = "1.0" +optional = true + +[features] +feat = ["opt-dep1"] +"#, + cargo::core::package::MANIFEST_PREAMBLE + ), + )], + ); +} + +#[cargo_test] +fn publish() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + // Publish behavior with explicit dep: syntax. + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + description = "foo" + license = "MIT" + homepage = "https://example.com/" + + [dependencies] + bar = { version = "1.0", optional = true } + + [features] + feat1 = [] + feat2 = ["dep:bar"] + feat3 = ["feat2"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.1.0 [..] +[VERIFYING] foo v0.1.0 [..] +[UPDATING] [..] +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [ + { + "default_features": true, + "features": [], + "kind": "normal", + "name": "bar", + "optional": true, + "target": null, + "version_req": "^1.0" + } + ], + "description": "foo", + "documentation": null, + "features": { + "feat1": [], + "feat2": ["dep:bar"], + "feat3": ["feat2"] + }, + "homepage": "https://example.com/", + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": null, + "vers": "0.1.0" + } + "#, + "foo-0.1.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[( + "Cargo.toml", + &format!( + r#"{} +[package] +name = "foo" +version = "0.1.0" +description = "foo" +homepage = "https://example.com/" +license = "MIT" + +[dependencies.bar] +version = "1.0" +optional = true + +[features] +feat1 = [] +feat2 = ["dep:bar"] +feat3 = ["feat2"] +"#, + cargo::core::package::MANIFEST_PREAMBLE + ), + )], + ); +} + +#[cargo_test] +fn namespaced_feature_together() { + // Check for an error when `dep:` is used with `/` + Package::new("bar", "1.0.0") + .feature("bar-feat", &[]) + .publish(); + + // Non-optional shouldn't have extra err. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + + [features] + f1 = ["dep:bar/bar-feat"] + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/` + To fix this, remove the `dep:` prefix. +", + ) + .run(); + + // Weak dependency shouldn't have extra err. + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {version = "1.0", optional = true } + + [features] + f1 = ["dep:bar?/bar-feat"] + "#, + ); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + feature `f1` includes `dep:bar?/bar-feat` with both `dep:` and `/` + To fix this, remove the `dep:` prefix. +", + ) + .run(); + + // If dep: is already specified, shouldn't have extra err. + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {version = "1.0", optional = true } + + [features] + f1 = ["dep:bar", "dep:bar/bar-feat"] + "#, + ); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/` + To fix this, remove the `dep:` prefix. +", + ) + .run(); + + // Only when the other 3 cases aren't true should it give some extra help. + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {version = "1.0", optional = true } + + [features] + f1 = ["dep:bar/bar-feat"] + "#, + ); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/` + To fix this, remove the `dep:` prefix. + If the intent is to avoid creating an implicit feature `bar` for an optional \ + dependency, then consider replacing this with two values: + \"dep:bar\", \"bar/bar-feat\" +", + ) + .run(); +} diff --git a/tests/testsuite/fetch.rs b/tests/testsuite/fetch.rs new file mode 100644 index 0000000..f90131a --- /dev/null +++ b/tests/testsuite/fetch.rs @@ -0,0 +1,135 @@ +//! Tests for the `cargo fetch` command. + +use cargo_test_support::registry::Package; +use cargo_test_support::rustc_host; +use cargo_test_support::{basic_manifest, cross_compile, project}; + +#[cargo_test] +fn no_deps() { + let p = project() + .file("src/main.rs", "mod a; fn main() {}") + .file("src/a.rs", "") + .build(); + + p.cargo("fetch").with_stdout("").run(); +} + +#[cargo_test] +fn fetch_all_platform_dependencies_when_no_target_is_given() { + if cross_compile::disabled() { + return; + } + + Package::new("d1", "1.2.3") + .file("Cargo.toml", &basic_manifest("d1", "1.2.3")) + .file("src/lib.rs", "") + .publish(); + + Package::new("d2", "0.1.2") + .file("Cargo.toml", &basic_manifest("d2", "0.1.2")) + .file("src/lib.rs", "") + .publish(); + + let target = cross_compile::alternate(); + let host = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [target.{host}.dependencies] + d1 = "1.2.3" + + [target.{target}.dependencies] + d2 = "0.1.2" + "#, + host = host, + target = target + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fetch") + .with_stderr_contains("[DOWNLOADED] d1 v1.2.3 [..]") + .with_stderr_contains("[DOWNLOADED] d2 v0.1.2 [..]") + .run(); +} + +#[cargo_test] +fn fetch_platform_specific_dependencies() { + if cross_compile::disabled() { + return; + } + + Package::new("d1", "1.2.3") + .file("Cargo.toml", &basic_manifest("d1", "1.2.3")) + .file("src/lib.rs", "") + .publish(); + + Package::new("d2", "0.1.2") + .file("Cargo.toml", &basic_manifest("d2", "0.1.2")) + .file("src/lib.rs", "") + .publish(); + + let target = cross_compile::alternate(); + let host = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [target.{host}.dependencies] + d1 = "1.2.3" + + [target.{target}.dependencies] + d2 = "0.1.2" + "#, + host = host, + target = target + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fetch --target") + .arg(&host) + .with_stderr_contains("[DOWNLOADED] d1 v1.2.3 [..]") + .with_stderr_does_not_contain("[DOWNLOADED] d2 v0.1.2 [..]") + .run(); + + p.cargo("fetch --target") + .arg(&target) + .with_stderr_contains("[DOWNLOADED] d2 v0.1.2[..]") + .with_stderr_does_not_contain("[DOWNLOADED] d1 v1.2.3 [..]") + .run(); +} + +#[cargo_test] +fn fetch_warning() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + misspelled = "wut" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("fetch") + .with_stderr("[WARNING] unused manifest key: package.misspelled") + .run(); +} diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs new file mode 100644 index 0000000..17bcd11 --- /dev/null +++ b/tests/testsuite/fix.rs @@ -0,0 +1,1855 @@ +//! Tests for the `cargo fix` command. + +use cargo::core::Edition; +use cargo_test_support::compare::assert_match_exact; +use cargo_test_support::git::{self, init}; +use cargo_test_support::paths::{self, CargoPathExt}; +use cargo_test_support::registry::{Dependency, Package}; +use cargo_test_support::tools; +use cargo_test_support::{basic_manifest, is_nightly, project}; + +#[cargo_test] +fn do_not_fix_broken_builds() { + let p = project() + .file( + "src/lib.rs", + r#" + pub fn foo() { + let mut x = 3; + drop(x); + } + + pub fn foo2() { + let _x: u32 = "a"; + } + "#, + ) + .build(); + + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .with_status(101) + .with_stderr_contains("[ERROR] could not compile `foo` due to previous error") + .run(); + assert!(p.read_file("src/lib.rs").contains("let mut x = 3;")); +} + +#[cargo_test] +fn fix_broken_if_requested() { + let p = project() + .file( + "src/lib.rs", + r#" + fn foo(a: &u32) -> u32 { a + 1 } + pub fn bar() { + foo(1); + } + "#, + ) + .build(); + + p.cargo("fix --allow-no-vcs --broken-code") + .env("__CARGO_FIX_YOLO", "1") + .run(); +} + +#[cargo_test] +fn broken_fixes_backed_out() { + // This works as follows: + // - Create a `rustc` shim (the "foo" project) which will pretend that the + // verification step fails. + // - There is an empty build script so `foo` has `OUT_DIR` to track the steps. + // - The first "check", `foo` creates a file in OUT_DIR, and it completes + // successfully with a warning diagnostic to remove unused `mut`. + // - rustfix removes the `mut`. + // - The second "check" to verify the changes, `foo` swaps out the content + // with something that fails to compile. It creates a second file so it + // won't do anything in the third check. + // - cargo fix discovers that the fix failed, and it backs out the changes. + // - The third "check" is done to display the original diagnostics of the + // original code. + let p = project() + .file( + "foo/Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + [workspace] + "#, + ) + .file( + "foo/src/main.rs", + r#" + use std::env; + use std::fs; + use std::io::Write; + use std::path::{Path, PathBuf}; + use std::process::{self, Command}; + + fn main() { + // Ignore calls to things like --print=file-names and compiling build.rs. + // Also compatible for rustc invocations with `@path` argfile. + let is_lib_rs = env::args_os() + .map(PathBuf::from) + .flat_map(|p| if let Some(p) = p.to_str().unwrap_or_default().strip_prefix("@") { + fs::read_to_string(p).unwrap().lines().map(PathBuf::from).collect() + } else { + vec![p] + }) + .any(|l| l == Path::new("src/lib.rs")); + if is_lib_rs { + let path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let first = path.join("first"); + let second = path.join("second"); + if first.exists() && !second.exists() { + fs::write("src/lib.rs", b"not rust code").unwrap(); + fs::File::create(&second).unwrap(); + } else { + fs::File::create(&first).unwrap(); + } + } + + let status = Command::new("rustc") + .args(env::args().skip(1)) + .status() + .expect("failed to run rustc"); + process::exit(status.code().unwrap_or(2)); + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = 'bar' + version = '0.1.0' + [workspace] + "#, + ) + .file("bar/build.rs", "fn main() {}") + .file( + "bar/src/lib.rs", + r#" + pub fn foo() { + let mut x = 3; + drop(x); + } + "#, + ) + .build(); + + // Build our rustc shim + p.cargo("build").cwd("foo").run(); + + // Attempt to fix code, but our shim will always fail the second compile + p.cargo("fix --allow-no-vcs --lib") + .cwd("bar") + .env("__CARGO_FIX_YOLO", "1") + .env("RUSTC", p.root().join("foo/target/debug/foo")) + .with_stderr_contains( + "warning: failed to automatically apply fixes suggested by rustc \ + to crate `bar`\n\ + \n\ + after fixes were automatically applied the compiler reported \ + errors within these files:\n\ + \n \ + * src/lib.rs\n\ + \n\ + This likely indicates a bug in either rustc or cargo itself,\n\ + and we would appreciate a bug report! You're likely to see \n\ + a number of compiler warnings after this message which cargo\n\ + attempted to fix but failed. If you could open an issue at\n\ + [..]\n\ + quoting the full output of this command we'd be very appreciative!\n\ + Note that you may be able to make some more progress in the near-term\n\ + fixing code with the `--broken-code` flag\n\ + \n\ + The following errors were reported:\n\ + error: expected one of `!` or `::`, found `rust`\n\ + ", + ) + .with_stderr_contains("Original diagnostics will follow.") + .with_stderr_contains("[WARNING] variable does not need to be mutable") + .with_stderr_does_not_contain("[..][FIXED][..]") + .run(); + + // Make sure the fix which should have been applied was backed out + assert!(p.read_file("bar/src/lib.rs").contains("let mut x = 3;")); +} + +#[cargo_test] +fn fix_path_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = 'bar' } + + [workspace] + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate bar; + + pub fn foo() -> u32 { + let mut x = 3; + x + } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file( + "bar/src/lib.rs", + r#" + pub fn foo() -> u32 { + let mut x = 3; + x + } + "#, + ) + .build(); + + p.cargo("fix --allow-no-vcs -p foo -p bar") + .env("__CARGO_FIX_YOLO", "1") + .with_stdout("") + .with_stderr_unordered( + "\ +[CHECKING] bar v0.1.0 ([..]) +[FIXED] bar/src/lib.rs (1 fix) +[CHECKING] foo v0.1.0 ([..]) +[FIXED] src/lib.rs (1 fix) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn do_not_fix_non_relevant_deps() { + let p = project() + .no_manifest() + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = '../bar' } + + [workspace] + "#, + ) + .file("foo/src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file( + "bar/src/lib.rs", + r#" + pub fn foo() -> u32 { + let mut x = 3; + x + } + "#, + ) + .build(); + + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .cwd("foo") + .run(); + + assert!(p.read_file("bar/src/lib.rs").contains("mut")); +} + +#[cargo_test] +fn prepare_for_2018() { + let p = project() + .file( + "src/lib.rs", + r#" + #![allow(unused)] + + mod foo { + pub const FOO: &str = "fooo"; + } + + mod bar { + use ::foo::FOO; + } + + fn main() { + let x = ::foo::FOO; + } + "#, + ) + .build(); + + let stderr = "\ +[CHECKING] foo v0.0.1 ([..]) +[MIGRATING] src/lib.rs from 2015 edition to 2018 +[FIXED] src/lib.rs (2 fixes) +[FINISHED] [..] +"; + p.cargo("fix --edition --allow-no-vcs") + .with_stderr(stderr) + .with_stdout("") + .run(); + + println!("{}", p.read_file("src/lib.rs")); + assert!(p.read_file("src/lib.rs").contains("use crate::foo::FOO;")); + assert!(p + .read_file("src/lib.rs") + .contains("let x = crate::foo::FOO;")); +} + +#[cargo_test] +fn local_paths() { + let p = project() + .file( + "src/lib.rs", + r#" + use test::foo; + + mod test { + pub fn foo() {} + } + + pub fn f() { + foo(); + } + "#, + ) + .build(); + + p.cargo("fix --edition --allow-no-vcs") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([..]) +[MIGRATING] src/lib.rs from 2015 edition to 2018 +[FIXED] src/lib.rs (1 fix) +[FINISHED] [..] +", + ) + .with_stdout("") + .run(); + + println!("{}", p.read_file("src/lib.rs")); + assert!(p.read_file("src/lib.rs").contains("use crate::test::foo;")); +} + +#[cargo_test] +fn upgrade_extern_crate() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = '2018' + + [workspace] + + [dependencies] + bar = { path = 'bar' } + "#, + ) + .file( + "src/lib.rs", + r#" + #![warn(rust_2018_idioms)] + extern crate bar; + + use bar::bar; + + pub fn foo() { + ::bar::bar(); + bar(); + } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + let stderr = "\ +[CHECKING] bar v0.1.0 ([..]) +[CHECKING] foo v0.1.0 ([..]) +[FIXED] src/lib.rs (1 fix) +[FINISHED] [..] +"; + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .with_stderr(stderr) + .with_stdout("") + .run(); + println!("{}", p.read_file("src/lib.rs")); + assert!(!p.read_file("src/lib.rs").contains("extern crate")); +} + +#[cargo_test] +fn specify_rustflags() { + let p = project() + .file( + "src/lib.rs", + r#" + #![allow(unused)] + + mod foo { + pub const FOO: &str = "fooo"; + } + + fn main() { + let x = ::foo::FOO; + } + "#, + ) + .build(); + + p.cargo("fix --edition --allow-no-vcs") + .env("RUSTFLAGS", "-C linker=cc") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([..]) +[MIGRATING] src/lib.rs from 2015 edition to 2018 +[FIXED] src/lib.rs (1 fix) +[FINISHED] [..] +", + ) + .with_stdout("") + .run(); +} + +#[cargo_test] +fn no_changes_necessary() { + let p = project().file("src/lib.rs", "").build(); + + let stderr = "\ +[CHECKING] foo v0.0.1 ([..]) +[FINISHED] [..] +"; + p.cargo("fix --allow-no-vcs") + .with_stderr(stderr) + .with_stdout("") + .run(); +} + +#[cargo_test] +fn fixes_extra_mut() { + let p = project() + .file( + "src/lib.rs", + r#" + pub fn foo() -> u32 { + let mut x = 3; + x + } + "#, + ) + .build(); + + let stderr = "\ +[CHECKING] foo v0.0.1 ([..]) +[FIXED] src/lib.rs (1 fix) +[FINISHED] [..] +"; + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .with_stderr(stderr) + .with_stdout("") + .run(); +} + +#[cargo_test] +fn fixes_two_missing_ampersands() { + let p = project() + .file( + "src/lib.rs", + r#" + pub fn foo() -> u32 { + let mut x = 3; + let mut y = 3; + x + y + } + "#, + ) + .build(); + + let stderr = "\ +[CHECKING] foo v0.0.1 ([..]) +[FIXED] src/lib.rs (2 fixes) +[FINISHED] [..] +"; + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .with_stderr(stderr) + .with_stdout("") + .run(); +} + +#[cargo_test] +fn tricky() { + let p = project() + .file( + "src/lib.rs", + r#" + pub fn foo() -> u32 { + let mut x = 3; let mut y = 3; + x + y + } + "#, + ) + .build(); + + let stderr = "\ +[CHECKING] foo v0.0.1 ([..]) +[FIXED] src/lib.rs (2 fixes) +[FINISHED] [..] +"; + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .with_stderr(stderr) + .with_stdout("") + .run(); +} + +#[cargo_test] +fn preserve_line_endings() { + let p = project() + .file( + "src/lib.rs", + "fn add(a: &u32) -> u32 { a + 1 }\r\n\ + pub fn foo() -> u32 { let mut x = 3; add(&x) }\r\n\ + ", + ) + .build(); + + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .run(); + assert!(p.read_file("src/lib.rs").contains("\r\n")); +} + +#[cargo_test] +fn fix_deny_warnings() { + let p = project() + .file( + "src/lib.rs", + "#![deny(warnings)] + pub fn foo() { let mut x = 3; drop(x); } + ", + ) + .build(); + + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .run(); +} + +#[cargo_test] +fn fix_deny_warnings_but_not_others() { + let p = project() + .file( + "src/lib.rs", + " + #![deny(unused_mut)] + + pub fn foo() -> u32 { + let mut x = 3; + x + } + + pub fn bar() { + #[allow(unused_mut)] + let mut _y = 4; + } + ", + ) + .build(); + + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .run(); + assert!(!p.read_file("src/lib.rs").contains("let mut x = 3;")); + assert!(p.read_file("src/lib.rs").contains("let mut _y = 4;")); +} + +#[cargo_test] +fn fix_two_files() { + let p = project() + .file( + "src/lib.rs", + " + pub mod bar; + + pub fn foo() -> u32 { + let mut x = 3; + x + } + ", + ) + .file( + "src/bar.rs", + " + pub fn foo() -> u32 { + let mut x = 3; + x + } + + ", + ) + .build(); + + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .with_stderr_contains("[FIXED] src/bar.rs (1 fix)") + .with_stderr_contains("[FIXED] src/lib.rs (1 fix)") + .run(); + assert!(!p.read_file("src/lib.rs").contains("let mut x = 3;")); + assert!(!p.read_file("src/bar.rs").contains("let mut x = 3;")); +} + +#[cargo_test] +fn fixes_missing_ampersand() { + let p = project() + .file("src/main.rs", "fn main() { let mut x = 3; drop(x); }") + .file( + "src/lib.rs", + r#" + pub fn foo() { let mut x = 3; drop(x); } + + #[test] + pub fn foo2() { let mut x = 3; drop(x); } + "#, + ) + .file( + "tests/a.rs", + r#" + #[test] + pub fn foo() { let mut x = 3; drop(x); } + "#, + ) + .file("examples/foo.rs", "fn main() { let mut x = 3; drop(x); }") + .file("build.rs", "fn main() { let mut x = 3; drop(x); }") + .build(); + + p.cargo("fix --all-targets --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .with_stdout("") + .with_stderr_contains("[COMPILING] foo v0.0.1 ([..])") + .with_stderr_contains("[FIXED] build.rs (1 fix)") + // Don't assert number of fixes for this one, as we don't know if we're + // fixing it once or twice! We run this all concurrently, and if we + // compile (and fix) in `--test` mode first, we get two fixes. Otherwise + // we'll fix one non-test thing, and then fix another one later in + // test mode. + .with_stderr_contains("[FIXED] src/lib.rs[..]") + .with_stderr_contains("[FIXED] src/main.rs (1 fix)") + .with_stderr_contains("[FIXED] examples/foo.rs (1 fix)") + .with_stderr_contains("[FIXED] tests/a.rs (1 fix)") + .with_stderr_contains("[FINISHED] [..]") + .run(); + p.cargo("check").run(); + p.cargo("test").run(); +} + +#[cargo_test] +fn fix_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + bar = [] + + [workspace] + "#, + ) + .file( + "src/lib.rs", + r#" + #[cfg(feature = "bar")] + pub fn foo() -> u32 { let mut x = 3; x } + "#, + ) + .build(); + + p.cargo("fix --allow-no-vcs").run(); + p.cargo("check").run(); + p.cargo("fix --features bar --allow-no-vcs").run(); + p.cargo("check --features bar").run(); +} + +#[cargo_test] +fn shows_warnings() { + let p = project() + .file( + "src/lib.rs", + "#[deprecated] fn bar() {} pub fn foo() { let _ = bar(); }", + ) + .build(); + + p.cargo("fix --allow-no-vcs") + .with_stderr_contains("[..]warning: use of deprecated[..]") + .run(); +} + +#[cargo_test] +fn warns_if_no_vcs_detected() { + let p = project().file("src/lib.rs", "pub fn foo() {}").build(); + + p.cargo("fix") + .with_status(101) + .with_stderr( + "error: no VCS found for this package and `cargo fix` can potentially perform \ + destructive changes; if you'd like to suppress this error pass `--allow-no-vcs`\ + ", + ) + .run(); + p.cargo("fix --allow-no-vcs").run(); +} + +#[cargo_test] +fn warns_about_dirty_working_directory() { + let p = git::new("foo", |p| p.file("src/lib.rs", "pub fn foo() {}")); + + p.change_file("src/lib.rs", ""); + + p.cargo("fix") + .with_status(101) + .with_stderr( + "\ +error: the working directory of this package has uncommitted changes, \ +and `cargo fix` can potentially perform destructive changes; if you'd \ +like to suppress this error pass `--allow-dirty`, `--allow-staged`, or \ +commit the changes to these files: + + * src/lib.rs (dirty) + + +", + ) + .run(); + p.cargo("fix --allow-dirty").run(); +} + +#[cargo_test] +fn warns_about_staged_working_directory() { + let (p, repo) = git::new_repo("foo", |p| p.file("src/lib.rs", "pub fn foo() {}")); + + p.change_file("src/lib.rs", "pub fn bar() {}"); + git::add(&repo); + + p.cargo("fix") + .with_status(101) + .with_stderr( + "\ +error: the working directory of this package has uncommitted changes, \ +and `cargo fix` can potentially perform destructive changes; if you'd \ +like to suppress this error pass `--allow-dirty`, `--allow-staged`, or \ +commit the changes to these files: + + * src/lib.rs (staged) + + +", + ) + .run(); + p.cargo("fix --allow-staged").run(); +} + +#[cargo_test] +fn errors_about_untracked_files() { + let mut git_project = project().at("foo"); + git_project = git_project.file("src/lib.rs", "pub fn foo() {}"); + let p = git_project.build(); + let _ = init(&p.root()); + + p.cargo("fix") + .with_status(101) + .with_stderr( + "\ +error: the working directory of this package has uncommitted changes, \ +and `cargo fix` can potentially perform destructive changes; if you'd \ +like to suppress this error pass `--allow-dirty`, `--allow-staged`, or \ +commit the changes to these files: + + * Cargo.toml (dirty) + * src/ (dirty) + + +", + ) + .run(); + p.cargo("fix --allow-dirty").run(); +} + +#[cargo_test] +fn does_not_warn_about_clean_working_directory() { + let p = git::new("foo", |p| p.file("src/lib.rs", "pub fn foo() {}")); + p.cargo("fix").run(); +} + +#[cargo_test] +fn does_not_warn_about_dirty_ignored_files() { + let p = git::new("foo", |p| { + p.file("src/lib.rs", "pub fn foo() {}") + .file(".gitignore", "bar\n") + }); + + p.change_file("bar", ""); + + p.cargo("fix").run(); +} + +#[cargo_test] +fn fix_all_targets_by_default() { + let p = project() + .file("src/lib.rs", "pub fn foo() { let mut x = 3; drop(x); }") + .file("tests/foo.rs", "pub fn foo() { let mut x = 3; drop(x); }") + .build(); + p.cargo("fix --allow-no-vcs") + .env("__CARGO_FIX_YOLO", "1") + .run(); + assert!(!p.read_file("src/lib.rs").contains("let mut x")); + assert!(!p.read_file("tests/foo.rs").contains("let mut x")); +} + +#[cargo_test] +fn prepare_for_unstable() { + // During the period where a new edition is coming up, but not yet stable, + // this test will verify that it cannot be migrated to on stable. If there + // is no next edition, it does nothing. + let next = match Edition::LATEST_UNSTABLE { + Some(next) => next, + None => { + eprintln!("Next edition is currently not available, skipping test."); + return; + } + }; + let latest_stable = Edition::LATEST_STABLE; + let prev = latest_stable.previous().unwrap(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "{}" + "#, + latest_stable + ), + ) + .file("src/lib.rs", "") + .build(); + + // -j1 to make the error more deterministic (otherwise there can be + // multiple errors since they run in parallel). + p.cargo("fix --edition --allow-no-vcs -j1") + .with_stderr(&format_args!("\ +[CHECKING] foo [..] +[WARNING] `src/lib.rs` is on the latest edition, but trying to migrate to edition {next}. +Edition {next} is unstable and not allowed in this release, consider trying the nightly release channel. + +If you are trying to migrate from the previous edition ({prev}), the +process requires following these steps: + +1. Start with `edition = \"{prev}\"` in `Cargo.toml` +2. Run `cargo fix --edition` +3. Modify `Cargo.toml` to set `edition = \"{latest_stable}\"` +4. Run `cargo build` or `cargo test` to verify the fixes worked + +More details may be found at +https://doc.rust-lang.org/edition-guide/editions/transitioning-an-existing-project-to-a-new-edition.html + +[FINISHED] [..] +", next=next, latest_stable=latest_stable, prev=prev)) + .run(); + + if !is_nightly() { + // The rest of this test is fundamentally always nightly. + return; + } + + p.cargo("fix --edition --allow-no-vcs") + .masquerade_as_nightly_cargo(&["always_nightly"]) + .with_stderr(&format!( + "\ +[CHECKING] foo [..] +[MIGRATING] src/lib.rs from {latest_stable} edition to {next} +[FINISHED] [..] +", + latest_stable = latest_stable, + next = next, + )) + .run(); +} + +#[cargo_test] +fn prepare_for_latest_stable() { + // This is the stable counterpart of prepare_for_unstable. + let latest_stable = Edition::LATEST_STABLE; + let previous = latest_stable.previous().unwrap(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = 'foo' + version = '0.1.0' + edition = '{}' + "#, + previous + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fix --edition --allow-no-vcs") + .with_stderr(&format!( + "\ +[CHECKING] foo [..] +[MIGRATING] src/lib.rs from {} edition to {} +[FINISHED] [..] +", + previous, latest_stable + )) + .run(); +} + +#[cargo_test(nightly, reason = "fundamentally always nightly")] +fn prepare_for_already_on_latest_unstable() { + // During the period where a new edition is coming up, but not yet stable, + // this test will check what happens if you are already on the latest. If + // there is no next edition, it does nothing. + let next_edition = match Edition::LATEST_UNSTABLE { + Some(next) => next, + None => { + eprintln!("Next edition is currently not available, skipping test."); + return; + } + }; + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + cargo-features = ["edition{}"] + + [package] + name = 'foo' + version = '0.1.0' + edition = '{}' + "#, + next_edition, next_edition + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fix --edition --allow-no-vcs") + .masquerade_as_nightly_cargo(&["always_nightly"]) + .with_stderr_contains("[CHECKING] foo [..]") + .with_stderr_contains(&format!( + "\ +[WARNING] `src/lib.rs` is already on the latest edition ({next_edition}), unable to migrate further +", + next_edition = next_edition + )) + .run(); +} + +#[cargo_test] +fn prepare_for_already_on_latest_stable() { + // Stable counterpart of prepare_for_already_on_latest_unstable. + if Edition::LATEST_UNSTABLE.is_some() { + eprintln!("This test cannot run while the latest edition is unstable, skipping."); + return; + } + let latest_stable = Edition::LATEST_STABLE; + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = 'foo' + version = '0.1.0' + edition = '{}' + "#, + latest_stable + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fix --edition --allow-no-vcs") + .with_stderr_contains("[CHECKING] foo [..]") + .with_stderr_contains(&format!( + "\ +[WARNING] `src/lib.rs` is already on the latest edition ({latest_stable}), unable to migrate further +", + latest_stable = latest_stable + )) + .run(); +} + +#[cargo_test] +fn fix_overlapping() { + let p = project() + .file( + "src/lib.rs", + r#" + pub fn foo() {} + pub struct A; + + pub mod bar { + pub fn baz() { + ::foo::<::A>(); + } + } + "#, + ) + .build(); + + p.cargo("fix --allow-no-vcs --edition --lib") + .with_stderr( + "\ +[CHECKING] foo [..] +[MIGRATING] src/lib.rs from 2015 edition to 2018 +[FIXED] src/lib.rs (2 fixes) +[FINISHED] dev [..] +", + ) + .run(); + + let contents = p.read_file("src/lib.rs"); + println!("{}", contents); + assert!(contents.contains("crate::foo::()")); +} + +#[cargo_test] +fn fix_idioms() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + edition = '2018' + "#, + ) + .file( + "src/lib.rs", + r#" + use std::any::Any; + pub fn foo() { + let _x: Box = Box::new(3); + } + "#, + ) + .build(); + + let stderr = "\ +[CHECKING] foo [..] +[FIXED] src/lib.rs (1 fix) +[FINISHED] [..] +"; + p.cargo("fix --edition-idioms --allow-no-vcs") + .with_stderr(stderr) + .run(); + + assert!(p.read_file("src/lib.rs").contains("Box")); +} + +#[cargo_test] +fn idioms_2015_ok() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("fix --edition-idioms --allow-no-vcs").run(); +} + +#[cargo_test] +fn shows_warnings_on_second_run_without_changes() { + let p = project() + .file( + "src/lib.rs", + r#" + #[deprecated] + fn bar() {} + + pub fn foo() { + let _ = bar(); + } + "#, + ) + .build(); + + p.cargo("fix --allow-no-vcs") + .with_stderr_contains("[..]warning: use of deprecated[..]") + .run(); + + p.cargo("fix --allow-no-vcs") + .with_stderr_contains("[..]warning: use of deprecated[..]") + .run(); +} + +#[cargo_test] +fn shows_warnings_on_second_run_without_changes_on_multiple_targets() { + let p = project() + .file( + "src/lib.rs", + r#" + #[deprecated] + fn bar() {} + + pub fn foo() { + let _ = bar(); + } + "#, + ) + .file( + "src/main.rs", + r#" + #[deprecated] + fn bar() {} + + fn main() { + let _ = bar(); + } + "#, + ) + .file( + "tests/foo.rs", + r#" + #[deprecated] + fn bar() {} + + #[test] + fn foo_test() { + let _ = bar(); + } + "#, + ) + .file( + "tests/bar.rs", + r#" + #[deprecated] + fn bar() {} + + #[test] + fn foo_test() { + let _ = bar(); + } + "#, + ) + .file( + "examples/fooxample.rs", + r#" + #[deprecated] + fn bar() {} + + fn main() { + let _ = bar(); + } + "#, + ) + .build(); + + p.cargo("fix --allow-no-vcs --all-targets") + .with_stderr_contains(" --> examples/fooxample.rs:6:29") + .with_stderr_contains(" --> src/lib.rs:6:29") + .with_stderr_contains(" --> src/main.rs:6:29") + .with_stderr_contains(" --> tests/bar.rs:7:29") + .with_stderr_contains(" --> tests/foo.rs:7:29") + .run(); + + p.cargo("fix --allow-no-vcs --all-targets") + .with_stderr_contains(" --> examples/fooxample.rs:6:29") + .with_stderr_contains(" --> src/lib.rs:6:29") + .with_stderr_contains(" --> src/main.rs:6:29") + .with_stderr_contains(" --> tests/bar.rs:7:29") + .with_stderr_contains(" --> tests/foo.rs:7:29") + .run(); +} + +#[cargo_test] +fn doesnt_rebuild_dependencies() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = 'bar' } + + [workspace] + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("fix --allow-no-vcs -p foo") + .env("__CARGO_FIX_YOLO", "1") + .with_stdout("") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 ([..]) +[CHECKING] foo v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("fix --allow-no-vcs -p foo") + .env("__CARGO_FIX_YOLO", "1") + .with_stdout("") + .with_stderr( + "\ +[CHECKING] foo v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn does_not_crash_with_rustc_wrapper() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fix --allow-no-vcs") + .env("RUSTC_WRAPPER", tools::echo_wrapper()) + .run(); + p.build_dir().rm_rf(); + p.cargo("fix --allow-no-vcs --verbose") + .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) + .run(); +} + +#[cargo_test] +fn uses_workspace_wrapper_and_primary_wrapper_override() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fix --allow-no-vcs --verbose") + .env("RUSTC_WORKSPACE_WRAPPER", tools::echo_wrapper()) + .with_stderr_contains("WRAPPER CALLED: rustc src/lib.rs --crate-name foo [..]") + .run(); +} + +#[cargo_test] +fn only_warn_for_relevant_crates() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = { path = 'a' } + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + "#, + ) + .file( + "a/src/lib.rs", + " + pub fn foo() {} + pub mod bar { + use foo; + pub fn baz() { foo() } + } + ", + ) + .build(); + + p.cargo("fix --allow-no-vcs --edition") + .with_stderr( + "\ +[CHECKING] a v0.1.0 ([..]) +[CHECKING] foo v0.1.0 ([..]) +[MIGRATING] src/lib.rs from 2015 edition to 2018 +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn fix_to_broken_code() { + let p = project() + .file( + "foo/Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + [workspace] + "#, + ) + .file( + "foo/src/main.rs", + r#" + use std::env; + use std::fs; + use std::io::Write; + use std::path::{Path, PathBuf}; + use std::process::{self, Command}; + + fn main() { + // Ignore calls to things like --print=file-names and compiling build.rs. + // Also compatible for rustc invocations with `@path` argfile. + let is_lib_rs = env::args_os() + .map(PathBuf::from) + .flat_map(|p| if let Some(p) = p.to_str().unwrap_or_default().strip_prefix("@") { + fs::read_to_string(p).unwrap().lines().map(PathBuf::from).collect() + } else { + vec![p] + }) + .any(|l| l == Path::new("src/lib.rs")); + if is_lib_rs { + let path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let path = path.join("foo"); + if path.exists() { + panic!() + } else { + fs::File::create(&path).unwrap(); + } + } + + let status = Command::new("rustc") + .args(env::args().skip(1)) + .status() + .expect("failed to run rustc"); + process::exit(status.code().unwrap_or(2)); + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = 'bar' + version = '0.1.0' + [workspace] + "#, + ) + .file("bar/build.rs", "fn main() {}") + .file("bar/src/lib.rs", "pub fn foo() { let mut x = 3; drop(x); }") + .build(); + + // Build our rustc shim + p.cargo("build").cwd("foo").run(); + + // Attempt to fix code, but our shim will always fail the second compile + p.cargo("fix --allow-no-vcs --broken-code") + .cwd("bar") + .env("RUSTC", p.root().join("foo/target/debug/foo")) + .with_status(101) + .with_stderr_contains("[WARNING] failed to automatically apply fixes [..]") + .run(); + + assert_eq!( + p.read_file("bar/src/lib.rs"), + "pub fn foo() { let x = 3; drop(x); }" + ); +} + +#[cargo_test] +fn fix_with_common() { + let p = project() + .file("src/lib.rs", "") + .file( + "tests/t1.rs", + "mod common; #[test] fn t1() { common::try(); }", + ) + .file( + "tests/t2.rs", + "mod common; #[test] fn t2() { common::try(); }", + ) + .file("tests/common/mod.rs", "pub fn try() {}") + .build(); + + p.cargo("fix --edition --allow-no-vcs").run(); + + assert_eq!(p.read_file("tests/common/mod.rs"), "pub fn r#try() {}"); +} + +#[cargo_test] +fn fix_in_existing_repo_weird_ignore() { + // Check that ignore doesn't ignore the repo itself. + let p = git::new("foo", |project| { + project + .file("src/lib.rs", "") + .file(".gitignore", "foo\ninner\nCargo.lock\ntarget\n") + .file("inner/file", "") + }); + + p.cargo("fix").run(); + // This is questionable about whether it is the right behavior. It should + // probably be checking if any source file for the current project is + // ignored. + p.cargo("fix") + .cwd("inner") + .with_stderr_contains("[ERROR] no VCS found[..]") + .with_status(101) + .run(); + p.cargo("fix").cwd("src").run(); +} + +#[cargo_test] +fn fix_color_message() { + // Check that color appears in diagnostics. + let p = project() + .file("src/lib.rs", "std::compile_error!{\"color test\"}") + .build(); + + p.cargo("fix --allow-no-vcs --color=always") + .with_stderr_contains("[..]\x1b[[..]") + .with_status(101) + .run(); + + p.cargo("fix --allow-no-vcs --color=never") + .with_stderr_contains("error: color test") + .with_stderr_does_not_contain("[..]\x1b[[..]") + .with_status(101) + .run(); +} + +#[cargo_test] +fn edition_v2_resolver_report() { + // Show a report if the V2 resolver shows differences. + Package::new("common", "1.0.0") + .feature("f1", &[]) + .feature("dev-feat", &[]) + .add_dep(Dependency::new("opt_dep", "1.0").optional(true)) + .publish(); + Package::new("opt_dep", "1.0.0").publish(); + + Package::new("bar", "1.0.0") + .add_dep( + Dependency::new("common", "1.0") + .target("cfg(whatever)") + .enable_features(&["f1"]), + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + common = "1.0" + bar = "1.0" + + [build-dependencies] + common = { version = "1.0", features = ["opt_dep"] } + + [dev-dependencies] + common = { version="1.0", features=["dev-feat"] } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fix --edition --allow-no-vcs") + .with_stderr_unordered("\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] common v1.0.0 [..] +[DOWNLOADED] bar v1.0.0 [..] +[DOWNLOADED] opt_dep v1.0.0 [..] +note: Switching to Edition 2021 will enable the use of the version 2 feature resolver in Cargo. +This may cause some dependencies to be built with fewer features enabled than previously. +More information about the resolver changes may be found at https://doc.rust-lang.org/nightly/edition-guide/rust-2021/default-cargo-resolver.html +When building the following dependencies, the given features will no longer be used: + + common v1.0.0 removed features: dev-feat, f1, opt_dep + common v1.0.0 (as host dependency) removed features: dev-feat, f1 + +The following differences only apply when building with dev-dependencies: + + common v1.0.0 removed features: f1, opt_dep + +[CHECKING] opt_dep v1.0.0 +[CHECKING] common v1.0.0 +[CHECKING] bar v1.0.0 +[CHECKING] foo v0.1.0 [..] +[MIGRATING] src/lib.rs from 2018 edition to 2021 +[FINISHED] [..] +") + .run(); +} + +#[cargo_test] +fn rustfix_handles_multi_spans() { + // Checks that rustfix handles a single diagnostic with multiple + // suggestion spans (non_fmt_panic in this case). + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file( + "src/lib.rs", + r#" + pub fn foo() { + panic!(format!("hey")); + } + "#, + ) + .build(); + + p.cargo("fix --allow-no-vcs").run(); + assert!(p.read_file("src/lib.rs").contains(r#"panic!("hey");"#)); +} + +#[cargo_test] +fn fix_edition_2021() { + // Can migrate 2021, even when lints are allowed. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + "#, + ) + .file( + "src/lib.rs", + r#" + #![allow(ellipsis_inclusive_range_patterns)] + + pub fn f() -> bool { + let x = 123; + match x { + 0...100 => true, + _ => false, + } + } + "#, + ) + .build(); + p.cargo("fix --edition --allow-no-vcs") + .with_stderr( + "\ +[CHECKING] foo v0.1.0 [..] +[MIGRATING] src/lib.rs from 2018 edition to 2021 +[FIXED] src/lib.rs (1 fix) +[FINISHED] [..] +", + ) + .run(); + assert!(p.read_file("src/lib.rs").contains(r#"0..=100 => true,"#)); +} + +#[cargo_test] +fn fix_shared_cross_workspace() { + // Fixing a file that is shared between multiple packages in the same workspace. + // Make sure two processes don't try to fix the same file at the same time. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "pub mod shared;") + // This will fix both unused and bare trait. + .file("foo/src/shared.rs", "pub fn fixme(x: Box<&Fn() -> ()>) {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file( + "bar/src/lib.rs", + r#" + #[path="../../foo/src/shared.rs"] + pub mod shared; + "#, + ) + .build(); + + // The output here can be either of these two, depending on who runs first: + // [FIXED] bar/src/../../foo/src/shared.rs (2 fixes) + // [FIXED] foo/src/shared.rs (2 fixes) + p.cargo("fix --allow-no-vcs") + .with_stderr_unordered( + "\ +[CHECKING] foo v0.1.0 [..] +[CHECKING] bar v0.1.0 [..] +[FIXED] [..]foo/src/shared.rs (2 fixes) +[FINISHED] [..] +", + ) + .run(); + + assert_match_exact( + "pub fn fixme(_x: Box<&dyn Fn() -> ()>) {}", + &p.read_file("foo/src/shared.rs"), + ); +} + +#[cargo_test] +fn abnormal_exit() { + // rustc fails unexpectedly after applying fixes, should show some error information. + // + // This works with a proc-macro that runs three times: + // - First run (collect diagnostics pass): writes a file, exits normally. + // - Second run (verify diagnostics work): it detects the presence of the + // file, removes the file, and aborts the process. + // - Third run (collecting messages to display): file not found, exits normally. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + pm = {path="pm"} + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn f() { + let mut x = 1; + pm::crashme!(); + } + "#, + ) + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + edition = "2018" + + [lib] + proc-macro = true + "#, + ) + .file( + "pm/src/lib.rs", + r#" + use proc_macro::TokenStream; + #[proc_macro] + pub fn crashme(_input: TokenStream) -> TokenStream { + // Use a file to succeed on the first pass, and fail on the second. + let p = std::env::var_os("ONCE_PATH").unwrap(); + let check_path = std::path::Path::new(&p); + if check_path.exists() { + eprintln!("I'm not a diagnostic."); + std::fs::remove_file(check_path).unwrap(); + std::process::abort(); + } else { + std::fs::write(check_path, "").unwrap(); + "".parse().unwrap() + } + } + "#, + ) + .build(); + + p.cargo("fix --lib --allow-no-vcs") + .env( + "ONCE_PATH", + paths::root().join("proc-macro-run-once").to_str().unwrap(), + ) + .with_stderr_contains( + "[WARNING] failed to automatically apply fixes suggested by rustc to crate `foo`", + ) + .with_stderr_contains("I'm not a diagnostic.") + // "signal: 6, SIGABRT: process abort signal" on some platforms + .with_stderr_contains("rustc exited abnormally: [..]") + .with_stderr_contains("Original diagnostics will follow.") + .run(); +} + +#[cargo_test] +fn fix_with_run_cargo_in_proc_macros() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [lib] + proc-macro = true + "#, + ) + .file( + "src/lib.rs", + r#" + use proc_macro::*; + + #[proc_macro] + pub fn foo(_input: TokenStream) -> TokenStream { + let output = std::process::Command::new(env!("CARGO")) + .args(&["metadata", "--format-version=1"]) + .output() + .unwrap(); + eprintln!("{}", std::str::from_utf8(&output.stderr).unwrap()); + println!("{}", std::str::from_utf8(&output.stdout).unwrap()); + "".parse().unwrap() + } + "#, + ) + .file( + "src/bin/main.rs", + r#" + use foo::foo; + + fn main() { + foo!("bar") + } + "#, + ) + .build(); + p.cargo("fix --allow-no-vcs") + .with_stderr_does_not_contain("error: could not find .rs file in rustc args") + .run(); +} + +#[cargo_test] +fn non_edition_lint_migration() { + // Migrating to a new edition where a non-edition lint causes problems. + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file( + "src/lib.rs", + r#" + // This is only used in a test. + // To be correct, this should be gated on #[cfg(test)], but + // sometimes people don't do that. If the unused_imports + // lint removes this, then the unittest will fail to compile. + use std::str::from_utf8; + + pub mod foo { + pub const FOO: &[u8] = &[102, 111, 111]; + } + + #[test] + fn example() { + assert_eq!( + from_utf8(::foo::FOO), Ok("foo") + ); + } + "#, + ) + .build(); + // Check that it complains about an unused import. + p.cargo("check --lib") + .with_stderr_contains("[..]unused_imports[..]") + .with_stderr_contains("[..]std::str::from_utf8[..]") + .run(); + p.cargo("fix --edition --allow-no-vcs").run(); + let contents = p.read_file("src/lib.rs"); + // Check it does not remove the "unused" import. + assert!(contents.contains("use std::str::from_utf8;")); + // Check that it made the edition migration. + assert!(contents.contains("from_utf8(crate::foo::FOO)")); +} + +// For rust-lang/cargo#9857 +#[cargo_test] +fn fix_in_dependency() { + Package::new("bar", "1.0.0") + .file( + "src/lib.rs", + r#" + #[macro_export] + macro_rules! m { + ($i:tt) => { + let $i = 1; + }; + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn foo() { + bar::m!(abc); + } + "#, + ) + .build(); + + p.cargo("fix --allow-no-vcs") + .with_stderr_does_not_contain("[FIXED] [..]") + .run(); +} diff --git a/tests/testsuite/freshness.rs b/tests/testsuite/freshness.rs new file mode 100644 index 0000000..86b186a --- /dev/null +++ b/tests/testsuite/freshness.rs @@ -0,0 +1,2816 @@ +//! Tests for fingerprinting (rebuild detection). + +use filetime::FileTime; +use std::fs::{self, OpenOptions}; +use std::io; +use std::io::prelude::*; +use std::net::TcpListener; +use std::path::{Path, PathBuf}; +use std::process::Stdio; +use std::thread; +use std::time::SystemTime; + +use super::death; +use cargo_test_support::paths::{self, CargoPathExt}; +use cargo_test_support::registry::Package; +use cargo_test_support::{ + basic_manifest, is_coarse_mtime, project, rustc_host, rustc_host_env, sleep_ms, +}; + +#[cargo_test] +fn modifying_and_moving() { + let p = project() + .file("src/main.rs", "mod a; fn main() {}") + .file("src/a.rs", "") + .build(); + + p.cargo("build") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("build").with_stdout("").run(); + p.root().move_into_the_past(); + p.root().join("target").move_into_the_past(); + + p.change_file("src/a.rs", "#[allow(unused)]fn main() {}"); + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo v0.0.1 ([CWD]): the file `src/a.rs` has changed ([..]) +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + fs::rename(&p.root().join("src/a.rs"), &p.root().join("src/b.rs")).unwrap(); + p.cargo("build") + .with_status(101) + .with_stderr_contains("[..]file not found[..]") + .run(); +} + +#[cargo_test] +fn modify_only_some_files() { + let p = project() + .file("src/lib.rs", "mod a;") + .file("src/a.rs", "") + .file("src/main.rs", "mod b; fn main() {}") + .file("src/b.rs", "") + .file("tests/test.rs", "") + .build(); + + p.cargo("build") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("test").run(); + sleep_ms(1000); + + assert!(p.bin("foo").is_file()); + + let lib = p.root().join("src/lib.rs"); + p.change_file("src/lib.rs", "invalid rust code"); + p.change_file("src/b.rs", "#[allow(unused)]fn foo() {}"); + lib.move_into_the_past(); + + // Make sure the binary is rebuilt, not the lib + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo v0.0.1 ([CWD]): the file `src/b.rs` has changed ([..]) +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + assert!(p.bin("foo").is_file()); +} + +#[cargo_test] +fn rebuild_sub_package_then_while_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [dependencies.a] + path = "a" + [dependencies.b] + path = "b" + "#, + ) + .file("src/lib.rs", "extern crate a; extern crate b;") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + authors = [] + version = "0.0.1" + [dependencies.b] + path = "../b" + "#, + ) + .file("a/src/lib.rs", "extern crate b;") + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("build") + .with_stderr( + "\ +[COMPILING] b [..] +[COMPILING] a [..] +[COMPILING] foo [..] +[FINISHED] dev [..] +", + ) + .run(); + + if is_coarse_mtime() { + sleep_ms(1000); + } + p.change_file("b/src/lib.rs", "pub fn b() {}"); + + p.cargo("build -pb -v") + .with_stderr( + "\ +[DIRTY] b v0.0.1 ([..]): the file `b/src/lib.rs` has changed ([..]) +[COMPILING] b [..] +[RUNNING] `rustc --crate-name b [..] +[FINISHED] dev [..] +", + ) + .run(); + + p.change_file( + "src/lib.rs", + "extern crate a; extern crate b; pub fn toplevel() {}", + ); + + p.cargo("build -v") + .with_stderr( + "\ +[FRESH] b [..] +[DIRTY] a [..]: the dependency b was rebuilt ([..]) +[COMPILING] a [..] +[RUNNING] `rustc --crate-name a [..] +[DIRTY] foo [..]: the dependency b was rebuilt ([..]) +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] dev [..] +", + ) + .run(); +} + +#[cargo_test] +fn changing_lib_features_caches_targets() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [features] + foo = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_stderr( + "\ +[..]Compiling foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("build --features foo") + .with_stderr( + "\ +[..]Compiling foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + /* Targets should be cached from the first build */ + + p.cargo("build") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); + + p.cargo("build").with_stdout("").run(); + + p.cargo("build --features foo") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); +} + +#[cargo_test] +fn changing_profiles_caches_targets() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [profile.dev] + panic = "abort" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_stderr( + "\ +[..]Compiling foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("test") + .with_stderr( + "\ +[..]Compiling foo v0.0.1 ([..]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target[..]debug[..]deps[..]foo-[..][EXE]) +[DOCTEST] foo +", + ) + .run(); + + /* Targets should be cached from the first build */ + + p.cargo("build") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); + + p.cargo("test foo") + .with_stderr( + "\ +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target[..]debug[..]deps[..]foo-[..][EXE]) +", + ) + .run(); +} + +#[cargo_test] +fn changing_bin_paths_common_target_features_caches_targets() { + // Make sure dep_cache crate is built once per feature + let p = project() + .no_manifest() + .file( + ".cargo/config", + r#" + [build] + target-dir = "./target" + "#, + ) + .file( + "dep_crate/Cargo.toml", + r#" + [package] + name = "dep_crate" + version = "0.0.1" + authors = [] + + [features] + ftest = [] + "#, + ) + .file( + "dep_crate/src/lib.rs", + r#" + #[cfg(feature = "ftest")] + pub fn yo() { + println!("ftest on") + } + #[cfg(not(feature = "ftest"))] + pub fn yo() { + println!("ftest off") + } + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [dependencies] + dep_crate = {path = "../dep_crate", features = []} + "#, + ) + .file("a/src/lib.rs", "") + .file( + "a/src/main.rs", + r#" + extern crate dep_crate; + use dep_crate::yo; + fn main() { + yo(); + } + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.0.1" + authors = [] + + [dependencies] + dep_crate = {path = "../dep_crate", features = ["ftest"]} + "#, + ) + .file("b/src/lib.rs", "") + .file( + "b/src/main.rs", + r#" + extern crate dep_crate; + use dep_crate::yo; + fn main() { + yo(); + } + "#, + ) + .build(); + + /* Build and rebuild a/. Ensure dep_crate only builds once */ + p.cargo("run") + .cwd("a") + .with_stdout("ftest off") + .with_stderr( + "\ +[..]Compiling dep_crate v0.0.1 ([..]) +[..]Compiling a v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]target/debug/a[EXE]` +", + ) + .run(); + p.cargo("clean -p a").cwd("a").run(); + p.cargo("run") + .cwd("a") + .with_stdout("ftest off") + .with_stderr( + "\ +[..]Compiling a v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]target/debug/a[EXE]` +", + ) + .run(); + + /* Build and rebuild b/. Ensure dep_crate only builds once */ + p.cargo("run") + .cwd("b") + .with_stdout("ftest on") + .with_stderr( + "\ +[..]Compiling dep_crate v0.0.1 ([..]) +[..]Compiling b v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]target/debug/b[EXE]` +", + ) + .run(); + p.cargo("clean -p b").cwd("b").run(); + p.cargo("run") + .cwd("b") + .with_stdout("ftest on") + .with_stderr( + "\ +[..]Compiling b v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]target/debug/b[EXE]` +", + ) + .run(); + + /* Build a/ package again. If we cache different feature dep builds correctly, + * this should not cause a rebuild of dep_crate */ + p.cargo("clean -p a").cwd("a").run(); + p.cargo("run") + .cwd("a") + .with_stdout("ftest off") + .with_stderr( + "\ +[..]Compiling a v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]target/debug/a[EXE]` +", + ) + .run(); + + /* Build b/ package again. If we cache different feature dep builds correctly, + * this should not cause a rebuild */ + p.cargo("clean -p b").cwd("b").run(); + p.cargo("run") + .cwd("b") + .with_stdout("ftest on") + .with_stderr( + "\ +[..]Compiling b v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]target/debug/b[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn changing_bin_features_caches_targets() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [features] + foo = [] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + let msg = if cfg!(feature = "foo") { "feature on" } else { "feature off" }; + println!("{}", msg); + } + "#, + ) + .build(); + + p.cargo("build") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.rename_run("foo", "off1").with_stdout("feature off").run(); + + p.cargo("build --features foo") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.rename_run("foo", "on1").with_stdout("feature on").run(); + + /* Targets should be cached from the first build */ + + let mut e = p.cargo("build -v"); + + // MSVC does not include hash in binary filename, so it gets recompiled. + if cfg!(target_env = "msvc") { + e.with_stderr( + "\ +[DIRTY] foo v0.0.1 ([..]): the list of features changed +[COMPILING] foo[..] +[RUNNING] `rustc [..] +[FINISHED] dev[..]", + ); + } else { + e.with_stderr("[FRESH] foo v0.0.1 ([..])\n[FINISHED] dev[..]"); + } + e.run(); + p.rename_run("foo", "off2").with_stdout("feature off").run(); + + let mut e = p.cargo("build --features foo -v"); + if cfg!(target_env = "msvc") { + e.with_stderr( + "\ +[DIRTY] foo v0.0.1 ([..]): the list of features changed +[COMPILING] foo[..] +[RUNNING] `rustc [..] +[FINISHED] dev[..]", + ); + } else { + e.with_stderr( + "\ +[FRESH] foo v0.0.1 ([..]) +[FINISHED] dev[..]", + ); + } + e.run(); + p.rename_run("foo", "on2").with_stdout("feature on").run(); +} + +#[cargo_test] +fn rebuild_tests_if_lib_changes() { + let p = project() + .file("src/lib.rs", "pub fn foo() {}") + .file( + "tests/foo.rs", + r#" + extern crate foo; + #[test] + fn test() { foo::foo(); } + "#, + ) + .build(); + + p.cargo("build").run(); + p.cargo("test").run(); + + sleep_ms(1000); + p.change_file("src/lib.rs", ""); + + p.cargo("build -v").run(); + p.cargo("test -v") + .with_status(101) + .with_stderr_contains("[..]cannot find function `foo`[..]") + .run(); +} + +#[cargo_test] +fn no_rebuild_transitive_target_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + [dev-dependencies] + b = { path = "b" } + "#, + ) + .file("src/lib.rs", "") + .file("tests/foo.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [target.foo.dependencies] + c = { path = "../c" } + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.0.1" + authors = [] + + [dependencies] + c = { path = "../c" } + "#, + ) + .file("b/src/lib.rs", "") + .file("c/Cargo.toml", &basic_manifest("c", "0.0.1")) + .file("c/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("test --no-run") + .with_stderr( + "\ +[COMPILING] c v0.0.1 ([..]) +[COMPILING] b v0.0.1 ([..]) +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[..][EXE]) +[EXECUTABLE] tests/foo.rs (target/debug/deps/foo-[..][EXE]) +", + ) + .run(); +} + +#[cargo_test] +fn rerun_if_changed_in_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file( + "a/build.rs", + r#" + fn main() { + println!("cargo:rerun-if-changed=build.rs"); + } + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("build").with_stdout("").run(); +} + +#[cargo_test] +fn same_build_dir_cached_packages() { + let p = project() + .no_manifest() + .file( + "a1/Cargo.toml", + r#" + [package] + name = "a1" + version = "0.0.1" + authors = [] + [dependencies] + b = { path = "../b" } + "#, + ) + .file("a1/src/lib.rs", "") + .file( + "a2/Cargo.toml", + r#" + [package] + name = "a2" + version = "0.0.1" + authors = [] + [dependencies] + b = { path = "../b" } + "#, + ) + .file("a2/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.0.1" + authors = [] + [dependencies] + c = { path = "../c" } + "#, + ) + .file("b/src/lib.rs", "") + .file( + "c/Cargo.toml", + r#" + [package] + name = "c" + version = "0.0.1" + authors = [] + [dependencies] + d = { path = "../d" } + "#, + ) + .file("c/src/lib.rs", "") + .file("d/Cargo.toml", &basic_manifest("d", "0.0.1")) + .file("d/src/lib.rs", "") + .file( + ".cargo/config", + r#" + [build] + target-dir = "./target" + "#, + ) + .build(); + + p.cargo("build") + .cwd("a1") + .with_stderr(&format!( + "\ +[COMPILING] d v0.0.1 ({dir}/d) +[COMPILING] c v0.0.1 ({dir}/c) +[COMPILING] b v0.0.1 ({dir}/b) +[COMPILING] a1 v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + dir = p.url().to_file_path().unwrap().to_str().unwrap() + )) + .run(); + p.cargo("build") + .cwd("a2") + .with_stderr( + "\ +[COMPILING] a2 v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn no_rebuild_if_build_artifacts_move_backwards_in_time() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + p.root().move_into_the_past(); + + p.cargo("build") + .with_stdout("") + .with_stderr("[FINISHED] [..]") + .run(); +} + +#[cargo_test] +fn rebuild_if_build_artifacts_move_forward_in_time() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + p.root().move_into_the_future(); + + p.cargo("build") + .env("CARGO_LOG", "") + .with_stdout("") + .with_stderr( + "\ +[COMPILING] a v0.0.1 ([..]) +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn rebuild_if_environment_changes() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + description = "old desc" + version = "0.0.1" + authors = [] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + println!("{}", env!("CARGO_PKG_DESCRIPTION")); + } + "#, + ) + .build(); + + p.cargo("run") + .with_stdout("old desc") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]` +", + ) + .run(); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + description = "new desc" + version = "0.0.1" + authors = [] + "#, + ); + + p.cargo("run -v") + .with_stdout("new desc") + .with_stderr( + "\ +[DIRTY] foo v0.0.1 ([CWD]): the metadata changed +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn no_rebuild_when_rename_dir() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [workspace] + + [dependencies] + foo = { path = "foo" } + "#, + ) + .file("src/_unused.rs", "") + .file("build.rs", "fn main() {}") + .file("foo/Cargo.toml", &basic_manifest("foo", "0.0.1")) + .file("foo/src/lib.rs", "") + .file("foo/build.rs", "fn main() {}") + .build(); + + // make sure the most recently modified file is `src/lib.rs`, not + // `Cargo.toml`, to expose a historical bug where we forgot to strip the + // `Cargo.toml` path from looking for the package root. + cargo_test_support::sleep_ms(100); + fs::write(p.root().join("src/lib.rs"), "").unwrap(); + + p.cargo("build").run(); + let mut new = p.root(); + new.pop(); + new.push("bar"); + fs::rename(p.root(), &new).unwrap(); + + p.cargo("build") + .cwd(&new) + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); +} + +#[cargo_test] +fn unused_optional_dep() { + Package::new("registry1", "0.1.0").publish(); + Package::new("registry2", "0.1.0").publish(); + Package::new("registry3", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "p" + authors = [] + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + baz = { path = "baz" } + registry1 = "*" + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.1" + authors = [] + + [dev-dependencies] + registry2 = "*" + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.1" + authors = [] + + [dependencies] + registry3 = { version = "*", optional = true } + "#, + ) + .file("baz/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("build").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn path_dev_dep_registry_updates() { + Package::new("registry1", "0.1.0").publish(); + Package::new("registry2", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "p" + authors = [] + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.1" + authors = [] + + [dependencies] + registry1 = "*" + + [dev-dependencies] + baz = { path = "../baz"} + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.1" + authors = [] + + [dependencies] + registry2 = "*" + "#, + ) + .file("baz/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("build").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn change_panic_mode() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ['bar', 'baz'] + [profile.dev] + panic = 'abort' + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("bar/src/lib.rs", "") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.1" + authors = [] + + [lib] + proc-macro = true + + [dependencies] + bar = { path = '../bar' } + "#, + ) + .file("baz/src/lib.rs", "extern crate bar;") + .build(); + + p.cargo("build -p bar").run(); + p.cargo("build -p baz").run(); +} + +#[cargo_test] +fn dont_rebuild_based_on_plugins() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.1" + + [workspace] + members = ['baz'] + + [dependencies] + proc-macro-thing = { path = 'proc-macro-thing' } + "#, + ) + .file("src/lib.rs", "") + .file( + "proc-macro-thing/Cargo.toml", + r#" + [package] + name = "proc-macro-thing" + version = "0.1.1" + + [lib] + proc-macro = true + + [dependencies] + qux = { path = '../qux' } + "#, + ) + .file("proc-macro-thing/src/lib.rs", "") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.1" + + [dependencies] + qux = { path = '../qux' } + "#, + ) + .file("baz/src/main.rs", "fn main() {}") + .file("qux/Cargo.toml", &basic_manifest("qux", "0.1.1")) + .file("qux/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("build -p baz").run(); + p.cargo("build").with_stderr("[FINISHED] [..]\n").run(); + p.cargo("build -p bar") + .with_stderr("[FINISHED] [..]\n") + .run(); +} + +#[cargo_test] +fn reuse_workspace_lib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.1" + + [workspace] + + [dependencies] + baz = { path = 'baz' } + "#, + ) + .file("src/lib.rs", "") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.1")) + .file("baz/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("test -p baz -v --no-run") + .with_stderr( + "\ +[COMPILING] baz v0.1.1 ([..]) +[RUNNING] `rustc[..] --test [..]` +[FINISHED] [..] +[EXECUTABLE] `[..]/target/debug/deps/baz-[..][EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn reuse_shared_build_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + shared = {path = "shared"} + + [workspace] + members = ["shared", "bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("shared/Cargo.toml", &basic_manifest("shared", "0.0.1")) + .file("shared/src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + + [build-dependencies] + shared = { path = "../shared" } + "#, + ) + .file("bar/src/lib.rs", "") + .file("bar/build.rs", "fn main() {}") + .build(); + + p.cargo("build --workspace").run(); + // This should not recompile! + p.cargo("build -p foo -v") + .with_stderr( + "\ +[FRESH] shared [..] +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn changing_rustflags_is_cached() { + let p = project().file("src/lib.rs", "").build(); + + // This isn't ever cached, we always have to recompile + p.cargo("build") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + p.cargo("build -v") + .env("RUSTFLAGS", "-C linker=cc") + .with_stderr( + "\ +[DIRTY] foo v0.0.1 ([..]): the rustflags changed +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] foo v0.0.1 ([..]): the rustflags changed +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + p.cargo("build -v") + .env("RUSTFLAGS", "-C linker=cc") + .with_stderr( + "\ +[DIRTY] foo v0.0.1 ([..]): the rustflags changed +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn update_dependency_mtime_does_not_rebuild() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("build -Z mtime-on-use") + .masquerade_as_nightly_cargo(&["mtime-on-use"]) + .env("RUSTFLAGS", "-C linker=cc") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([..]) +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + // This does not make new files, but it does update the mtime of the dependency. + p.cargo("build -p bar -Z mtime-on-use") + .masquerade_as_nightly_cargo(&["mtime-on-use"]) + .env("RUSTFLAGS", "-C linker=cc") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); + // This should not recompile! + p.cargo("build -Z mtime-on-use") + .masquerade_as_nightly_cargo(&["mtime-on-use"]) + .env("RUSTFLAGS", "-C linker=cc") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); +} + +fn fingerprint_cleaner(mut dir: PathBuf, timestamp: filetime::FileTime) { + // Cargo is experimenting with letting outside projects develop some + // limited forms of GC for target_dir. This is one of the forms. + // Specifically, Cargo is updating the mtime of a file in + // target/profile/.fingerprint each time it uses the fingerprint. + // So a cleaner can remove files associated with a fingerprint + // if all the files in the fingerprint's folder are older then a time stamp without + // effecting any builds that happened since that time stamp. + let mut cleaned = false; + dir.push(".fingerprint"); + for fing in fs::read_dir(&dir).unwrap() { + let fing = fing.unwrap(); + + let outdated = |f: io::Result| { + filetime::FileTime::from_last_modification_time(&f.unwrap().metadata().unwrap()) + <= timestamp + }; + if fs::read_dir(fing.path()).unwrap().all(outdated) { + fs::remove_dir_all(fing.path()).unwrap(); + println!("remove: {:?}", fing.path()); + // a real cleaner would remove the big files in deps and build as well + // but fingerprint is sufficient for our tests + cleaned = true; + } else { + } + } + assert!( + cleaned, + "called fingerprint_cleaner, but there was nothing to remove" + ); +} + +#[cargo_test] +fn fingerprint_cleaner_does_not_rebuild() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = { path = "bar" } + + [features] + a = [] + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("build -Z mtime-on-use") + .masquerade_as_nightly_cargo(&["mtime-on-use"]) + .run(); + p.cargo("build -Z mtime-on-use --features a") + .masquerade_as_nightly_cargo(&["mtime-on-use"]) + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + if is_coarse_mtime() { + sleep_ms(1000); + } + let timestamp = filetime::FileTime::from_system_time(SystemTime::now()); + if is_coarse_mtime() { + sleep_ms(1000); + } + // This does not make new files, but it does update the mtime. + p.cargo("build -Z mtime-on-use --features a") + .masquerade_as_nightly_cargo(&["mtime-on-use"]) + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); + fingerprint_cleaner(p.target_debug_dir(), timestamp); + // This should not recompile! + p.cargo("build -Z mtime-on-use --features a") + .masquerade_as_nightly_cargo(&["mtime-on-use"]) + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); + // But this should be cleaned and so need a rebuild + p.cargo("build -Z mtime-on-use") + .masquerade_as_nightly_cargo(&["mtime-on-use"]) + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn reuse_panic_build_dep_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [build-dependencies] + bar = { path = "bar" } + + [dev-dependencies] + bar = { path = "bar" } + + [profile.dev] + panic = "abort" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + // Check that `bar` is not built twice. It is only needed once (without `panic`). + p.cargo("test --lib --no-run -v") + .with_stderr( + "\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name build_script_build [..] +[RUNNING] [..]build-script-build` +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--test[..] +[FINISHED] [..] +[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn reuse_panic_pm() { + // foo(panic) -> bar(panic) + // somepm(nopanic) -> bar(nopanic) + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = { path = "bar" } + somepm = { path = "somepm" } + + [profile.dev] + panic = "abort" + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .file( + "somepm/Cargo.toml", + r#" + [package] + name = "somepm" + version = "0.0.1" + + [lib] + proc-macro = true + + [dependencies] + bar = { path = "../bar" } + "#, + ) + .file("somepm/src/lib.rs", "extern crate bar;") + .build(); + + // bar is built once without panic (for proc-macro) and once with (for the + // normal dependency). + p.cargo("build -v") + .with_stderr_unordered( + "\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..] +[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C debuginfo=2 [..] +[COMPILING] somepm [..] +[RUNNING] `rustc --crate-name somepm [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C panic=abort[..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn bust_patched_dep() { + Package::new("registry1", "0.1.0").publish(); + Package::new("registry2", "0.1.0") + .dep("registry1", "0.1.0") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + registry2 = "0.1.0" + + [patch.crates-io] + registry1 = { path = "reg1new" } + "#, + ) + .file("src/lib.rs", "") + .file("reg1new/Cargo.toml", &basic_manifest("registry1", "0.1.0")) + .file("reg1new/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + if is_coarse_mtime() { + sleep_ms(1000); + } + + p.change_file("reg1new/src/lib.rs", ""); + if is_coarse_mtime() { + sleep_ms(1000); + } + + p.cargo("build -v") + .with_stderr( + "\ +[DIRTY] registry1 v0.1.0 ([..]): the file `reg1new/src/lib.rs` has changed ([..]) +[COMPILING] registry1 v0.1.0 ([..]) +[RUNNING] `rustc [..] +[DIRTY] registry2 v0.1.0: the dependency registry1 was rebuilt +[COMPILING] registry2 v0.1.0 +[RUNNING] `rustc [..] +[DIRTY] foo v0.0.1 ([..]): the dependency registry2 was rebuilt +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("build -v") + .with_stderr( + "\ +[FRESH] registry1 v0.1.0 ([..]) +[FRESH] registry2 v0.1.0 +[FRESH] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn rebuild_on_mid_build_file_modification() { + let server = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = server.local_addr().unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["root", "proc_macro_dep"] + "#, + ) + .file( + "root/Cargo.toml", + r#" + [package] + name = "root" + version = "0.1.0" + authors = [] + + [dependencies] + proc_macro_dep = { path = "../proc_macro_dep" } + "#, + ) + .file( + "root/src/lib.rs", + r#" + #[macro_use] + extern crate proc_macro_dep; + + #[derive(Noop)] + pub struct X; + "#, + ) + .file( + "proc_macro_dep/Cargo.toml", + r#" + [package] + name = "proc_macro_dep" + version = "0.1.0" + authors = [] + + [lib] + proc-macro = true + "#, + ) + .file( + "proc_macro_dep/src/lib.rs", + &format!( + r#" + extern crate proc_macro; + + use std::io::Read; + use std::net::TcpStream; + use proc_macro::TokenStream; + + #[proc_macro_derive(Noop)] + pub fn noop(_input: TokenStream) -> TokenStream {{ + let mut stream = TcpStream::connect("{}").unwrap(); + let mut v = Vec::new(); + stream.read_to_end(&mut v).unwrap(); + "".parse().unwrap() + }} + "#, + addr + ), + ) + .build(); + let root = p.root(); + + let t = thread::spawn(move || { + let socket = server.accept().unwrap().0; + sleep_ms(1000); + let mut file = OpenOptions::new() + .write(true) + .append(true) + .open(root.join("root/src/lib.rs")) + .unwrap(); + writeln!(file, "// modified").expect("Failed to append to root sources"); + drop(file); + drop(socket); + drop(server.accept().unwrap()); + }); + + p.cargo("build") + .with_stderr( + "\ +[COMPILING] proc_macro_dep v0.1.0 ([..]/proc_macro_dep) +[COMPILING] root v0.1.0 ([..]/root) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("build -v") + .with_stderr( + "\ +[FRESH] proc_macro_dep v0.1.0 ([..]/proc_macro_dep) +[DIRTY] root v0.1.0 ([..]/root): the file `root/src/lib.rs` has changed ([..]) +[COMPILING] root v0.1.0 ([..]/root) +[RUNNING] `rustc [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + t.join().ok().unwrap(); +} + +#[cargo_test] +fn dirty_both_lib_and_test() { + // This tests that all artifacts that depend on the results of a build + // script will get rebuilt when the build script reruns, even for separate + // commands. It does the following: + // + // 1. Project "foo" has a build script which will compile a small + // staticlib to link against. Normally this would use the `cc` crate, + // but here we just use rustc to avoid the `cc` dependency. + // 2. Build the library. + // 3. Build the unit test. The staticlib intentionally has a bad value. + // 4. Rewrite the staticlib with the correct value. + // 5. Build the library again. + // 6. Build the unit test. This should recompile. + + let slib = |n| { + format!( + r#" + #[no_mangle] + pub extern "C" fn doit() -> i32 {{ + return {}; + }} + "#, + n + ) + }; + + let p = project() + .file( + "src/lib.rs", + r#" + extern "C" { + fn doit() -> i32; + } + + #[test] + fn t1() { + assert_eq!(unsafe { doit() }, 1, "doit assert failure"); + } + "#, + ) + .file( + "build.rs", + r#" + use std::env; + use std::path::PathBuf; + use std::process::Command; + + fn main() { + let rustc = env::var_os("RUSTC").unwrap(); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + assert!( + Command::new(rustc) + .args(&[ + "--crate-type=staticlib", + "--out-dir", + out_dir.to_str().unwrap(), + "slib.rs" + ]) + .status() + .unwrap() + .success(), + "slib build failed" + ); + println!("cargo:rustc-link-lib=slib"); + println!("cargo:rustc-link-search={}", out_dir.display()); + } + "#, + ) + .file("slib.rs", &slib(2)) + .build(); + + p.cargo("build").run(); + + // 2 != 1 + p.cargo("test --lib") + .with_status(101) + .with_stdout_contains("[..]doit assert failure[..]") + .run(); + + if is_coarse_mtime() { + // #5918 + sleep_ms(1000); + } + // Fix the mistake. + p.change_file("slib.rs", &slib(1)); + + p.cargo("build").run(); + // This should recompile with the new static lib, and the test should pass. + p.cargo("test --lib").run(); +} + +#[cargo_test] +fn script_fails_stay_dirty() { + // Check if a script is aborted (such as hitting Ctrl-C) that it will re-run. + // Steps: + // 1. Build to establish fingerprints. + // 2. Make a change that triggers the build script to re-run. Abort the + // script while it is running. + // 3. Run the build again and make sure it re-runs the script. + let p = project() + .file( + "build.rs", + r#" + mod helper; + fn main() { + println!("cargo:rerun-if-changed=build.rs"); + helper::doit(); + } + "#, + ) + .file("helper.rs", "pub fn doit() {}") + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + if is_coarse_mtime() { + sleep_ms(1000); + } + p.change_file("helper.rs", r#"pub fn doit() {panic!("Crash!");}"#); + p.cargo("build") + .with_stderr_contains("[..]Crash![..]") + .with_status(101) + .run(); + // There was a bug where this second call would be "fresh". + p.cargo("build") + .with_stderr_contains("[..]Crash![..]") + .with_status(101) + .run(); +} + +#[cargo_test] +fn simulated_docker_deps_stay_cached() { + // Test what happens in docker where the nanoseconds are zeroed out. + Package::new("regdep", "1.0.0").publish(); + Package::new("regdep_old_style", "1.0.0") + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .publish(); + Package::new("regdep_env", "1.0.0") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-env-changed=SOMEVAR"); + } + "#, + ) + .file("src/lib.rs", "") + .publish(); + Package::new("regdep_rerun", "1.0.0") + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-changed=build.rs"); + } + "#, + ) + .file("src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + pathdep = { path = "pathdep" } + regdep = "1.0" + regdep_old_style = "1.0" + regdep_env = "1.0" + regdep_rerun = "1.0" + "#, + ) + .file( + "src/lib.rs", + " + extern crate pathdep; + extern crate regdep; + extern crate regdep_old_style; + extern crate regdep_env; + extern crate regdep_rerun; + ", + ) + .file("build.rs", "fn main() {}") + .file("pathdep/Cargo.toml", &basic_manifest("pathdep", "1.0.0")) + .file("pathdep/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + let already_zero = { + // This happens on HFS with 1-second timestamp resolution, + // or other filesystems where it just so happens to write exactly on a + // 1-second boundary. + let metadata = fs::metadata(p.root().join("src/lib.rs")).unwrap(); + let mtime = FileTime::from_last_modification_time(&metadata); + mtime.nanoseconds() == 0 + }; + + // Recursively remove `nanoseconds` from every path. + fn zeropath(path: &Path) { + for entry in walkdir::WalkDir::new(path) + .into_iter() + .filter_map(|e| e.ok()) + { + let metadata = fs::metadata(entry.path()).unwrap(); + let mtime = metadata.modified().unwrap(); + let mtime_duration = mtime.duration_since(SystemTime::UNIX_EPOCH).unwrap(); + let trunc_mtime = FileTime::from_unix_time(mtime_duration.as_secs() as i64, 0); + let atime = metadata.accessed().unwrap(); + let atime_duration = atime.duration_since(SystemTime::UNIX_EPOCH).unwrap(); + let trunc_atime = FileTime::from_unix_time(atime_duration.as_secs() as i64, 0); + if let Err(e) = filetime::set_file_times(entry.path(), trunc_atime, trunc_mtime) { + // Windows doesn't allow changing filetimes on some things + // (directories, other random things I'm not sure why). Just + // ignore them. + if e.kind() == std::io::ErrorKind::PermissionDenied { + println!("PermissionDenied filetime on {:?}", entry.path()); + } else { + panic!("FileTime error on {:?}: {:?}", entry.path(), e); + } + } + } + } + zeropath(&p.root()); + zeropath(&paths::home()); + + if already_zero { + println!("already zero"); + // If it was already truncated, then everything stays fresh. + p.cargo("build -v") + .with_stderr_unordered( + "\ +[FRESH] pathdep [..] +[FRESH] regdep [..] +[FRESH] regdep_env [..] +[FRESH] regdep_old_style [..] +[FRESH] regdep_rerun [..] +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); + } else { + println!("not already zero"); + // It is not ideal that `foo` gets recompiled, but that is the current + // behavior. Currently mtimes are ignored for registry deps. + // + // Note that this behavior is due to the fact that `foo` has a build + // script in "old" mode where it doesn't print `rerun-if-*`. In this + // mode we use `Precalculated` to fingerprint a path dependency, where + // `Precalculated` is an opaque string which has the most recent mtime + // in it. It differs between builds because one has nsec=0 and the other + // likely has a nonzero nsec. Hence, the rebuild. + p.cargo("build -v") + .with_stderr_unordered( + "\ +[FRESH] pathdep [..] +[FRESH] regdep [..] +[FRESH] regdep_env [..] +[FRESH] regdep_old_style [..] +[FRESH] regdep_rerun [..] +[DIRTY] foo [..]: the precalculated components changed +[COMPILING] foo [..] +[RUNNING] [..]/foo-[..]/build-script-build[..] +[RUNNING] `rustc --crate-name foo[..] +[FINISHED] [..] +", + ) + .run(); + } +} + +#[cargo_test] +fn metadata_change_invalidates() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + for attr in &[ + "authors = [\"foo\"]", + "description = \"desc\"", + "homepage = \"https://example.com\"", + "repository =\"https://example.com\"", + ] { + let mut file = OpenOptions::new() + .write(true) + .append(true) + .open(p.root().join("Cargo.toml")) + .unwrap(); + writeln!(file, "{}", attr).unwrap(); + p.cargo("build") + .with_stderr_contains("[COMPILING] foo [..]") + .run(); + } + p.cargo("build -v") + .with_stderr_contains("[FRESH] foo[..]") + .run(); + assert_eq!(p.glob("target/debug/deps/libfoo-*.rlib").count(), 1); +} + +#[cargo_test] +fn edition_change_invalidates() { + const MANIFEST: &str = r#" + [package] + name = "foo" + version = "0.1.0" + "#; + let p = project() + .file("Cargo.toml", MANIFEST) + .file("src/lib.rs", "") + .build(); + p.cargo("build").run(); + p.change_file("Cargo.toml", &format!("{}edition = \"2018\"", MANIFEST)); + p.cargo("build") + .with_stderr_contains("[COMPILING] foo [..]") + .run(); + p.change_file( + "Cargo.toml", + &format!( + r#"{}edition = "2018" + [lib] + edition = "2015" + "#, + MANIFEST + ), + ); + p.cargo("build") + .with_stderr_contains("[COMPILING] foo [..]") + .run(); + p.cargo("build -v") + .with_stderr_contains("[FRESH] foo[..]") + .run(); + assert_eq!(p.glob("target/debug/deps/libfoo-*.rlib").count(), 1); +} + +#[cargo_test] +fn rename_with_path_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + a = { path = 'a' } + "#, + ) + .file("src/lib.rs", "extern crate a; pub fn foo() { a::foo(); }") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + + [dependencies] + b = { path = 'b' } + "#, + ) + .file("a/src/lib.rs", "extern crate b; pub fn foo() { b::foo() }") + .file( + "a/b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + "#, + ) + .file("a/b/src/lib.rs", "pub fn foo() { }"); + let p = p.build(); + + p.cargo("build").run(); + + // Now rename the root directory and rerun `cargo run`. Not only should we + // not build anything but we also shouldn't crash. + let mut new = p.root(); + new.pop(); + new.push("foo2"); + + fs::rename(p.root(), &new).unwrap(); + + p.cargo("build") + .cwd(&new) + .with_stderr("[FINISHED] [..]") + .run(); +} + +#[cargo_test] +fn move_target_directory_with_path_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + "#, + ) + .file("src/lib.rs", "extern crate a; pub use a::print_msg;") + .file( + "a/build.rs", + r###" + use std::env; + use std::fs; + use std::path::Path; + + fn main() { + println!("cargo:rerun-if-changed=build.rs"); + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("hello.rs"); + fs::write(&dest_path, r#" + pub fn message() -> &'static str { + "Hello, World!" + } + "#).unwrap(); + } + "###, + ) + .file( + "a/src/lib.rs", + r#" + include!(concat!(env!("OUT_DIR"), "/hello.rs")); + pub fn print_msg() { message(); } + "#, + ); + let p = p.build(); + + let mut parent = p.root(); + parent.pop(); + + p.cargo("build").run(); + + let new_target = p.root().join("target2"); + fs::rename(p.root().join("target"), &new_target).unwrap(); + + p.cargo("build") + .env("CARGO_TARGET_DIR", &new_target) + .with_stderr("[FINISHED] [..]") + .run(); +} + +#[cargo_test] +fn rerun_if_changes() { + let p = project() + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rerun-if-env-changed=FOO"); + if std::env::var("FOO").is_ok() { + println!("cargo:rerun-if-env-changed=BAR"); + } + } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("build").with_stderr("[FINISHED] [..]").run(); + + p.cargo("build -v") + .env("FOO", "1") + .with_stderr( + "\ +[DIRTY] foo [..]: the env variable FOO changed +[COMPILING] foo [..] +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("build") + .env("FOO", "1") + .with_stderr("[FINISHED] [..]") + .run(); + + p.cargo("build -v") + .env("FOO", "1") + .env("BAR", "1") + .with_stderr( + "\ +[DIRTY] foo [..]: the env variable BAR changed +[COMPILING] foo [..] +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("build") + .env("FOO", "1") + .env("BAR", "1") + .with_stderr("[FINISHED] [..]") + .run(); + + p.cargo("build -v") + .env("BAR", "2") + .with_stderr( + "\ +[DIRTY] foo [..]: the env variable FOO changed +[COMPILING] foo [..] +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("build") + .env("BAR", "2") + .with_stderr("[FINISHED] [..]") + .run(); +} + +#[cargo_test] +fn channel_shares_filenames() { + // Test that different "nightly" releases use the same output filename. + + // Create separate rustc binaries to emulate running different toolchains. + let nightly1 = format!( + "\ +rustc 1.44.0-nightly (38114ff16 2020-03-21) +binary: rustc +commit-hash: 38114ff16e7856f98b2b4be7ab4cd29b38bed59a +commit-date: 2020-03-21 +host: {} +release: 1.44.0-nightly +LLVM version: 9.0 +", + rustc_host() + ); + + let nightly2 = format!( + "\ +rustc 1.44.0-nightly (a5b09d354 2020-03-31) +binary: rustc +commit-hash: a5b09d35473615e7142f5570f5c5fad0caf68bd2 +commit-date: 2020-03-31 +host: {} +release: 1.44.0-nightly +LLVM version: 9.0 +", + rustc_host() + ); + + let beta1 = format!( + "\ +rustc 1.43.0-beta.3 (4c587bbda 2020-03-25) +binary: rustc +commit-hash: 4c587bbda04ab55aaf56feab11dfdfe387a85d7a +commit-date: 2020-03-25 +host: {} +release: 1.43.0-beta.3 +LLVM version: 9.0 +", + rustc_host() + ); + + let beta2 = format!( + "\ +rustc 1.42.0-beta.5 (4e1c5f0e9 2020-02-28) +binary: rustc +commit-hash: 4e1c5f0e9769a588b91c977e3d81e140209ef3a2 +commit-date: 2020-02-28 +host: {} +release: 1.42.0-beta.5 +LLVM version: 9.0 +", + rustc_host() + ); + + let stable1 = format!( + "\ +rustc 1.42.0 (b8cedc004 2020-03-09) +binary: rustc +commit-hash: b8cedc00407a4c56a3bda1ed605c6fc166655447 +commit-date: 2020-03-09 +host: {} +release: 1.42.0 +LLVM version: 9.0 +", + rustc_host() + ); + + let stable2 = format!( + "\ +rustc 1.41.1 (f3e1a954d 2020-02-24) +binary: rustc +commit-hash: f3e1a954d2ead4e2fc197c7da7d71e6c61bad196 +commit-date: 2020-02-24 +host: {} +release: 1.41.1 +LLVM version: 9.0 +", + rustc_host() + ); + + let compiler = project() + .at("compiler") + .file("Cargo.toml", &basic_manifest("compiler", "0.1.0")) + .file( + "src/main.rs", + r#" + fn main() { + if std::env::args_os().any(|a| a == "-vV") { + print!("{}", env!("FUNKY_VERSION_TEST")); + return; + } + let mut cmd = std::process::Command::new("rustc"); + cmd.args(std::env::args_os().skip(1)); + assert!(cmd.status().unwrap().success()); + } + "#, + ) + .build(); + + let makeit = |version, vv| { + // Force a rebuild. + compiler.target_debug_dir().join("deps").rm_rf(); + compiler.cargo("build").env("FUNKY_VERSION_TEST", vv).run(); + fs::rename(compiler.bin("compiler"), compiler.bin(version)).unwrap(); + }; + makeit("nightly1", nightly1); + makeit("nightly2", nightly2); + makeit("beta1", beta1); + makeit("beta2", beta2); + makeit("stable1", stable1); + makeit("stable2", stable2); + + // Run `cargo check` with different rustc versions to observe its behavior. + let p = project().file("src/lib.rs", "").build(); + + // Runs `cargo check` and returns the rmeta filename created. + // Checks that the freshness matches the given value. + let check = |version, fresh| -> String { + let output = p + .cargo("check --message-format=json") + .env("RUSTC", compiler.bin(version)) + .exec_with_output() + .unwrap(); + // Collect the filenames generated. + let mut artifacts: Vec<_> = std::str::from_utf8(&output.stdout) + .unwrap() + .lines() + .filter_map(|line| { + let value: serde_json::Value = serde_json::from_str(line).unwrap(); + if value["reason"].as_str().unwrap() == "compiler-artifact" { + assert_eq!(value["fresh"].as_bool().unwrap(), fresh); + let filenames = value["filenames"].as_array().unwrap(); + assert_eq!(filenames.len(), 1); + Some(filenames[0].to_string()) + } else { + None + } + }) + .collect(); + // Should only generate one rmeta file. + assert_eq!(artifacts.len(), 1); + artifacts.pop().unwrap() + }; + + let nightly1_name = check("nightly1", false); + assert_eq!(check("nightly1", true), nightly1_name); + assert_eq!(check("nightly2", false), nightly1_name); // same as before + assert_eq!(check("nightly2", true), nightly1_name); + // Should rebuild going back to nightly1. + assert_eq!(check("nightly1", false), nightly1_name); + + let beta1_name = check("beta1", false); + assert_ne!(beta1_name, nightly1_name); + assert_eq!(check("beta1", true), beta1_name); + assert_eq!(check("beta2", false), beta1_name); // same as before + assert_eq!(check("beta2", true), beta1_name); + // Should rebuild going back to beta1. + assert_eq!(check("beta1", false), beta1_name); + + let stable1_name = check("stable1", false); + assert_ne!(stable1_name, nightly1_name); + assert_ne!(stable1_name, beta1_name); + let stable2_name = check("stable2", false); + assert_ne!(stable1_name, stable2_name); + // Check everything is fresh. + assert_eq!(check("stable1", true), stable1_name); + assert_eq!(check("stable2", true), stable2_name); + assert_eq!(check("beta1", true), beta1_name); + assert_eq!(check("nightly1", true), nightly1_name); +} + +#[cargo_test] +fn linking_interrupted() { + // Interrupt during the linking phase shouldn't leave test executable as "fresh". + + // This is used to detect when linking starts, then to pause the linker so + // that the test can kill cargo. + let link_listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let link_addr = link_listener.local_addr().unwrap(); + + // This is used to detect when rustc exits. + let rustc_listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let rustc_addr = rustc_listener.local_addr().unwrap(); + + // Create a linker that we can interrupt. + let linker = project() + .at("linker") + .file("Cargo.toml", &basic_manifest("linker", "1.0.0")) + .file( + "src/main.rs", + &r#" + fn main() { + // Figure out the output filename. + let output = match std::env::args().find(|a| a.starts_with("/OUT:")) { + Some(s) => s[5..].to_string(), + None => { + let mut args = std::env::args(); + loop { + if args.next().unwrap() == "-o" { + break; + } + } + args.next().unwrap() + } + }; + std::fs::remove_file(&output).unwrap(); + std::fs::write(&output, "").unwrap(); + // Tell the test that we are ready to be interrupted. + let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap(); + // Wait for the test to kill us. + std::thread::sleep(std::time::Duration::new(60, 0)); + } + "# + .replace("__ADDR__", &link_addr.to_string()), + ) + .build(); + linker.cargo("build").run(); + + // Create a wrapper around rustc that will tell us when rustc is finished. + let rustc = project() + .at("rustc-waiter") + .file("Cargo.toml", &basic_manifest("rustc-waiter", "1.0.0")) + .file( + "src/main.rs", + &r#" + fn main() { + let mut conn = None; + // Check for a normal build (not -vV or --print). + if std::env::args().any(|arg| arg == "t1") { + // Tell the test that rustc has started. + conn = Some(std::net::TcpStream::connect("__ADDR__").unwrap()); + } + let status = std::process::Command::new("rustc") + .args(std::env::args().skip(1)) + .status() + .expect("rustc to run"); + std::process::exit(status.code().unwrap_or(1)); + } + "# + .replace("__ADDR__", &rustc_addr.to_string()), + ) + .build(); + rustc.cargo("build").run(); + + // Build it once so that the fingerprint gets saved to disk. + let p = project() + .file("src/lib.rs", "") + .file("tests/t1.rs", "") + .build(); + p.cargo("test --test t1 --no-run").run(); + + // Make a change, start a build, then interrupt it. + p.change_file("src/lib.rs", "// modified"); + let linker_env = format!("CARGO_TARGET_{}_LINKER", rustc_host_env()); + // NOTE: This assumes that the paths to the linker or rustc are not in the + // fingerprint. But maybe they should be? + let mut cmd = p + .cargo("test --test t1 --no-run") + .env(&linker_env, linker.bin("linker")) + .env("RUSTC", rustc.bin("rustc-waiter")) + .build_command(); + let mut child = cmd + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1") + .spawn() + .unwrap(); + // Wait for rustc to start. + let mut rustc_conn = rustc_listener.accept().unwrap().0; + // Wait for linking to start. + drop(link_listener.accept().unwrap()); + + // Interrupt the child. + death::ctrl_c(&mut child); + assert!(!child.wait().unwrap().success()); + // Wait for rustc to exit. If we don't wait, then the command below could + // start while rustc is still being torn down. + let mut buf = [0]; + drop(rustc_conn.read_exact(&mut buf)); + + // Build again, shouldn't be fresh. + p.cargo("test --test t1 -v") + .with_stderr( + "\ +[DIRTY] foo v0.0.1 ([..]): the config settings changed +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..] +[RUNNING] `rustc --crate-name t1 [..] +[FINISHED] [..] +[RUNNING] `[..]target/debug/deps/t1-[..][EXE]` +", + ) + .run(); +} + +#[cargo_test] +#[cfg_attr( + not(all(target_arch = "x86_64", target_os = "windows", target_env = "msvc")), + ignore +)] +fn lld_is_fresh() { + // Check for bug when using lld linker that it remains fresh with dylib. + let p = project() + .file( + ".cargo/config", + r#" + [target.x86_64-pc-windows-msvc] + linker = "rust-lld" + rustflags = ["-C", "link-arg=-fuse-ld=lld"] + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [lib] + crate-type = ["dylib"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("build -v") + .with_stderr("[FRESH] foo [..]\n[FINISHED] [..]") + .run(); +} + +#[cargo_test] +fn env_in_code_causes_rebuild() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + println!("{:?}", option_env!("FOO")); + println!("{:?}", option_env!("FOO\nBAR")); + } + "#, + ) + .build(); + + p.cargo("build").env_remove("FOO").run(); + p.cargo("build") + .env_remove("FOO") + .with_stderr("[FINISHED] [..]") + .run(); + p.cargo("build -v") + .env("FOO", "bar") + .with_stderr( + "\ +[DIRTY] foo [..]: the environment variable FOO changed +[COMPILING][..] +[RUNNING] `rustc [..] +[FINISHED][..]", + ) + .run(); + p.cargo("build") + .env("FOO", "bar") + .with_stderr("[FINISHED][..]") + .run(); + p.cargo("build -v") + .env("FOO", "baz") + .with_stderr( + "\ +[DIRTY] foo [..]: the environment variable FOO changed +[COMPILING][..] +[RUNNING] `rustc [..] +[FINISHED][..]", + ) + .run(); + p.cargo("build") + .env("FOO", "baz") + .with_stderr("[FINISHED][..]") + .run(); + p.cargo("build -v") + .env_remove("FOO") + .with_stderr( + "\ +[DIRTY] foo [..]: the environment variable FOO changed +[COMPILING][..] +[RUNNING] `rustc [..] +[FINISHED][..]", + ) + .run(); + p.cargo("build") + .env_remove("FOO") + .with_stderr("[FINISHED][..]") + .run(); + + let interesting = " #!$\nabc\r\\\t\u{8}\r\n"; + p.cargo("build").env("FOO", interesting).run(); + p.cargo("build") + .env("FOO", interesting) + .with_stderr("[FINISHED][..]") + .run(); + + p.cargo("build").env("FOO\nBAR", interesting).run(); + p.cargo("build") + .env("FOO\nBAR", interesting) + .with_stderr("[FINISHED][..]") + .run(); +} + +#[cargo_test] +fn env_build_script_no_rebuild() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file( + "build.rs", + r#" + fn main() { + println!("cargo:rustc-env=FOO=bar"); + } + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + println!("{:?}", env!("FOO")); + } + "#, + ) + .build(); + + p.cargo("build").run(); + p.cargo("build").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn cargo_env_changes() { + // Checks that changes to the env var CARGO in the dep-info file triggers + // a rebuild. + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file( + "src/main.rs", + r#" + fn main() { + println!("{:?}", env!("CARGO")); + } + "#, + ) + .build(); + + let cargo_exe = cargo_test_support::cargo_exe(); + let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap()); + std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap(); + let other_cargo = || { + let mut pb = cargo_test_support::process(&other_cargo_path); + pb.cwd(p.root()); + cargo_test_support::execs().with_process_builder(pb) + }; + + p.cargo("check").run(); + other_cargo() + .arg("check") + .arg("-v") + .with_stderr( + "\ +[DIRTY] foo v1.0.0 ([..]): the environment variable CARGO changed +[CHECKING] foo [..] +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); + + // And just to confirm that without using env! it doesn't rebuild. + p.change_file("src/main.rs", "fn main() {}"); + p.cargo("check") + .with_stderr( + "\ +[CHECKING] foo [..] +[FINISHED] [..] +", + ) + .run(); + other_cargo() + .arg("check") + .arg("-v") + .with_stderr( + "\ +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn changing_linker() { + // Changing linker should rebuild. + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("build").run(); + let linker_env = format!("CARGO_TARGET_{}_LINKER", rustc_host_env()); + p.cargo("build --verbose") + .env(&linker_env, "nonexistent-linker") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] -C linker=nonexistent-linker [..]` +[ERROR] [..]linker[..] +", + ) + .run(); +} + +#[cargo_test] +fn verify_source_before_recompile() { + Package::new("bar", "0.1.0") + .file("src/lib.rs", "") + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("vendor --respect-source-config").run(); + p.change_file( + ".cargo/config.toml", + r#" + [source.crates-io] + replace-with = 'vendor' + + [source.vendor] + directory = 'vendor' + "#, + ); + // Sanity check: vendoring works correctly. + p.cargo("check --verbose") + .with_stderr_contains("[RUNNING] `rustc --crate-name bar [CWD]/vendor/bar/src/lib.rs[..]") + .run(); + // Now modify vendored crate. + p.change_file( + "vendor/bar/src/lib.rs", + r#"compile_error!("You shall not pass!");"#, + ); + // Should ignore modifed sources without any recompile. + p.cargo("check --verbose") + .with_stderr( + "\ +[FRESH] bar v0.1.0 +[FRESH] foo v0.1.0 ([CWD]) +[FINISHED] dev [..] +", + ) + .run(); + + // Add a `RUSTFLAGS` to trigger a recompile. + // + // Cargo should refuse to build because of checksum verfication failure. + // Cargo shouldn't recompile dependency `bar`. + p.cargo("check --verbose") + .env("RUSTFLAGS", "-W warnings") + .with_status(101) + .with_stderr( + "\ +error: the listed checksum of `[CWD]/vendor/bar/src/lib.rs` has changed: +expected: [..] +actual: [..] + +directory sources are not [..] +", + ) + .run(); +} diff --git a/tests/testsuite/future_incompat_report.rs b/tests/testsuite/future_incompat_report.rs new file mode 100644 index 0000000..9f451a6 --- /dev/null +++ b/tests/testsuite/future_incompat_report.rs @@ -0,0 +1,391 @@ +//! Tests for future-incompat-report messages +//! +//! Note that these tests use the -Zfuture-incompat-test for rustc. +//! This causes rustc to treat *every* lint as future-incompatible. +//! This is done because future-incompatible lints are inherently +//! ephemeral, but we don't want to continually update these tests. +//! So we pick some random lint that will likely always be the same +//! over time. + +use super::config::write_config_toml; +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_manifest, project, Project}; + +// An arbitrary lint (unused_variables) that triggers a lint. +// We use a special flag to force it to generate a report. +const FUTURE_EXAMPLE: &'static str = "fn main() { let x = 1; }"; +// Some text that will be displayed when the lint fires. +const FUTURE_OUTPUT: &'static str = "[..]unused_variables[..]"; + +fn simple_project() -> Project { + project() + .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) + .file("src/main.rs", FUTURE_EXAMPLE) + .build() +} + +#[cargo_test( + nightly, + reason = "-Zfuture-incompat-test requires nightly (permanently)" +)] +fn output_on_stable() { + let p = simple_project(); + + p.cargo("check") + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .with_stderr_contains(FUTURE_OUTPUT) + .with_stderr_contains("[..]cargo report[..]") + .run(); +} + +// This feature is stable, and should not be gated +#[cargo_test] +fn no_gate_future_incompat_report() { + let p = simple_project(); + + p.cargo("check --future-incompat-report") + .with_status(0) + .run(); + + p.cargo("report future-incompatibilities --id foo") + .with_stderr_contains("error: no reports are currently available") + .with_status(101) + .run(); +} + +#[cargo_test( + nightly, + reason = "-Zfuture-incompat-test requires nightly (permanently)" +)] +fn test_zero_future_incompat() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + // No note if --future-incompat-report is not specified. + p.cargo("check") + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .with_stderr( + "\ +[CHECKING] foo v0.0.0 [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("check --future-incompat-report") + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .with_stderr( + "\ +[FINISHED] [..] +note: 0 dependencies had future-incompatible warnings +", + ) + .run(); +} + +#[cargo_test( + nightly, + reason = "-Zfuture-incompat-test requires nightly (permanently)" +)] +fn test_single_crate() { + let p = simple_project(); + + for command in &["build", "check", "rustc", "test"] { + let check_has_future_compat = || { + p.cargo(command) + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .with_stderr_contains(FUTURE_OUTPUT) + .with_stderr_contains("warning: the following packages contain code that will be rejected by a future version of Rust: foo v0.0.0 [..]") + .with_stderr_does_not_contain("[..]incompatibility[..]") + .run(); + }; + + // Check that we show a message with no [future-incompat-report] config section + write_config_toml(""); + check_has_future_compat(); + + // Check that we show a message with `frequency = "always"` + write_config_toml( + "\ +[future-incompat-report] +frequency = 'always' +", + ); + check_has_future_compat(); + + // Check that we do not show a message with `frequency = "never"` + write_config_toml( + "\ +[future-incompat-report] +frequency = 'never' +", + ); + p.cargo(command) + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .with_stderr_contains(FUTURE_OUTPUT) + .with_stderr_does_not_contain("[..]rejected[..]") + .with_stderr_does_not_contain("[..]incompatibility[..]") + .run(); + + // Check that passing `--future-incompat-report` overrides `frequency = 'never'` + p.cargo(command).arg("--future-incompat-report") + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .with_stderr_contains(FUTURE_OUTPUT) + .with_stderr_contains("warning: the following packages contain code that will be rejected by a future version of Rust: foo v0.0.0 [..]") + .with_stderr_contains(" - foo@0.0.0[..]") + .run(); + } +} + +#[cargo_test( + nightly, + reason = "-Zfuture-incompat-test requires nightly (permanently)" +)] +fn test_multi_crate() { + Package::new("first-dep", "0.0.1") + .file("src/lib.rs", FUTURE_EXAMPLE) + .publish(); + Package::new("second-dep", "0.0.2") + .file("src/lib.rs", FUTURE_EXAMPLE) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + + [dependencies] + first-dep = "*" + second-dep = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + for command in &["build", "check", "rustc", "test"] { + p.cargo(command) + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .with_stderr_does_not_contain(FUTURE_OUTPUT) + .with_stderr_contains("warning: the following packages contain code that will be rejected by a future version of Rust: first-dep v0.0.1, second-dep v0.0.2") + // Check that we don't have the 'triggers' message shown at the bottom of this loop, + // and that we don't explain how to show a per-package report + .with_stderr_does_not_contain("[..]triggers[..]") + .with_stderr_does_not_contain("[..]--package[..]") + .with_stderr_does_not_contain("[..]-p[..]") + .run(); + + p.cargo(command).arg("--future-incompat-report") + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .with_stderr_contains("warning: the following packages contain code that will be rejected by a future version of Rust: first-dep v0.0.1, second-dep v0.0.2") + .with_stderr_contains(" - first-dep@0.0.1") + .with_stderr_contains(" - second-dep@0.0.2") + .run(); + + p.cargo("report future-incompatibilities").arg("--package").arg("first-dep@0.0.1") + .with_stdout_contains("The package `first-dep v0.0.1` currently triggers the following future incompatibility lints:") + .with_stdout_contains(FUTURE_OUTPUT) + .with_stdout_does_not_contain("[..]second-dep-0.0.2/src[..]") + .run(); + + p.cargo("report future-incompatibilities").arg("--package").arg("second-dep@0.0.2") + .with_stdout_contains("The package `second-dep v0.0.2` currently triggers the following future incompatibility lints:") + .with_stdout_contains(FUTURE_OUTPUT) + .with_stdout_does_not_contain("[..]first-dep-0.0.1/src[..]") + .run(); + } + + // Test that passing the correct id via '--id' doesn't generate a warning message + let output = p + .cargo("check") + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .exec_with_output() + .unwrap(); + + // Extract the 'id' from the stdout. We are looking + // for the id in a line of the form "run `cargo report future-incompatibilities --id yZ7S`" + // which is generated by Cargo to tell the user what command to run + // This is just to test that passing the id suppresses the warning mesasge. Any users needing + // access to the report from a shell script should use the `--future-incompat-report` flag + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + + // Find '--id ' in the output + let mut iter = stderr.split(' '); + iter.find(|w| *w == "--id").unwrap(); + let id = iter + .next() + .unwrap_or_else(|| panic!("Unexpected output:\n{}", stderr)); + // Strip off the trailing '`' included in the output + let id: String = id.chars().take_while(|c| *c != '`').collect(); + + p.cargo(&format!("report future-incompatibilities --id {}", id)) + .with_stdout_contains("The package `first-dep v0.0.1` currently triggers the following future incompatibility lints:") + .with_stdout_contains("The package `second-dep v0.0.2` currently triggers the following future incompatibility lints:") + .run(); + + // Test without --id, and also the full output of the report. + let output = p + .cargo("report future-incompat") + .exec_with_output() + .unwrap(); + let output = std::str::from_utf8(&output.stdout).unwrap(); + assert!(output.starts_with("The following warnings were discovered")); + let mut lines = output + .lines() + // Skip the beginning of the per-package information. + .skip_while(|line| !line.starts_with("The package")); + for expected in &["first-dep v0.0.1", "second-dep v0.0.2"] { + assert_eq!( + &format!( + "The package `{}` currently triggers the following future incompatibility lints:", + expected + ), + lines.next().unwrap(), + "Bad output:\n{}", + output + ); + let mut count = 0; + while let Some(line) = lines.next() { + if line.is_empty() { + break; + } + count += 1; + } + assert!(count > 0); + } + assert_eq!(lines.next(), None); +} + +#[cargo_test( + nightly, + reason = "-Zfuture-incompat-test requires nightly (permanently)" +)] +fn color() { + let p = simple_project(); + + p.cargo("check") + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .masquerade_as_nightly_cargo(&["future-incompat-test"]) + .run(); + + p.cargo("report future-incompatibilities") + .with_stdout_does_not_contain("[..]\x1b[[..]") + .run(); + + p.cargo("report future-incompatibilities") + .env("CARGO_TERM_COLOR", "always") + .with_stdout_contains("[..]\x1b[[..]") + .run(); +} + +#[cargo_test( + nightly, + reason = "-Zfuture-incompat-test requires nightly (permanently)" +)] +fn bad_ids() { + let p = simple_project(); + + p.cargo("report future-incompatibilities --id 1") + .with_status(101) + .with_stderr("error: no reports are currently available") + .run(); + + p.cargo("check") + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .masquerade_as_nightly_cargo(&["future-incompat-test"]) + .run(); + + p.cargo("report future-incompatibilities --id foo") + .with_status(1) + .with_stderr("error: Invalid value: could not parse `foo` as a number") + .run(); + + p.cargo("report future-incompatibilities --id 7") + .with_status(101) + .with_stderr( + "\ +error: could not find report with ID 7 +Available IDs are: 1 +", + ) + .run(); +} + +#[cargo_test( + nightly, + reason = "-Zfuture-incompat-test requires nightly (permanently)" +)] +fn suggestions_for_updates() { + Package::new("with_updates", "1.0.0") + .file("src/lib.rs", FUTURE_EXAMPLE) + .publish(); + Package::new("big_update", "1.0.0") + .file("src/lib.rs", FUTURE_EXAMPLE) + .publish(); + Package::new("without_updates", "1.0.0") + .file("src/lib.rs", FUTURE_EXAMPLE) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + with_updates = "1" + big_update = "1" + without_updates = "1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile").run(); + + Package::new("with_updates", "1.0.1") + .file("src/lib.rs", "") + .publish(); + Package::new("with_updates", "1.0.2") + .file("src/lib.rs", "") + .publish(); + Package::new("with_updates", "3.0.1") + .file("src/lib.rs", "") + .publish(); + Package::new("big_update", "2.0.0") + .file("src/lib.rs", "") + .publish(); + + // This is a hack to force cargo to update the index. Cargo can't do this + // automatically because doing a network update on every build would be a + // bad idea. Under normal circumstances, we'll hope the user has done + // something else along the way to trigger an update (building some other + // project or something). This could use some more consideration of how to + // handle this better (maybe only trigger an update if it hasn't updated + // in a long while?). + p.cargo("update -p without_updates").run(); + + let update_message = "\ +- Some affected dependencies have newer versions available. +You may want to consider updating them to a newer version to see if the issue has been fixed. + +big_update v1.0.0 has the following newer versions available: 2.0.0 +with_updates v1.0.0 has the following newer versions available: 1.0.1, 1.0.2, 3.0.1 +"; + + p.cargo("check --future-incompat-report") + .masquerade_as_nightly_cargo(&["future-incompat-test"]) + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .with_stderr_contains(update_message) + .run(); + + p.cargo("report future-incompatibilities") + .with_stdout_contains(update_message) + .run() +} diff --git a/tests/testsuite/generate_lockfile.rs b/tests/testsuite/generate_lockfile.rs new file mode 100644 index 0000000..74f6e78 --- /dev/null +++ b/tests/testsuite/generate_lockfile.rs @@ -0,0 +1,220 @@ +//! Tests for the `cargo generate-lockfile` command. + +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_manifest, paths, project, ProjectBuilder}; +use std::fs; + +#[cargo_test] +fn adding_and_removing_packages() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile").run(); + + let lock1 = p.read_lockfile(); + + // add a dep + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [dependencies.bar] + path = "bar" + "#, + ); + p.cargo("generate-lockfile").run(); + let lock2 = p.read_lockfile(); + assert_ne!(lock1, lock2); + + // change the dep + p.change_file("bar/Cargo.toml", &basic_manifest("bar", "0.0.2")); + p.cargo("generate-lockfile").run(); + let lock3 = p.read_lockfile(); + assert_ne!(lock1, lock3); + assert_ne!(lock2, lock3); + + // remove the dep + println!("lock4"); + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + "#, + ); + p.cargo("generate-lockfile").run(); + let lock4 = p.read_lockfile(); + assert_eq!(lock1, lock4); +} + +#[cargo_test] +fn no_index_update() { + Package::new("serde", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [dependencies] + serde = "1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("generate-lockfile") + .with_stderr("[UPDATING] `[..]` index") + .run(); + + p.cargo("generate-lockfile -Zno-index-update") + .masquerade_as_nightly_cargo(&["no-index-update"]) + .with_stdout("") + .with_stderr("") + .run(); +} + +#[cargo_test] +fn preserve_metadata() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile").run(); + + let metadata = r#" +[metadata] +bar = "baz" +foo = "bar" +"#; + let lock = p.read_lockfile(); + let data = lock + metadata; + p.change_file("Cargo.lock", &data); + + // Build and make sure the metadata is still there + p.cargo("build").run(); + let lock = p.read_lockfile(); + assert!(lock.contains(metadata.trim()), "{}", lock); + + // Update and make sure the metadata is still there + p.cargo("update").run(); + let lock = p.read_lockfile(); + assert!(lock.contains(metadata.trim()), "{}", lock); +} + +#[cargo_test] +fn preserve_line_endings_issue_2076() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + let lockfile = p.root().join("Cargo.lock"); + p.cargo("generate-lockfile").run(); + assert!(lockfile.is_file()); + p.cargo("generate-lockfile").run(); + + let lock0 = p.read_lockfile(); + + assert!(lock0.starts_with("# This file is automatically @generated by Cargo.\n# It is not intended for manual editing.\n")); + + let lock1 = lock0.replace("\n", "\r\n"); + p.change_file("Cargo.lock", &lock1); + + p.cargo("generate-lockfile").run(); + + let lock2 = p.read_lockfile(); + + assert!(lock2.starts_with("# This file is automatically @generated by Cargo.\r\n# It is not intended for manual editing.\r\n")); + assert_eq!(lock1, lock2); +} + +#[cargo_test] +fn cargo_update_generate_lockfile() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + let lockfile = p.root().join("Cargo.lock"); + assert!(!lockfile.is_file()); + p.cargo("update").with_stdout("").run(); + assert!(lockfile.is_file()); + + fs::remove_file(p.root().join("Cargo.lock")).unwrap(); + + assert!(!lockfile.is_file()); + p.cargo("update").with_stdout("").run(); + assert!(lockfile.is_file()); +} + +#[cargo_test] +fn duplicate_entries_in_lockfile() { + let _a = ProjectBuilder::new(paths::root().join("a")) + .file( + "Cargo.toml", + r#" + [package] + name = "a" + authors = [] + version = "0.0.1" + + [dependencies] + common = {path="common"} + "#, + ) + .file("src/lib.rs", "") + .build(); + + let common_toml = &basic_manifest("common", "0.0.1"); + + let _common_in_a = ProjectBuilder::new(paths::root().join("a/common")) + .file("Cargo.toml", common_toml) + .file("src/lib.rs", "") + .build(); + + let b = ProjectBuilder::new(paths::root().join("b")) + .file( + "Cargo.toml", + r#" + [package] + name = "b" + authors = [] + version = "0.0.1" + + [dependencies] + common = {path="common"} + a = {path="../a"} + "#, + ) + .file("src/lib.rs", "") + .build(); + + let _common_in_b = ProjectBuilder::new(paths::root().join("b/common")) + .file("Cargo.toml", common_toml) + .file("src/lib.rs", "") + .build(); + + // should fail due to a duplicate package `common` in the lock file + b.cargo("build") + .with_status(101) + .with_stderr_contains( + "[..]package collision in the lockfile: packages common [..] and \ + common [..] are different, but only one can be written to \ + lockfile unambiguously", + ) + .run(); +} diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs new file mode 100644 index 0000000..48e3eaa --- /dev/null +++ b/tests/testsuite/git.rs @@ -0,0 +1,3656 @@ +//! Tests for git support. + +use std::fs; +use std::io::prelude::*; +use std::net::{TcpListener, TcpStream}; +use std::path::Path; +use std::str; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::thread; + +use cargo_test_support::paths::{self, CargoPathExt}; +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_lib_manifest, basic_manifest, git, main_file, path2url, project}; +use cargo_test_support::{sleep_ms, t, Project}; + +#[cargo_test] +fn cargo_compile_simple_git_dep() { + let project = project(); + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file( + "src/dep1.rs", + r#" + pub fn hello() -> &'static str { + "hello world" + } + "#, + ) + }); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + + git = '{}' + "#, + git_project.url() + ), + ) + .file( + "src/main.rs", + &main_file(r#""{}", dep1::hello()"#, &["dep1"]), + ) + .build(); + + let git_root = git_project.root(); + + project + .cargo("build") + .with_stderr(&format!( + "[UPDATING] git repository `{}`\n\ + [COMPILING] dep1 v0.5.0 ({}#[..])\n\ + [COMPILING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + path2url(&git_root), + path2url(&git_root), + )) + .run(); + + assert!(project.bin("foo").is_file()); + + project + .process(&project.bin("foo")) + .with_stdout("hello world\n") + .run(); +} + +#[cargo_test] +fn cargo_compile_git_dep_branch() { + let project = project(); + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file( + "src/dep1.rs", + r#" + pub fn hello() -> &'static str { + "hello world" + } + "#, + ) + }); + + // Make a new branch based on the current HEAD commit + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let head = repo.head().unwrap().target().unwrap(); + let head = repo.find_commit(head).unwrap(); + repo.branch("branchy", &head, true).unwrap(); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + + git = '{}' + branch = "branchy" + + "#, + git_project.url() + ), + ) + .file( + "src/main.rs", + &main_file(r#""{}", dep1::hello()"#, &["dep1"]), + ) + .build(); + + let git_root = git_project.root(); + + project + .cargo("build") + .with_stderr(&format!( + "[UPDATING] git repository `{}`\n\ + [COMPILING] dep1 v0.5.0 ({}?branch=branchy#[..])\n\ + [COMPILING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + path2url(&git_root), + path2url(&git_root), + )) + .run(); + + assert!(project.bin("foo").is_file()); + + project + .process(&project.bin("foo")) + .with_stdout("hello world\n") + .run(); +} + +#[cargo_test] +fn cargo_compile_git_dep_tag() { + let project = project(); + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file( + "src/dep1.rs", + r#" + pub fn hello() -> &'static str { + "hello world" + } + "#, + ) + }); + + // Make a tag corresponding to the current HEAD + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let head = repo.head().unwrap().target().unwrap(); + repo.tag( + "v0.1.0", + &repo.find_object(head, None).unwrap(), + &repo.signature().unwrap(), + "make a new tag", + false, + ) + .unwrap(); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + + git = '{}' + tag = "v0.1.0" + "#, + git_project.url() + ), + ) + .file( + "src/main.rs", + &main_file(r#""{}", dep1::hello()"#, &["dep1"]), + ) + .build(); + + let git_root = git_project.root(); + + project + .cargo("build") + .with_stderr(&format!( + "[UPDATING] git repository `{}`\n\ + [COMPILING] dep1 v0.5.0 ({}?tag=v0.1.0#[..])\n\ + [COMPILING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + path2url(&git_root), + path2url(&git_root), + )) + .run(); + + assert!(project.bin("foo").is_file()); + + project + .process(&project.bin("foo")) + .with_stdout("hello world\n") + .run(); + + project.cargo("build").run(); +} + +#[cargo_test] +fn cargo_compile_git_dep_pull_request() { + let project = project(); + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file( + "src/dep1.rs", + r#" + pub fn hello() -> &'static str { + "hello world" + } + "#, + ) + }); + + // Make a reference in GitHub's pull request ref naming convention. + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let oid = repo.refname_to_id("HEAD").unwrap(); + let force = false; + let log_message = "open pull request"; + repo.reference("refs/pull/330/head", oid, force, log_message) + .unwrap(); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + + [dependencies] + dep1 = {{ git = "{}", rev = "refs/pull/330/head" }} + "#, + git_project.url() + ), + ) + .file( + "src/main.rs", + &main_file(r#""{}", dep1::hello()"#, &["dep1"]), + ) + .build(); + + let git_root = git_project.root(); + + project + .cargo("build") + .with_stderr(&format!( + "[UPDATING] git repository `{}`\n\ + [COMPILING] dep1 v0.5.0 ({}?rev=refs/pull/330/head#[..])\n\ + [COMPILING] foo v0.0.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + path2url(&git_root), + path2url(&git_root), + )) + .run(); + + assert!(project.bin("foo").is_file()); +} + +#[cargo_test] +fn cargo_compile_with_nested_paths() { + let git_project = git::new("dep1", |project| { + project + .file( + "Cargo.toml", + r#" + [package] + + name = "dep1" + version = "0.5.0" + authors = ["carlhuda@example.com"] + + [dependencies.dep2] + + version = "0.5.0" + path = "vendor/dep2" + + [lib] + + name = "dep1" + "#, + ) + .file( + "src/dep1.rs", + r#" + extern crate dep2; + + pub fn hello() -> &'static str { + dep2::hello() + } + "#, + ) + .file("vendor/dep2/Cargo.toml", &basic_lib_manifest("dep2")) + .file( + "vendor/dep2/src/dep2.rs", + r#" + pub fn hello() -> &'static str { + "hello world" + } + "#, + ) + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + + version = "0.5.0" + git = '{}' + + [[bin]] + + name = "foo" + "#, + git_project.url() + ), + ) + .file( + "src/foo.rs", + &main_file(r#""{}", dep1::hello()"#, &["dep1"]), + ) + .build(); + + p.cargo("build").run(); + + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("hello world\n").run(); +} + +#[cargo_test] +fn cargo_compile_with_malformed_nested_paths() { + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file( + "src/dep1.rs", + r#" + pub fn hello() -> &'static str { + "hello world" + } + "#, + ) + .file("vendor/dep2/Cargo.toml", "!INVALID!") + .file( + "vendor/dep3/Cargo.toml", + r#" + [package] + name = "dep3" + version = "0.5.0" + [dependencies] + subdep1 = { path = "../require-extra-build-step" } + "#, + ) + .file("vendor/dep3/src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + + version = "0.5.0" + git = '{}' + + [[bin]] + + name = "foo" + "#, + git_project.url() + ), + ) + .file( + "src/foo.rs", + &main_file(r#""{}", dep1::hello()"#, &["dep1"]), + ) + .build(); + + p.cargo("build").run(); + + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("hello world\n").run(); +} + +#[cargo_test] +fn cargo_compile_with_meta_package() { + let git_project = git::new("meta-dep", |project| { + project + .file("dep1/Cargo.toml", &basic_lib_manifest("dep1")) + .file( + "dep1/src/dep1.rs", + r#" + pub fn hello() -> &'static str { + "this is dep1" + } + "#, + ) + .file("dep2/Cargo.toml", &basic_lib_manifest("dep2")) + .file( + "dep2/src/dep2.rs", + r#" + pub fn hello() -> &'static str { + "this is dep2" + } + "#, + ) + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + + version = "0.5.0" + git = '{}' + + [dependencies.dep2] + + version = "0.5.0" + git = '{}' + + [[bin]] + + name = "foo" + "#, + git_project.url(), + git_project.url() + ), + ) + .file( + "src/foo.rs", + &main_file( + r#""{} {}", dep1::hello(), dep2::hello()"#, + &["dep1", "dep2"], + ), + ) + .build(); + + p.cargo("build").run(); + + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")) + .with_stdout("this is dep1 this is dep2\n") + .run(); +} + +#[cargo_test] +fn cargo_compile_with_short_ssh_git() { + let url = "git@github.com:a/dep"; + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep] + + git = "{}" + + [[bin]] + + name = "foo" + "#, + url + ), + ) + .file( + "src/foo.rs", + &main_file(r#""{}", dep1::hello()"#, &["dep1"]), + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stdout("") + .with_stderr(&format!( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + invalid url `{}`: relative URL without a base +", + url + )) + .run(); +} + +#[cargo_test] +fn two_revs_same_deps() { + let bar = git::new("meta-dep", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.0.0")) + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + }); + + let repo = git2::Repository::open(&bar.root()).unwrap(); + let rev1 = repo.revparse_single("HEAD").unwrap().id(); + + // Commit the changes and make sure we trigger a recompile + bar.change_file("src/lib.rs", "pub fn bar() -> i32 { 2 }"); + git::add(&repo); + let rev2 = git::commit(&repo); + + let foo = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies.bar] + git = '{}' + rev = "{}" + + [dependencies.baz] + path = "../baz" + "#, + bar.url(), + rev1 + ), + ) + .file( + "src/main.rs", + r#" + extern crate bar; + extern crate baz; + + fn main() { + assert_eq!(bar::bar(), 1); + assert_eq!(baz::baz(), 2); + } + "#, + ) + .build(); + + let _baz = project() + .at("baz") + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "baz" + version = "0.0.0" + authors = [] + + [dependencies.bar] + git = '{}' + rev = "{}" + "#, + bar.url(), + rev2 + ), + ) + .file( + "src/lib.rs", + r#" + extern crate bar; + pub fn baz() -> i32 { bar::bar() } + "#, + ) + .build(); + + foo.cargo("build -v").run(); + assert!(foo.bin("foo").is_file()); + foo.process(&foo.bin("foo")).run(); +} + +#[cargo_test] +fn recompilation() { + let git_project = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("bar")) + .file("src/bar.rs", "pub fn bar() {}") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + + version = "0.5.0" + git = '{}' + "#, + git_project.url() + ), + ) + .file("src/main.rs", &main_file(r#""{:?}", bar::bar()"#, &["bar"])) + .build(); + + // First time around we should compile both foo and bar + p.cargo("check") + .with_stderr(&format!( + "[UPDATING] git repository `{}`\n\ + [CHECKING] bar v0.5.0 ({}#[..])\n\ + [CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + git_project.url(), + git_project.url(), + )) + .run(); + + // Don't recompile the second time + p.cargo("check").with_stdout("").run(); + + // Modify a file manually, shouldn't trigger a recompile + git_project.change_file("src/bar.rs", r#"pub fn bar() { println!("hello!"); }"#); + + p.cargo("check").with_stdout("").run(); + + p.cargo("update") + .with_stderr(&format!( + "[UPDATING] git repository `{}`", + git_project.url() + )) + .run(); + + p.cargo("check").with_stdout("").run(); + + // Commit the changes and make sure we don't trigger a recompile because the + // lock file says not to change + let repo = git2::Repository::open(&git_project.root()).unwrap(); + git::add(&repo); + git::commit(&repo); + + println!("compile after commit"); + p.cargo("check").with_stdout("").run(); + p.root().move_into_the_past(); + + // Update the dependency and carry on! + p.cargo("update") + .with_stderr(&format!( + "[UPDATING] git repository `{}`\n\ + [UPDATING] bar v0.5.0 ([..]) -> #[..]\n\ + ", + git_project.url() + )) + .run(); + println!("going for the last compile"); + p.cargo("check") + .with_stderr(&format!( + "[CHECKING] bar v0.5.0 ({}#[..])\n\ + [CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + git_project.url(), + )) + .run(); + + // Make sure clean only cleans one dep + p.cargo("clean -p foo").with_stdout("").run(); + p.cargo("check") + .with_stderr( + "[CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]", + ) + .run(); +} + +#[cargo_test] +fn update_with_shared_deps() { + let git_project = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("bar")) + .file("src/bar.rs", "pub fn bar() {}") + }); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + path = "dep1" + [dependencies.dep2] + path = "dep2" + "#, + ) + .file( + "src/main.rs", + r#" + #[allow(unused_extern_crates)] + extern crate dep1; + #[allow(unused_extern_crates)] + extern crate dep2; + fn main() {} + "#, + ) + .file( + "dep1/Cargo.toml", + &format!( + r#" + [package] + name = "dep1" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + version = "0.5.0" + git = '{}' + "#, + git_project.url() + ), + ) + .file("dep1/src/lib.rs", "") + .file( + "dep2/Cargo.toml", + &format!( + r#" + [package] + name = "dep2" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + version = "0.5.0" + git = '{}' + "#, + git_project.url() + ), + ) + .file("dep2/src/lib.rs", "") + .build(); + + // First time around we should compile both foo and bar + p.cargo("check") + .with_stderr(&format!( + "\ +[UPDATING] git repository `{git}` +[CHECKING] bar v0.5.0 ({git}#[..]) +[CHECKING] [..] v0.5.0 ([..]) +[CHECKING] [..] v0.5.0 ([..]) +[CHECKING] foo v0.5.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + git = git_project.url(), + )) + .run(); + + // Modify a file manually, and commit it + git_project.change_file("src/bar.rs", r#"pub fn bar() { println!("hello!"); }"#); + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let old_head = repo.head().unwrap().target().unwrap(); + git::add(&repo); + git::commit(&repo); + + sleep_ms(1000); + + // By default, not transitive updates + println!("dep1 update"); + p.cargo("update -p dep1").with_stdout("").run(); + + // Don't do anything bad on a weird --precise argument + println!("bar bad precise update"); + p.cargo("update -p bar --precise 0.1.2") + .with_status(101) + .with_stderr( + "\ +[ERROR] Unable to update [..] + +Caused by: + precise value for git is not a git revision: 0.1.2 + +Caused by: + unable to parse OID - contains invalid characters; class=Invalid (3) +", + ) + .run(); + + // Specifying a precise rev to the old rev shouldn't actually update + // anything because we already have the rev in the db. + println!("bar precise update"); + p.cargo("update -p bar --precise") + .arg(&old_head.to_string()) + .with_stdout("") + .run(); + + // Updating aggressively should, however, update the repo. + println!("dep1 aggressive update"); + p.cargo("update -p dep1 --aggressive") + .with_stderr(&format!( + "[UPDATING] git repository `{}`\n\ + [UPDATING] bar v0.5.0 ([..]) -> #[..]\n\ + ", + git_project.url() + )) + .run(); + + // Make sure we still only compile one version of the git repo + println!("build"); + p.cargo("check") + .with_stderr(&format!( + "\ +[CHECKING] bar v0.5.0 ({git}#[..]) +[CHECKING] [..] v0.5.0 ([CWD][..]dep[..]) +[CHECKING] [..] v0.5.0 ([CWD][..]dep[..]) +[CHECKING] foo v0.5.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + git = git_project.url(), + )) + .run(); + + // We should be able to update transitive deps + p.cargo("update -p bar") + .with_stderr(&format!( + "[UPDATING] git repository `{}`", + git_project.url() + )) + .run(); +} + +#[cargo_test] +fn dep_with_submodule() { + let project = project(); + let git_project = git::new("dep1", |project| { + project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + }); + let git_project2 = git::new("dep2", |project| project.file("lib.rs", "pub fn dep() {}")); + + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let url = path2url(git_project2.root()).to_string(); + git::add_submodule(&repo, &url, Path::new("src")); + git::commit(&repo); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + + git = '{}' + "#, + git_project.url() + ), + ) + .file( + "src/lib.rs", + "extern crate dep1; pub fn foo() { dep1::dep() }", + ) + .build(); + + project + .cargo("check") + .with_stderr( + "\ +[UPDATING] git repository [..] +[UPDATING] git submodule `file://[..]/dep2` +[CHECKING] dep1 [..] +[CHECKING] foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn dep_with_relative_submodule() { + let foo = project(); + let base = git::new("base", |project| { + project + .file( + "Cargo.toml", + r#" + [package] + name = "base" + version = "0.5.0" + + [dependencies] + deployment.path = "deployment" + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn dep() { + deployment::deployment_func(); + } + "#, + ) + }); + let _deployment = git::new("deployment", |project| { + project + .file("src/lib.rs", "pub fn deployment_func() {}") + .file("Cargo.toml", &basic_lib_manifest("deployment")) + }); + + let base_repo = git2::Repository::open(&base.root()).unwrap(); + git::add_submodule(&base_repo, "../deployment", Path::new("deployment")); + git::commit(&base_repo); + + let project = foo + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies.base] + git = '{}' + "#, + base.url() + ), + ) + .file("src/lib.rs", "pub fn foo() { }") + .build(); + + project + .cargo("check") + .with_stderr( + "\ +[UPDATING] git repository [..] +[UPDATING] git submodule `file://[..]/deployment` +[CHECKING] deployment [..] +[CHECKING] base [..] +[CHECKING] foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn dep_with_bad_submodule() { + let project = project(); + let git_project = git::new("dep1", |project| { + project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + }); + let git_project2 = git::new("dep2", |project| project.file("lib.rs", "pub fn dep() {}")); + + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let url = path2url(git_project2.root()).to_string(); + git::add_submodule(&repo, &url, Path::new("src")); + git::commit(&repo); + + // now amend the first commit on git_project2 to make submodule ref point to not-found + // commit + let repo = git2::Repository::open(&git_project2.root()).unwrap(); + let original_submodule_ref = repo.refname_to_id("refs/heads/master").unwrap(); + let commit = repo.find_commit(original_submodule_ref).unwrap(); + commit + .amend( + Some("refs/heads/master"), + None, + None, + None, + Some("something something"), + None, + ) + .unwrap(); + + let p = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + + git = '{}' + "#, + git_project.url() + ), + ) + .file( + "src/lib.rs", + "extern crate dep1; pub fn foo() { dep1::dep() }", + ) + .build(); + + let expected = format!( + "\ +[UPDATING] git repository [..] +[UPDATING] git submodule `file://[..]/dep2` +[ERROR] failed to get `dep1` as a dependency of package `foo v0.5.0 [..]` + +Caused by: + failed to load source for dependency `dep1` + +Caused by: + Unable to update {} + +Caused by: + failed to update submodule `src` + +Caused by: + object not found - no match for id [..] +", + path2url(git_project.root()) + ); + + p.cargo("check") + .with_stderr(expected) + .with_status(101) + .run(); +} + +#[cargo_test] +fn dep_with_skipped_submodule() { + // Ensure we skip dependency submodules if their update strategy is `none`. + let qux = git::new("qux", |project| { + project.no_manifest().file("README", "skip me") + }); + + let bar = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.0.0")) + .file("src/lib.rs", "") + }); + + // `qux` is a submodule of `bar`, but we don't want to update it. + let repo = git2::Repository::open(&bar.root()).unwrap(); + git::add_submodule(&repo, qux.url().as_str(), Path::new("qux")); + + let mut conf = git2::Config::open(&bar.root().join(".gitmodules")).unwrap(); + conf.set_str("submodule.qux.update", "none").unwrap(); + + git::add(&repo); + git::commit(&repo); + + let foo = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies.bar] + git = "{}" + "#, + bar.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + foo.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `file://[..]/bar` +[SKIPPING] git submodule `file://[..]/qux` [..] +[CHECKING] bar [..] +[CHECKING] foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn ambiguous_published_deps() { + let project = project(); + let git_project = git::new("dep", |project| { + project + .file( + "aaa/Cargo.toml", + &format!( + r#" + [package] + name = "bar" + version = "0.5.0" + publish = true + "# + ), + ) + .file("aaa/src/lib.rs", "") + .file( + "bbb/Cargo.toml", + &format!( + r#" + [package] + name = "bar" + version = "0.5.0" + publish = true + "# + ), + ) + .file("bbb/src/lib.rs", "") + }); + + let p = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + git = '{}' + "#, + git_project.url() + ), + ) + .file("src/main.rs", "fn main() { }") + .build(); + + p.cargo("build").run(); + p.cargo("run") + .with_stderr( + "\ +[WARNING] skipping duplicate package `bar` found at `[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn two_deps_only_update_one() { + let project = project(); + let git1 = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + .file("src/lib.rs", "") + }); + let git2 = git::new("dep2", |project| { + project + .file("Cargo.toml", &basic_manifest("dep2", "0.5.0")) + .file("src/lib.rs", "") + }); + + let p = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + git = '{}' + [dependencies.dep2] + git = '{}' + "#, + git1.url(), + git2.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + fn oid_to_short_sha(oid: git2::Oid) -> String { + oid.to_string()[..8].to_string() + } + fn git_repo_head_sha(p: &Project) -> String { + let repo = git2::Repository::open(p.root()).unwrap(); + let head = repo.head().unwrap().target().unwrap(); + oid_to_short_sha(head) + } + + println!("dep1 head sha: {}", git_repo_head_sha(&git1)); + println!("dep2 head sha: {}", git_repo_head_sha(&git2)); + + p.cargo("check") + .with_stderr( + "[UPDATING] git repository `[..]`\n\ + [UPDATING] git repository `[..]`\n\ + [CHECKING] [..] v0.5.0 ([..])\n\ + [CHECKING] [..] v0.5.0 ([..])\n\ + [CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); + + git1.change_file("src/lib.rs", "pub fn foo() {}"); + let repo = git2::Repository::open(&git1.root()).unwrap(); + git::add(&repo); + let oid = git::commit(&repo); + println!("dep1 head sha: {}", oid_to_short_sha(oid)); + + p.cargo("update -p dep1") + .with_stderr(&format!( + "[UPDATING] git repository `{}`\n\ + [UPDATING] dep1 v0.5.0 ([..]) -> #[..]\n\ + ", + git1.url() + )) + .run(); +} + +#[cargo_test] +fn stale_cached_version() { + let bar = git::new("meta-dep", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.0.0")) + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + }); + + // Update the git database in the cache with the current state of the git + // repo + let foo = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies.bar] + git = '{}' + "#, + bar.url() + ), + ) + .file( + "src/main.rs", + r#" + extern crate bar; + + fn main() { assert_eq!(bar::bar(), 1) } + "#, + ) + .build(); + + foo.cargo("build").run(); + foo.process(&foo.bin("foo")).run(); + + // Update the repo, and simulate someone else updating the lock file and then + // us pulling it down. + bar.change_file("src/lib.rs", "pub fn bar() -> i32 { 1 + 0 }"); + let repo = git2::Repository::open(&bar.root()).unwrap(); + git::add(&repo); + git::commit(&repo); + + sleep_ms(1000); + + let rev = repo.revparse_single("HEAD").unwrap().id(); + + foo.change_file( + "Cargo.lock", + &format!( + r#" + [[package]] + name = "foo" + version = "0.0.0" + dependencies = [ + 'bar 0.0.0 (git+{url}#{hash})' + ] + + [[package]] + name = "bar" + version = "0.0.0" + source = 'git+{url}#{hash}' + "#, + url = bar.url(), + hash = rev + ), + ); + + // Now build! + foo.cargo("build") + .with_stderr(&format!( + "\ +[UPDATING] git repository `{bar}` +[COMPILING] bar v0.0.0 ({bar}#[..]) +[COMPILING] foo v0.0.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + bar = bar.url(), + )) + .run(); + foo.process(&foo.bin("foo")).run(); +} + +#[cargo_test] +fn dep_with_changed_submodule() { + let project = project(); + let git_project = git::new("dep1", |project| { + project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + }); + + let git_project2 = git::new("dep2", |project| { + project.file("lib.rs", "pub fn dep() -> &'static str { \"project2\" }") + }); + + let git_project3 = git::new("dep3", |project| { + project.file("lib.rs", "pub fn dep() -> &'static str { \"project3\" }") + }); + + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let mut sub = git::add_submodule(&repo, &git_project2.url().to_string(), Path::new("src")); + git::commit(&repo); + + let p = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + [dependencies.dep1] + git = '{}' + "#, + git_project.url() + ), + ) + .file( + "src/main.rs", + " + extern crate dep1; + pub fn main() { println!(\"{}\", dep1::dep()) } + ", + ) + .build(); + + println!("first run"); + p.cargo("run") + .with_stderr( + "[UPDATING] git repository `[..]`\n\ + [UPDATING] git submodule `file://[..]/dep2`\n\ + [COMPILING] dep1 v0.5.0 ([..])\n\ + [COMPILING] foo v0.5.0 ([..])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in \ + [..]\n\ + [RUNNING] `target/debug/foo[EXE]`\n", + ) + .with_stdout("project2\n") + .run(); + + git_project.change_file( + ".gitmodules", + &format!( + "[submodule \"src\"]\n\tpath = src\n\turl={}", + git_project3.url() + ), + ); + + // Sync the submodule and reset it to the new remote. + sub.sync().unwrap(); + { + let subrepo = sub.open().unwrap(); + subrepo + .remote_add_fetch("origin", "refs/heads/*:refs/heads/*") + .unwrap(); + subrepo + .remote_set_url("origin", &git_project3.url().to_string()) + .unwrap(); + let mut origin = subrepo.find_remote("origin").unwrap(); + origin.fetch(&Vec::::new(), None, None).unwrap(); + let id = subrepo.refname_to_id("refs/remotes/origin/master").unwrap(); + let obj = subrepo.find_object(id, None).unwrap(); + subrepo.reset(&obj, git2::ResetType::Hard, None).unwrap(); + } + sub.add_to_index(true).unwrap(); + git::add(&repo); + git::commit(&repo); + + sleep_ms(1000); + // Update the dependency and carry on! + println!("update"); + p.cargo("update -v") + .with_stderr("") + .with_stderr(&format!( + "[UPDATING] git repository `{}`\n\ + [UPDATING] git submodule `file://[..]/dep3`\n\ + [UPDATING] dep1 v0.5.0 ([..]) -> #[..]\n\ + ", + git_project.url() + )) + .run(); + + println!("last run"); + p.cargo("run") + .with_stderr( + "[COMPILING] dep1 v0.5.0 ([..])\n\ + [COMPILING] foo v0.5.0 ([..])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in \ + [..]\n\ + [RUNNING] `target/debug/foo[EXE]`\n", + ) + .with_stdout("project3\n") + .run(); +} + +#[cargo_test] +fn dev_deps_with_testing() { + let p2 = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file( + "src/lib.rs", + r#" + pub fn gimme() -> &'static str { "zoidberg" } + "#, + ) + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dev-dependencies.bar] + version = "0.5.0" + git = '{}' + "#, + p2.url() + ), + ) + .file( + "src/main.rs", + r#" + fn main() {} + + #[cfg(test)] + mod tests { + extern crate bar; + #[test] fn foo() { bar::gimme(); } + } + "#, + ) + .build(); + + // Generate a lock file which did not use `bar` to compile, but had to update + // `bar` to generate the lock file + p.cargo("check") + .with_stderr(&format!( + "\ +[UPDATING] git repository `{bar}` +[CHECKING] foo v0.5.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + bar = p2.url() + )) + .run(); + + // Make sure we use the previous resolution of `bar` instead of updating it + // a second time. + p.cargo("test") + .with_stderr( + "\ +[COMPILING] [..] v0.5.0 ([..]) +[COMPILING] [..] v0.5.0 ([..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test tests::foo ... ok") + .run(); +} + +#[cargo_test] +fn git_build_cmd_freshness() { + let foo = git::new("foo", |project| { + project + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = "build.rs" + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + .file(".gitignore", "src/bar.rs") + }); + foo.root().move_into_the_past(); + + sleep_ms(1000); + + foo.cargo("check") + .with_stderr( + "\ +[COMPILING] foo v0.0.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + // Smoke test to make sure it doesn't compile again + println!("first pass"); + foo.cargo("check").with_stdout("").run(); + + // Modify an ignored file and make sure we don't rebuild + println!("second pass"); + foo.change_file("src/bar.rs", ""); + foo.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn git_name_not_always_needed() { + let p2 = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file( + "src/lib.rs", + r#" + pub fn gimme() -> &'static str { "zoidberg" } + "#, + ) + }); + + let repo = git2::Repository::open(&p2.root()).unwrap(); + let mut cfg = repo.config().unwrap(); + let _ = cfg.remove("user.name"); + let _ = cfg.remove("user.email"); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dev-dependencies.bar] + git = '{}' + "#, + p2.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + // Generate a lock file which did not use `bar` to compile, but had to update + // `bar` to generate the lock file + p.cargo("check") + .with_stderr(&format!( + "\ +[UPDATING] git repository `{bar}` +[CHECKING] foo v0.5.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + bar = p2.url() + )) + .run(); +} + +#[cargo_test] +fn git_repo_changing_no_rebuild() { + let bar = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + }); + + // Lock p1 to the first rev in the git repo + let p1 = project() + .at("p1") + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "p1" + version = "0.5.0" + authors = [] + build = 'build.rs' + [dependencies.bar] + git = '{}' + "#, + bar.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .file("build.rs", "fn main() {}") + .build(); + p1.root().move_into_the_past(); + p1.cargo("check") + .with_stderr(&format!( + "\ +[UPDATING] git repository `{bar}` +[COMPILING] [..] +[CHECKING] [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + bar = bar.url() + )) + .run(); + + // Make a commit to lock p2 to a different rev + bar.change_file("src/lib.rs", "pub fn bar() -> i32 { 2 }"); + let repo = git2::Repository::open(&bar.root()).unwrap(); + git::add(&repo); + git::commit(&repo); + + // Lock p2 to the second rev + let p2 = project() + .at("p2") + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "p2" + version = "0.5.0" + authors = [] + [dependencies.bar] + git = '{}' + "#, + bar.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + p2.cargo("check") + .with_stderr(&format!( + "\ +[UPDATING] git repository `{bar}` +[CHECKING] [..] +[CHECKING] [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + bar = bar.url() + )) + .run(); + + // And now for the real test! Make sure that p1 doesn't get rebuilt + // even though the git repo has changed. + p1.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn git_dep_build_cmd() { + let p = git::new("foo", |project| { + project + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + + version = "0.5.0" + path = "bar" + + [[bin]] + + name = "foo" + "#, + ) + .file("src/foo.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + + [lib] + name = "bar" + path = "src/bar.rs" + "#, + ) + .file( + "bar/src/bar.rs.in", + r#" + pub fn gimme() -> i32 { 0 } + "#, + ) + .file( + "bar/build.rs", + r#" + use std::fs; + fn main() { + fs::copy("src/bar.rs.in", "src/bar.rs").unwrap(); + } + "#, + ) + }); + + p.root().join("bar").move_into_the_past(); + + p.cargo("build").run(); + + p.process(&p.bin("foo")).with_stdout("0\n").run(); + + // Touching bar.rs.in should cause the `build` command to run again. + p.change_file("bar/src/bar.rs.in", "pub fn gimme() -> i32 { 1 }"); + + p.cargo("build").run(); + + p.process(&p.bin("foo")).with_stdout("1\n").run(); +} + +#[cargo_test] +fn fetch_downloads() { + let bar = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.bar] + git = '{}' + "#, + bar.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("fetch") + .with_stderr(&format!( + "[UPDATING] git repository `{url}`", + url = bar.url() + )) + .run(); + + p.cargo("fetch").with_stdout("").run(); +} + +#[cargo_test] +fn warnings_in_git_dep() { + let bar = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "fn unused() {}") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.bar] + git = '{}' + "#, + bar.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr(&format!( + "[UPDATING] git repository `{}`\n\ + [CHECKING] bar v0.5.0 ({}#[..])\n\ + [CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + bar.url(), + bar.url(), + )) + .run(); +} + +#[cargo_test] +fn update_ambiguous() { + let bar1 = git::new("bar1", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "") + }); + let bar2 = git::new("bar2", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.6.0")) + .file("src/lib.rs", "") + }); + let baz = git::new("baz", |project| { + project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "baz" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + git = '{}' + "#, + bar2.url() + ), + ) + .file("src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.bar] + git = '{}' + [dependencies.baz] + git = '{}' + "#, + bar1.url(), + baz.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("generate-lockfile").run(); + p.cargo("update -p bar") + .with_status(101) + .with_stderr( + "\ +[ERROR] There are multiple `bar` packages in your project, and the specification `bar` \ +is ambiguous. +Please re-run this command with `-p ` where `` is one of the \ +following: + bar@0.[..].0 + bar@0.[..].0 +", + ) + .run(); +} + +#[cargo_test] +fn update_one_dep_in_repo_with_many_deps() { + let bar = git::new("bar", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.5.0")) + .file("a/src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.bar] + git = '{}' + [dependencies.a] + git = '{}' + "#, + bar.url(), + bar.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("generate-lockfile").run(); + p.cargo("update -p bar") + .with_stderr(&format!("[UPDATING] git repository `{}`", bar.url())) + .run(); +} + +#[cargo_test] +fn switch_deps_does_not_update_transitive() { + let transitive = git::new("transitive", |project| { + project + .file("Cargo.toml", &basic_manifest("transitive", "0.5.0")) + .file("src/lib.rs", "") + }); + let dep1 = git::new("dep1", |project| { + project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "dep" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.transitive] + git = '{}' + "#, + transitive.url() + ), + ) + .file("src/lib.rs", "") + }); + let dep2 = git::new("dep2", |project| { + project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "dep" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.transitive] + git = '{}' + "#, + transitive.url() + ), + ) + .file("src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.dep] + git = '{}' + "#, + dep1.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr(&format!( + "\ +[UPDATING] git repository `{}` +[UPDATING] git repository `{}` +[CHECKING] transitive [..] +[CHECKING] dep [..] +[CHECKING] foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + dep1.url(), + transitive.url() + )) + .run(); + + // Update the dependency to point to the second repository, but this + // shouldn't update the transitive dependency which is the same. + p.change_file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.dep] + git = '{}' + "#, + dep2.url() + ), + ); + + p.cargo("check") + .with_stderr(&format!( + "\ +[UPDATING] git repository `{}` +[CHECKING] dep [..] +[CHECKING] foo [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + dep2.url() + )) + .run(); +} + +#[cargo_test] +fn update_one_source_updates_all_packages_in_that_git_source() { + let dep = git::new("dep", |project| { + project + .file( + "Cargo.toml", + r#" + [package] + name = "dep" + version = "0.5.0" + authors = [] + + [dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.5.0")) + .file("a/src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.dep] + git = '{}' + "#, + dep.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check").run(); + + let repo = git2::Repository::open(&dep.root()).unwrap(); + let rev1 = repo.revparse_single("HEAD").unwrap().id(); + + // Just be sure to change a file + dep.change_file("src/lib.rs", "pub fn bar() -> i32 { 2 }"); + git::add(&repo); + git::commit(&repo); + + p.cargo("update -p dep").run(); + let lockfile = p.read_lockfile(); + assert!( + !lockfile.contains(&rev1.to_string()), + "{} in {}", + rev1, + lockfile + ); +} + +#[cargo_test] +fn switch_sources() { + let a1 = git::new("a1", |project| { + project + .file("Cargo.toml", &basic_manifest("a", "0.5.0")) + .file("src/lib.rs", "") + }); + let a2 = git::new("a2", |project| { + project + .file("Cargo.toml", &basic_manifest("a", "0.5.1")) + .file("src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.b] + path = "b" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "b/Cargo.toml", + &format!( + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + [dependencies.a] + git = '{}' + "#, + a1.url() + ), + ) + .file("b/src/lib.rs", "pub fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `file://[..]a1` +[CHECKING] a v0.5.0 ([..]a1#[..] +[CHECKING] b v0.5.0 ([..]) +[CHECKING] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.change_file( + "b/Cargo.toml", + &format!( + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + [dependencies.a] + git = '{}' + "#, + a2.url() + ), + ); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `file://[..]a2` +[CHECKING] a v0.5.1 ([..]a2#[..] +[CHECKING] b v0.5.0 ([..]) +[CHECKING] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn dont_require_submodules_are_checked_out() { + let p = project().build(); + let git1 = git::new("dep1", |p| { + p.file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .file("a/foo", "") + }); + let git2 = git::new("dep2", |p| p); + + let repo = git2::Repository::open(&git1.root()).unwrap(); + let url = path2url(git2.root()).to_string(); + git::add_submodule(&repo, &url, Path::new("a/submodule")); + git::commit(&repo); + + git2::Repository::init(&p.root()).unwrap(); + let url = path2url(git1.root()).to_string(); + let dst = paths::home().join("foo"); + git2::Repository::clone(&url, &dst).unwrap(); + + git1.cargo("check -v").cwd(&dst).run(); +} + +#[cargo_test] +fn doctest_same_name() { + let a2 = git::new("a2", |p| { + p.file("Cargo.toml", &basic_manifest("a", "0.5.0")) + .file("src/lib.rs", "pub fn a2() {}") + }); + + let a1 = git::new("a1", |p| { + p.file( + "Cargo.toml", + &format!( + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + [dependencies] + a = {{ git = '{}' }} + "#, + a2.url() + ), + ) + .file("src/lib.rs", "extern crate a; pub fn a1() {}") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = {{ git = '{}' }} + "#, + a1.url() + ), + ) + .file( + "src/lib.rs", + r#" + #[macro_use] + extern crate a; + "#, + ) + .build(); + + p.cargo("test -v").run(); +} + +#[cargo_test] +fn lints_are_suppressed() { + let a = git::new("a", |p| { + p.file("Cargo.toml", &basic_manifest("a", "0.5.0")).file( + "src/lib.rs", + " + use std::option; + ", + ) + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = {{ git = '{}' }} + "#, + a.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `[..]` +[CHECKING] a v0.5.0 ([..]) +[CHECKING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn denied_lints_are_allowed() { + let a = git::new("a", |p| { + p.file("Cargo.toml", &basic_manifest("a", "0.5.0")).file( + "src/lib.rs", + " + #![deny(warnings)] + use std::option; + ", + ) + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = {{ git = '{}' }} + "#, + a.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `[..]` +[CHECKING] a v0.5.0 ([..]) +[CHECKING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn add_a_git_dep() { + let git = git::new("git", |p| { + p.file("Cargo.toml", &basic_manifest("git", "0.5.0")) + .file("src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = {{ path = 'a' }} + git = {{ git = '{}' }} + "#, + git.url() + ), + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + assert!(paths::home().join(".cargo/git/CACHEDIR.TAG").is_file()); + + p.change_file( + "a/Cargo.toml", + &format!( + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [dependencies] + git = {{ git = '{}' }} + "#, + git.url() + ), + ); + + p.cargo("check").run(); +} + +#[cargo_test] +fn two_at_rev_instead_of_tag() { + let git = git::new("git", |p| { + p.file("Cargo.toml", &basic_manifest("git1", "0.5.0")) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("git2", "0.5.0")) + .file("a/src/lib.rs", "") + }); + + // Make a tag corresponding to the current HEAD + let repo = git2::Repository::open(&git.root()).unwrap(); + let head = repo.head().unwrap().target().unwrap(); + repo.tag( + "v0.1.0", + &repo.find_object(head, None).unwrap(), + &repo.signature().unwrap(), + "make a new tag", + false, + ) + .unwrap(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + git1 = {{ git = '{0}', rev = 'v0.1.0' }} + git2 = {{ git = '{0}', rev = 'v0.1.0' }} + "#, + git.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile").run(); + p.cargo("check -v").run(); +} + +#[cargo_test] +fn include_overrides_gitignore() { + // Make sure that `package.include` takes precedence over .gitignore. + let p = git::new("foo", |repo| { + repo.file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + include = ["src/lib.rs", "ignored.txt", "Cargo.toml"] + "#, + ) + .file( + ".gitignore", + r#" + /target + Cargo.lock + ignored.txt + "#, + ) + .file("src/lib.rs", "") + .file("ignored.txt", "") + .file("build.rs", "fn main() {}") + }); + + p.cargo("check").run(); + p.change_file("ignored.txt", "Trigger rebuild."); + p.cargo("check -v") + .with_stderr( + "\ +[DIRTY] foo v0.5.0 ([..]): the precalculated components changed +[COMPILING] foo v0.5.0 ([..]) +[RUNNING] `[..]build-script-build[..]` +[RUNNING] `rustc --crate-name foo src/lib.rs [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("package --list --allow-dirty") + .with_stdout( + "\ +Cargo.toml +Cargo.toml.orig +ignored.txt +src/lib.rs +", + ) + .run(); +} + +#[cargo_test] +fn invalid_git_dependency_manifest() { + let project = project(); + let git_project = git::new("dep1", |project| { + project + .file( + "Cargo.toml", + r#" + [package] + + name = "dep1" + version = "0.5.0" + authors = ["carlhuda@example.com"] + categories = ["algorithms"] + categories = ["algorithms"] + + [lib] + + name = "dep1" + "#, + ) + .file( + "src/dep1.rs", + r#" + pub fn hello() -> &'static str { + "hello world" + } + "#, + ) + }); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.dep1] + + git = '{}' + "#, + git_project.url() + ), + ) + .file( + "src/main.rs", + &main_file(r#""{}", dep1::hello()"#, &["dep1"]), + ) + .build(); + + let git_root = git_project.root(); + + project + .cargo("check") + .with_status(101) + .with_stderr(&format!( + "\ +[UPDATING] git repository `{}` +[ERROR] failed to get `dep1` as a dependency of package `foo v0.5.0 ([..])` + +Caused by: + failed to load source for dependency `dep1` + +Caused by: + Unable to update {} + +Caused by: + failed to parse manifest at `[..]` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 8, column 21 + | + 8 | categories = [\"algorithms\"] + | ^ + duplicate key `categories` in table `package` +", + path2url(&git_root), + path2url(&git_root), + )) + .run(); +} + +#[cargo_test] +fn failed_submodule_checkout() { + let project = project(); + let git_project = git::new("dep1", |project| { + project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + }); + + let git_project2 = git::new("dep2", |project| project.file("lib.rs", "")); + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + let done = Arc::new(AtomicBool::new(false)); + let done2 = done.clone(); + + let t = thread::spawn(move || { + while !done2.load(Ordering::SeqCst) { + if let Ok((mut socket, _)) = listener.accept() { + drop(socket.write_all(b"foo\r\n")); + } + } + }); + + let repo = git2::Repository::open(&git_project2.root()).unwrap(); + let url = format!("https://{}:{}/", addr.ip(), addr.port()); + { + let mut s = repo.submodule(&url, Path::new("bar"), false).unwrap(); + let subrepo = s.open().unwrap(); + let mut cfg = subrepo.config().unwrap(); + cfg.set_str("user.email", "foo@bar.com").unwrap(); + cfg.set_str("user.name", "Foo Bar").unwrap(); + git::commit(&subrepo); + s.add_finalize().unwrap(); + } + git::commit(&repo); + drop((repo, url)); + + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let url = path2url(git_project2.root()).to_string(); + git::add_submodule(&repo, &url, Path::new("src")); + git::commit(&repo); + drop(repo); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + dep1 = {{ git = '{}' }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + project + .cargo("check") + .with_status(101) + .with_stderr_contains(" failed to update submodule `src`") + .with_stderr_contains(" failed to update submodule `bar`") + .run(); + project + .cargo("check") + .with_status(101) + .with_stderr_contains(" failed to update submodule `src`") + .with_stderr_contains(" failed to update submodule `bar`") + .run(); + + done.store(true, Ordering::SeqCst); + drop(TcpStream::connect(&addr)); + t.join().unwrap(); +} + +#[cargo_test(requires_git)] +fn use_the_cli() { + let project = project(); + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + .file("src/lib.rs", "") + }); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + dep1 = {{ git = '{}' }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + " + [net] + git-fetch-with-cli = true + ", + ) + .build(); + + let stderr = "\ +[UPDATING] git repository `[..]` +[RUNNING] `git fetch [..]` +From [..] + * [new ref] -> origin/HEAD +[CHECKING] dep1 [..] +[RUNNING] `rustc [..]` +[CHECKING] foo [..] +[RUNNING] `rustc [..]` +[FINISHED] [..] +"; + + project.cargo("check -v").with_stderr(stderr).run(); + assert!(paths::home().join(".cargo/git/CACHEDIR.TAG").is_file()); +} + +#[cargo_test] +fn templatedir_doesnt_cause_problems() { + let git_project2 = git::new("dep2", |project| { + project + .file("Cargo.toml", &basic_manifest("dep2", "0.5.0")) + .file("src/lib.rs", "") + }); + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + .file("src/lib.rs", "") + }); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "fo" + version = "0.5.0" + authors = [] + + [dependencies] + dep1 = {{ git = '{}' }} + "#, + git_project.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + fs::write( + paths::home().join(".gitconfig"), + format!( + r#" + [init] + templatedir = {} + "#, + git_project2 + .url() + .to_file_path() + .unwrap() + .to_str() + .unwrap() + .replace("\\", "/") + ), + ) + .unwrap(); + + p.cargo("check").run(); +} + +#[cargo_test(requires_git)] +fn git_with_cli_force() { + // Supports a force-pushed repo. + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file("src/lib.rs", r#"pub fn f() { println!("one"); }"#) + }); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2018" + + [dependencies] + dep1 = {{ git = "{}" }} + "#, + git_project.url() + ), + ) + .file("src/main.rs", "fn main() { dep1::f(); }") + .file( + ".cargo/config", + " + [net] + git-fetch-with-cli = true + ", + ) + .build(); + p.cargo("build").run(); + p.rename_run("foo", "foo1").with_stdout("one").run(); + + // commit --amend a change that will require a force fetch. + let repo = git2::Repository::open(&git_project.root()).unwrap(); + git_project.change_file("src/lib.rs", r#"pub fn f() { println!("two"); }"#); + git::add(&repo); + let id = repo.refname_to_id("HEAD").unwrap(); + let commit = repo.find_commit(id).unwrap(); + let tree_id = t!(t!(repo.index()).write_tree()); + t!(commit.amend( + Some("HEAD"), + None, + None, + None, + None, + Some(&t!(repo.find_tree(tree_id))) + )); + // Perform the fetch. + p.cargo("update").run(); + p.cargo("build").run(); + p.rename_run("foo", "foo2").with_stdout("two").run(); +} + +#[cargo_test(requires_git)] +fn git_fetch_cli_env_clean() { + // This tests that git-fetch-with-cli works when GIT_DIR environment + // variable is set (for whatever reason). + let git_dep = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + .file("src/lib.rs", "") + }); + + let git_proj = git::new("foo", |project| { + project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + dep1 = {{ git = '{}' }} + "#, + git_dep.url() + ), + ) + .file("src/lib.rs", "pub extern crate dep1;") + .file( + ".cargo/config", + " + [net] + git-fetch-with-cli = true + ", + ) + }); + + // The directory set here isn't too important. Pointing to our own git + // directory causes git to be confused and fail. Can also point to an + // empty directory, or a nonexistent one. + git_proj + .cargo("fetch") + .env("GIT_DIR", git_proj.root().join(".git")) + .run(); +} + +#[cargo_test] +fn dirty_submodule() { + // `cargo package` warns for dirty file in submodule. + let (git_project, repo) = git::new_repo("foo", |project| { + project + .file("Cargo.toml", &basic_manifest("foo", "0.5.0")) + // This is necessary because `git::add` is too eager. + .file(".gitignore", "/target") + }); + let git_project2 = git::new("src", |project| { + project.no_manifest().file("lib.rs", "pub fn f() {}") + }); + + let url = path2url(git_project2.root()).to_string(); + git::add_submodule(&repo, &url, Path::new("src")); + + // Submodule added, but not committed. + git_project + .cargo("package --no-verify") + .with_status(101) + .with_stderr( + "\ +[WARNING] manifest has no [..] +See [..] +[ERROR] 1 files in the working directory contain changes that were not yet committed into git: + +.gitmodules + +to proceed despite [..] +", + ) + .run(); + + git::commit(&repo); + git_project.cargo("package --no-verify").run(); + + // Modify file, check for warning. + git_project.change_file("src/lib.rs", ""); + git_project + .cargo("package --no-verify") + .with_status(101) + .with_stderr( + "\ +[WARNING] manifest has no [..] +See [..] +[ERROR] 1 files in the working directory contain changes that were not yet committed into git: + +src/lib.rs + +to proceed despite [..] +", + ) + .run(); + // Commit the change. + let sub_repo = git2::Repository::open(git_project.root().join("src")).unwrap(); + git::add(&sub_repo); + git::commit(&sub_repo); + git::add(&repo); + git::commit(&repo); + git_project.cargo("package --no-verify").run(); + + // Try with a nested submodule. + let git_project3 = git::new("bar", |project| project.no_manifest().file("mod.rs", "")); + let url = path2url(git_project3.root()).to_string(); + git::add_submodule(&sub_repo, &url, Path::new("bar")); + git_project + .cargo("package --no-verify") + .with_status(101) + .with_stderr( + "\ +[WARNING] manifest has no [..] +See [..] +[ERROR] 1 files in the working directory contain changes that were not yet committed into git: + +src/.gitmodules + +to proceed despite [..] +", + ) + .run(); + + // Commit the submodule addition. + git::commit(&sub_repo); + git::add(&repo); + git::commit(&repo); + git_project.cargo("package --no-verify").run(); + // Modify within nested submodule. + git_project.change_file("src/bar/new_file.rs", "//test"); + git_project + .cargo("package --no-verify") + .with_status(101) + .with_stderr( + "\ +[WARNING] manifest has no [..] +See [..] +[ERROR] 1 files in the working directory contain changes that were not yet committed into git: + +src/bar/new_file.rs + +to proceed despite [..] +", + ) + .run(); + // And commit the change. + let sub_sub_repo = git2::Repository::open(git_project.root().join("src/bar")).unwrap(); + git::add(&sub_sub_repo); + git::commit(&sub_sub_repo); + git::add(&sub_repo); + git::commit(&sub_repo); + git::add(&repo); + git::commit(&repo); + git_project.cargo("package --no-verify").run(); +} + +#[cargo_test] +fn default_not_master() { + let project = project(); + + // Create a repository with a `master` branch, but switch the head to a + // branch called `main` at the same time. + let (git_project, repo) = git::new_repo("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file("src/lib.rs", "pub fn foo() {}") + }); + let head_id = repo.head().unwrap().target().unwrap(); + let head = repo.find_commit(head_id).unwrap(); + repo.branch("main", &head, false).unwrap(); + repo.set_head("refs/heads/main").unwrap(); + + // Then create a commit on the new `main` branch so `master` and `main` + // differ. + git_project.change_file("src/lib.rs", "pub fn bar() {}"); + git::add(&repo); + git::commit(&repo); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + [dependencies] + dep1 = {{ git = '{}' }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "pub fn foo() { dep1::bar() }") + .build(); + + project + .cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `[..]` +[CHECKING] dep1 v0.5.0 ([..]) +[CHECKING] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn historical_lockfile_works() { + let project = project(); + + let (git_project, repo) = git::new_repo("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file("src/lib.rs", "") + }); + let head_id = repo.head().unwrap().target().unwrap(); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies] + dep1 = {{ git = '{}', branch = 'master' }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + project.cargo("check").run(); + project.change_file( + "Cargo.lock", + &format!( + r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "dep1" +version = "0.5.0" +source = "git+{}#{}" + +[[package]] +name = "foo" +version = "0.5.0" +dependencies = [ + "dep1", +] +"#, + git_project.url(), + head_id + ), + ); + project + .cargo("check") + .with_stderr("[FINISHED] [..]\n") + .run(); +} + +#[cargo_test] +fn historical_lockfile_works_with_vendor() { + let project = project(); + + let (git_project, repo) = git::new_repo("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file("src/lib.rs", "") + }); + let head_id = repo.head().unwrap().target().unwrap(); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies] + dep1 = {{ git = '{}', branch = 'master' }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + let output = project.cargo("vendor").exec_with_output().unwrap(); + project.change_file(".cargo/config", str::from_utf8(&output.stdout).unwrap()); + project.change_file( + "Cargo.lock", + &format!( + r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "dep1" +version = "0.5.0" +source = "git+{}#{}" + +[[package]] +name = "foo" +version = "0.5.0" +dependencies = [ + "dep1", +] +"#, + git_project.url(), + head_id + ), + ); + project.cargo("check").run(); +} + +#[cargo_test] +fn two_dep_forms() { + let project = project(); + + let (git_project, _repo) = git::new_repo("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file("src/lib.rs", "") + }); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + [dependencies] + dep1 = {{ git = '{}', branch = 'master' }} + a = {{ path = 'a' }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + &format!( + r#" + [package] + name = "a" + version = "0.5.0" + [dependencies] + dep1 = {{ git = '{}' }} + "#, + git_project.url() + ), + ) + .file("a/src/lib.rs", "") + .build(); + + // This'll download the git repository twice, one with HEAD and once with + // the master branch. Then it'll compile 4 crates, the 2 git deps, then + // the two local deps. + project + .cargo("check") + .with_stderr( + "\ +[UPDATING] [..] +[UPDATING] [..] +[CHECKING] [..] +[CHECKING] [..] +[CHECKING] [..] +[CHECKING] [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn metadata_master_consistency() { + // SourceId consistency in the `cargo metadata` output when `master` is + // explicit or implicit, using new or old Cargo.lock. + let (git_project, git_repo) = git::new_repo("bar", |project| { + project + .file("Cargo.toml", &basic_manifest("bar", "1.0.0")) + .file("src/lib.rs", "") + }); + let bar_hash = git_repo.head().unwrap().target().unwrap().to_string(); + + // Explicit branch="master" with a lock file created before 1.47 (does not contain ?branch=master). + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {{ git = "{}", branch = "master" }} + "#, + git_project.url() + ), + ) + .file( + "Cargo.lock", + &format!( + r#" + [[package]] + name = "bar" + version = "1.0.0" + source = "git+{}#{}" + + [[package]] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar", + ] + "#, + git_project.url(), + bar_hash, + ), + ) + .file("src/lib.rs", "") + .build(); + + let metadata = |bar_source| -> String { + r#" + { + "packages": [ + { + "name": "bar", + "version": "1.0.0", + "id": "bar 1.0.0 (__BAR_SOURCE__#__BAR_HASH__)", + "license": null, + "license_file": null, + "description": null, + "source": "__BAR_SOURCE__#__BAR_HASH__", + "dependencies": [], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + }, + { + "name": "foo", + "version": "0.1.0", + "id": "foo 0.1.0 [..]", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": [ + { + "name": "bar", + "source": "__BAR_SOURCE__", + "req": "*", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": "{...}", + "features": {}, + "manifest_path": "[..]", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + } + ], + "workspace_members": [ + "foo 0.1.0 [..]" + ], + "resolve": { + "nodes": [ + { + "id": "bar 1.0.0 (__BAR_SOURCE__#__BAR_HASH__)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "foo 0.1.0 [..]", + "dependencies": [ + "bar 1.0.0 (__BAR_SOURCE__#__BAR_HASH__)" + ], + "deps": [ + { + "name": "bar", + "pkg": "bar 1.0.0 (__BAR_SOURCE__#__BAR_HASH__)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + } + ], + "root": "foo 0.1.0 [..]" + }, + "target_directory": "[..]", + "version": 1, + "workspace_root": "[..]", + "metadata": null + } + "# + .replace("__BAR_SOURCE__", bar_source) + .replace("__BAR_HASH__", &bar_hash) + }; + + let bar_source = format!("git+{}?branch=master", git_project.url()); + p.cargo("metadata").with_json(&metadata(&bar_source)).run(); + + // Conversely, remove branch="master" from Cargo.toml, but use a new Cargo.lock that has ?branch=master. + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {{ git = "{}" }} + "#, + git_project.url() + ), + ) + .file( + "Cargo.lock", + &format!( + r#" + [[package]] + name = "bar" + version = "1.0.0" + source = "git+{}?branch=master#{}" + + [[package]] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar", + ] + "#, + git_project.url(), + bar_hash + ), + ) + .file("src/lib.rs", "") + .build(); + + // No ?branch=master! + let bar_source = format!("git+{}", git_project.url()); + p.cargo("metadata").with_json(&metadata(&bar_source)).run(); +} + +#[cargo_test] +fn git_with_force_push() { + // Checks that cargo can handle force-pushes to git repos. + // This works by having a git dependency that is updated with an amend + // commit, and tries with various forms (default branch, branch, rev, + // tag). + let main = |text| format!(r#"pub fn f() {{ println!("{}"); }}"#, text); + let (git_project, repo) = git::new_repo("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file("src/lib.rs", &main("one")) + }); + let manifest = |extra| { + format!( + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2018" + + [dependencies] + dep1 = {{ git = "{}"{} }} + "#, + git_project.url(), + extra + ) + }; + let p = project() + .file("Cargo.toml", &manifest("")) + .file("src/main.rs", "fn main() { dep1::f(); }") + .build(); + // Download the original and make sure it is OK. + p.cargo("build").run(); + p.rename_run("foo", "foo1").with_stdout("one").run(); + + let find_head = || t!(t!(repo.head()).peel_to_commit()); + + let amend_commit = |text| { + // commit --amend a change that will require a force fetch. + git_project.change_file("src/lib.rs", &main(text)); + git::add(&repo); + let commit = find_head(); + let tree_id = t!(t!(repo.index()).write_tree()); + t!(commit.amend( + Some("HEAD"), + None, + None, + None, + None, + Some(&t!(repo.find_tree(tree_id))) + )); + }; + + let mut rename_annoyance = 1; + + let mut verify = |text: &str| { + // Perform the fetch. + p.cargo("update").run(); + p.cargo("build").run(); + rename_annoyance += 1; + p.rename_run("foo", &format!("foo{}", rename_annoyance)) + .with_stdout(text) + .run(); + }; + + amend_commit("two"); + verify("two"); + + // Try with a rev. + let head1 = find_head().id().to_string(); + let extra = format!(", rev = \"{}\"", head1); + p.change_file("Cargo.toml", &manifest(&extra)); + verify("two"); + amend_commit("three"); + let head2 = find_head().id().to_string(); + assert_ne!(&head1, &head2); + let extra = format!(", rev = \"{}\"", head2); + p.change_file("Cargo.toml", &manifest(&extra)); + verify("three"); + + // Try with a tag. + git::tag(&repo, "my-tag"); + p.change_file("Cargo.toml", &manifest(", tag = \"my-tag\"")); + verify("three"); + amend_commit("tag-three"); + let head = t!(t!(repo.head()).peel(git2::ObjectType::Commit)); + t!(repo.tag("my-tag", &head, &t!(repo.signature()), "move tag", true)); + verify("tag-three"); + + // Try with a branch. + let br = t!(repo.branch("awesome-stuff", &find_head(), false)); + t!(repo.checkout_tree(&t!(br.get().peel(git2::ObjectType::Tree)), None)); + t!(repo.set_head("refs/heads/awesome-stuff")); + git_project.change_file("src/lib.rs", &main("awesome-three")); + git::add(&repo); + git::commit(&repo); + p.change_file("Cargo.toml", &manifest(", branch = \"awesome-stuff\"")); + verify("awesome-three"); + amend_commit("awesome-four"); + verify("awesome-four"); +} + +#[cargo_test] +fn corrupted_checkout() { + // Test what happens if the checkout is corrupted somehow. + _corrupted_checkout(false); +} + +#[cargo_test] +fn corrupted_checkout_with_cli() { + // Test what happens if the checkout is corrupted somehow with git cli. + _corrupted_checkout(true); +} + +fn _corrupted_checkout(with_cli: bool) { + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + .file("src/lib.rs", "") + }); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep1 = {{ git = "{}" }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fetch").run(); + + let mut paths = t!(glob::glob( + paths::home() + .join(".cargo/git/checkouts/dep1-*/*") + .to_str() + .unwrap() + )); + let path = paths.next().unwrap().unwrap(); + let ok = path.join(".cargo-ok"); + + // Deleting this file simulates an interrupted checkout. + t!(fs::remove_file(&ok)); + + // This should refresh the checkout. + let mut e = p.cargo("fetch"); + if with_cli { + e.env("CARGO_NET_GIT_FETCH_WITH_CLI", "true"); + } + e.run(); + assert!(ok.exists()); +} + +#[cargo_test] +fn cleans_temp_pack_files() { + // Checks that cargo removes temp files left by libgit2 when it is + // interrupted (see clean_repo_temp_files). + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("fetch").run(); + // Simulate what happens when libgit2 is interrupted while indexing a pack file. + let tmp_path = super::git_gc::find_index().join(".git/objects/pack/pack_git2_91ab40da04fdc2e7"); + fs::write(&tmp_path, "test").unwrap(); + let mut perms = fs::metadata(&tmp_path).unwrap().permissions(); + perms.set_readonly(true); + fs::set_permissions(&tmp_path, perms).unwrap(); + + // Trigger an index update. + p.cargo("generate-lockfile").run(); + assert!(!tmp_path.exists()); +} diff --git a/tests/testsuite/git_auth.rs b/tests/testsuite/git_auth.rs new file mode 100644 index 0000000..9f2bece --- /dev/null +++ b/tests/testsuite/git_auth.rs @@ -0,0 +1,398 @@ +//! Tests for git authentication. + +use std::collections::HashSet; +use std::io::prelude::*; +use std::io::BufReader; +use std::net::{SocketAddr, TcpListener}; +use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use std::sync::Arc; +use std::thread::{self, JoinHandle}; + +use cargo_test_support::paths; +use cargo_test_support::{basic_manifest, project}; + +fn setup_failed_auth_test() -> (SocketAddr, JoinHandle<()>, Arc) { + let server = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = server.local_addr().unwrap(); + + fn headers(rdr: &mut dyn BufRead) -> HashSet { + let valid = ["GET", "Authorization", "Accept"]; + rdr.lines() + .map(|s| s.unwrap()) + .take_while(|s| s.len() > 2) + .map(|s| s.trim().to_string()) + .filter(|s| valid.iter().any(|prefix| s.starts_with(*prefix))) + .collect() + } + + let connections = Arc::new(AtomicUsize::new(0)); + let connections2 = connections.clone(); + let t = thread::spawn(move || { + let mut conn = BufReader::new(server.accept().unwrap().0); + let req = headers(&mut conn); + connections2.fetch_add(1, SeqCst); + conn.get_mut() + .write_all( + b"HTTP/1.1 401 Unauthorized\r\n\ + WWW-Authenticate: Basic realm=\"wheee\"\r\n\ + Content-Length: 0\r\n\ + \r\n", + ) + .unwrap(); + assert_eq!( + req, + vec![ + "GET /foo/bar/info/refs?service=git-upload-pack HTTP/1.1", + "Accept: */*", + ] + .into_iter() + .map(|s| s.to_string()) + .collect() + ); + + let req = headers(&mut conn); + connections2.fetch_add(1, SeqCst); + conn.get_mut() + .write_all( + b"HTTP/1.1 401 Unauthorized\r\n\ + WWW-Authenticate: Basic realm=\"wheee\"\r\n\ + \r\n", + ) + .unwrap(); + assert_eq!( + req, + vec![ + "GET /foo/bar/info/refs?service=git-upload-pack HTTP/1.1", + "Authorization: Basic Zm9vOmJhcg==", + "Accept: */*", + ] + .into_iter() + .map(|s| s.to_string()) + .collect() + ); + }); + + let script = project() + .at("script") + .file("Cargo.toml", &basic_manifest("script", "0.1.0")) + .file( + "src/main.rs", + r#" + fn main() { + println!("username=foo"); + println!("password=bar"); + } + "#, + ) + .build(); + + script.cargo("build -v").run(); + let script = script.bin("script"); + + let config = paths::home().join(".gitconfig"); + let mut config = git2::Config::open(&config).unwrap(); + config + .set_str( + "credential.helper", + // This is a bash script so replace `\` with `/` for Windows + &script.display().to_string().replace("\\", "/"), + ) + .unwrap(); + (addr, t, connections) +} + +// Tests that HTTP auth is offered from `credential.helper`. +#[cargo_test] +fn http_auth_offered() { + let (addr, t, connections) = setup_failed_auth_test(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + git = "http://127.0.0.1:{}/foo/bar" + "#, + addr.port() + ), + ) + .file("src/main.rs", "") + .file( + ".cargo/config", + "[net] + retry = 0 + ", + ) + .build(); + + // This is a "contains" check because the last error differs by platform, + // may span multiple lines, and isn't relevant to this test. + p.cargo("check") + .with_status(101) + .with_stderr_contains(&format!( + "\ +[UPDATING] git repository `http://{addr}/foo/bar` +[ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 [..]` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update http://{addr}/foo/bar + +Caused by: + failed to clone into: [..] + +Caused by: + failed to authenticate when downloading repository + + * attempted to find username/password via `credential.helper`, but [..] + + if the git CLI succeeds then `net.git-fetch-with-cli` may help here + https://[..] + +Caused by: +", + addr = addr + )) + .run(); + + assert_eq!(connections.load(SeqCst), 2); + t.join().ok().unwrap(); +} + +// Boy, sure would be nice to have a TLS implementation in rust! +#[cargo_test] +fn https_something_happens() { + let server = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = server.local_addr().unwrap(); + let t = thread::spawn(move || { + let mut conn = server.accept().unwrap().0; + drop(conn.write(b"1234")); + drop(conn.shutdown(std::net::Shutdown::Write)); + drop(conn.read(&mut [0; 16])); + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + git = "https://127.0.0.1:{}/foo/bar" + "#, + addr.port() + ), + ) + .file("src/main.rs", "") + .file( + ".cargo/config", + "[net] + retry = 0 + ", + ) + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr_contains(&format!( + "[UPDATING] git repository `https://{addr}/foo/bar`", + addr = addr + )) + .with_stderr_contains(&format!( + "\ +Caused by: + {errmsg} +", + errmsg = if cfg!(windows) { + "[..]failed to send request: [..]" + } else if cfg!(target_os = "macos") { + // macOS is difficult to tests as some builds may use Security.framework, + // while others may use OpenSSL. In that case, let's just not verify the error + // message here. + "[..]" + } else { + "[..]SSL error: [..]" + } + )) + .run(); + + t.join().ok().unwrap(); +} + +// It would sure be nice to have an SSH implementation in Rust! +#[cargo_test] +fn ssh_something_happens() { + let server = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = server.local_addr().unwrap(); + let t = thread::spawn(move || { + drop(server.accept().unwrap()); + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + git = "ssh://127.0.0.1:{}/foo/bar" + "#, + addr.port() + ), + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr_contains(&format!( + "[UPDATING] git repository `ssh://{addr}/foo/bar`", + addr = addr + )) + .with_stderr_contains( + "\ +Caused by: + [..]failed to start SSH session: Failed getting banner[..] +", + ) + .run(); + t.join().ok().unwrap(); +} + +#[cargo_test] +fn net_err_suggests_fetch_with_cli() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies] + foo = { git = "ssh://needs-proxy.invalid/git" } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr( + "\ +[UPDATING] git repository `ssh://needs-proxy.invalid/git` +warning: spurious network error[..] +warning: spurious network error[..] +[ERROR] failed to get `foo` as a dependency of package `foo v0.0.0 [..]` + +Caused by: + failed to load source for dependency `foo` + +Caused by: + Unable to update ssh://needs-proxy.invalid/git + +Caused by: + failed to clone into: [..] + +Caused by: + network failure seems to have happened + if a proxy or similar is necessary `net.git-fetch-with-cli` may help here + https://[..] + +Caused by: + failed to resolve address for needs-proxy.invalid[..] +", + ) + .run(); + + p.change_file( + ".cargo/config", + " + [net] + git-fetch-with-cli = true + ", + ); + + p.cargo("check -v") + .with_status(101) + .with_stderr_contains("[..]Unable to update[..]") + .with_stderr_does_not_contain("[..]try enabling `git-fetch-with-cli`[..]") + .run(); +} + +#[cargo_test] +fn instead_of_url_printed() { + let (addr, t, _connections) = setup_failed_auth_test(); + let config = paths::home().join(".gitconfig"); + let mut config = git2::Config::open(&config).unwrap(); + config + .set_str( + &format!("url.http://{}/.insteadOf", addr), + "https://foo.bar/", + ) + .unwrap(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + git = "https://foo.bar/foo/bar" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr(&format!( + "\ +[UPDATING] git repository `https://foo.bar/foo/bar` +[ERROR] failed to get `bar` as a dependency of package `foo [..]` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update https://foo.bar/foo/bar + +Caused by: + failed to clone into: [..] + +Caused by: + failed to authenticate when downloading repository: http://{addr}/foo/bar + + * attempted to find username/password via `credential.helper`, but maybe the found credentials were incorrect + + if the git CLI succeeds then `net.git-fetch-with-cli` may help here + https://[..] + +Caused by: + [..] +", + addr = addr + )) + .run(); + + t.join().ok().unwrap(); +} diff --git a/tests/testsuite/git_gc.rs b/tests/testsuite/git_gc.rs new file mode 100644 index 0000000..4a8228f --- /dev/null +++ b/tests/testsuite/git_gc.rs @@ -0,0 +1,111 @@ +//! Tests for git garbage collection. + +use std::env; +use std::ffi::OsStr; +use std::path::PathBuf; + +use cargo_test_support::git; +use cargo_test_support::paths; +use cargo_test_support::project; +use cargo_test_support::registry::Package; + +use url::Url; + +pub fn find_index() -> PathBuf { + let dir = paths::home().join(".cargo/registry/index"); + dir.read_dir().unwrap().next().unwrap().unwrap().path() +} + +fn run_test(path_env: Option<&OsStr>) { + const N: usize = 50; + + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .build(); + Package::new("bar", "0.1.0").publish(); + + foo.cargo("check").run(); + + let index = find_index(); + let path = paths::home().join("tmp"); + let url = Url::from_file_path(&path).unwrap().to_string(); + let repo = git2::Repository::init(&path).unwrap(); + let index = git2::Repository::open(&index).unwrap(); + let mut cfg = repo.config().unwrap(); + cfg.set_str("user.email", "foo@bar.com").unwrap(); + cfg.set_str("user.name", "Foo Bar").unwrap(); + let mut cfg = index.config().unwrap(); + cfg.set_str("user.email", "foo@bar.com").unwrap(); + cfg.set_str("user.name", "Foo Bar").unwrap(); + + for _ in 0..N { + git::commit(&repo); + index + .remote_anonymous(&url) + .unwrap() + .fetch(&["refs/heads/master:refs/remotes/foo/master"], None, None) + .unwrap(); + } + drop((repo, index)); + Package::new("bar", "0.1.1").publish(); + + let before = find_index() + .join(".git/objects/pack") + .read_dir() + .unwrap() + .count(); + assert!(before > N); + + let mut cmd = foo.cargo("update"); + cmd.env("__CARGO_PACKFILE_LIMIT", "10"); + if let Some(path) = path_env { + cmd.env("PATH", path); + } + cmd.env("CARGO_LOG", "trace"); + cmd.run(); + let after = find_index() + .join(".git/objects/pack") + .read_dir() + .unwrap() + .count(); + assert!( + after < before, + "packfiles before: {}\n\ + packfiles after: {}", + before, + after + ); +} + +#[cargo_test(requires_git)] +fn use_git_gc() { + run_test(None); +} + +#[cargo_test] +fn avoid_using_git() { + let path = env::var_os("PATH").unwrap_or_default(); + let mut paths = env::split_paths(&path).collect::>(); + let idx = paths + .iter() + .position(|p| p.join("git").exists() || p.join("git.exe").exists()); + match idx { + Some(i) => { + paths.remove(i); + } + None => return, + } + run_test(Some(&env::join_paths(&paths).unwrap())); +} diff --git a/tests/testsuite/glob_targets.rs b/tests/testsuite/glob_targets.rs new file mode 100644 index 0000000..8021dff --- /dev/null +++ b/tests/testsuite/glob_targets.rs @@ -0,0 +1,539 @@ +//! Tests for target filter flags with glob patterns. + +use cargo_test_support::{project, Project}; + +#[cargo_test] +fn build_example() { + full_project() + .cargo("build -v --example 'ex*1'") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name example1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_bin() { + full_project() + .cargo("build -v --bin 'bi*1'") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name bin1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_bench() { + full_project() + .cargo("build -v --bench 'be*1'") + .with_stderr_contains("[RUNNING] `rustc --crate-name bench1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin2 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]`") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_test() { + full_project() + .cargo("build -v --test 'te*1'") + .with_stderr_contains("[RUNNING] `rustc --crate-name test1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin2 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]`") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn check_example() { + full_project() + .cargo("check -v --example 'ex*1'") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name example1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn check_bin() { + full_project() + .cargo("check -v --bin 'bi*1'") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name bin1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn check_bench() { + full_project() + .cargo("check -v --bench 'be*1'") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name bench1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn check_test() { + full_project() + .cargo("check -v --test 'te*1'") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name test1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn doc_bin() { + full_project() + .cargo("doc -v --bin 'bi*1'") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[RUNNING] `rustdoc --crate-type bin --crate-name bin1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn fix_example() { + full_project() + .cargo("fix -v --example 'ex*1' --allow-no-vcs") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[RUNNING] `[..] rustc --crate-name example1 [..]` +[FIXING] examples/example1.rs +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn fix_bin() { + full_project() + .cargo("fix -v --bin 'bi*1' --allow-no-vcs") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[RUNNING] `[..] rustc --crate-name bin1 [..]` +[FIXING] src/bin/bin1.rs +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn fix_bench() { + full_project() + .cargo("fix -v --bench 'be*1' --allow-no-vcs") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[RUNNING] `[..] rustc --crate-name bench1 [..]` +[FIXING] benches/bench1.rs +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn fix_test() { + full_project() + .cargo("fix -v --test 'te*1' --allow-no-vcs") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([CWD]) +[RUNNING] `[..] rustc --crate-name test1 [..]` +[FIXING] tests/test1.rs +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn run_example_and_bin() { + let p = full_project(); + p.cargo("run -v --bin 'bi*1'") + .with_status(101) + .with_stderr("[ERROR] `cargo run` does not support glob patterns on target selection") + .run(); + + p.cargo("run -v --example 'ex*1'") + .with_status(101) + .with_stderr("[ERROR] `cargo run` does not support glob patterns on target selection") + .run(); +} + +#[cargo_test] +fn test_example() { + full_project() + .cargo("test -v --example 'ex*1'") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name example1 [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..]example1[..] +", + ) + .run(); +} + +#[cargo_test] +fn test_bin() { + full_project() + .cargo("test -v --bin 'bi*1'") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name bin1 [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..]bin1[..] +", + ) + .run(); +} + +#[cargo_test] +fn test_bench() { + full_project() + .cargo("test -v --bench 'be*1'") + .with_stderr_contains("[RUNNING] `rustc --crate-name bench1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin2 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]`") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..]bench1[..] +", + ) + .run(); +} + +#[cargo_test] +fn test_test() { + full_project() + .cargo("test -v --test 'te*1'") + .with_stderr_contains("[RUNNING] `rustc --crate-name test1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin2 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]`") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..]test1[..] +", + ) + .run(); +} + +#[cargo_test] +fn bench_example() { + full_project() + .cargo("bench -v --example 'ex*1'") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name example1 [..]` +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] `[..]example1[..] --bench` +", + ) + .run(); +} + +#[cargo_test] +fn bench_bin() { + full_project() + .cargo("bench -v --bin 'bi*1'") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name bin1 [..]` +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] `[..]bin1[..] --bench` +", + ) + .run(); +} + +#[cargo_test] +fn bench_bench() { + full_project() + .cargo("bench -v --bench 'be*1'") + .with_stderr_contains("[RUNNING] `rustc --crate-name bench1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin2 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]`") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] `[..]bench1[..] --bench` +", + ) + .run(); +} + +#[cargo_test] +fn bench_test() { + full_project() + .cargo("bench -v --test 'te*1'") + .with_stderr_contains("[RUNNING] `rustc --crate-name test1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin2 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]`") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] `[..]test1[..] --bench` +", + ) + .run(); +} + +#[cargo_test] +fn install_example() { + full_project() + .cargo("install --path . --example 'ex*1'") + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/home/.cargo/bin/example1[EXE] +[INSTALLED] package `foo v0.0.1 ([CWD])` (executable `example1[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); +} + +#[cargo_test] +fn install_bin() { + full_project() + .cargo("install --path . --bin 'bi*1'") + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/home/.cargo/bin/bin1[EXE] +[INSTALLED] package `foo v0.0.1 ([CWD])` (executable `bin1[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustdoc_example() { + full_project() + .cargo("rustdoc -v --example 'ex*1'") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[RUNNING] `rustdoc --crate-type bin --crate-name example1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustdoc_bin() { + full_project() + .cargo("rustdoc -v --bin 'bi*1'") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[RUNNING] `rustdoc --crate-type bin --crate-name bin1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustdoc_bench() { + full_project() + .cargo("rustdoc -v --bench 'be*1'") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[RUNNING] `rustdoc --crate-type bin --crate-name bench1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustdoc_test() { + full_project() + .cargo("rustdoc -v --test 'te*1'") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[RUNNING] `rustdoc --crate-type bin --crate-name test1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustc_example() { + full_project() + .cargo("rustc -v --example 'ex*1'") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name example1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustc_bin() { + full_project() + .cargo("rustc -v --bin 'bi*1'") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name bin1 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustc_bench() { + full_project() + .cargo("rustc -v --bench 'be*1'") + .with_stderr_contains("[RUNNING] `rustc --crate-name bench1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin2 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]`") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustc_test() { + full_project() + .cargo("rustc -v --test 'te*1'") + .with_stderr_contains("[RUNNING] `rustc --crate-name test1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin2 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name bin1 [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]`") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[RUNNING] `rustc --crate-name [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +fn full_project() -> Project { + project() + .file("examples/example1.rs", "fn main() { }") + .file("examples/example2.rs", "fn main() { }") + .file("benches/bench1.rs", "") + .file("benches/bench2.rs", "") + .file("tests/test1.rs", "") + .file("tests/test2.rs", "") + .file("src/main.rs", "fn main() { }") + .file("src/bin/bin1.rs", "fn main() { }") + .file("src/bin/bin2.rs", "fn main() { }") + .build() +} diff --git a/tests/testsuite/help.rs b/tests/testsuite/help.rs new file mode 100644 index 0000000..fdb527e --- /dev/null +++ b/tests/testsuite/help.rs @@ -0,0 +1,219 @@ +//! Tests for cargo's help output. + +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_manifest, cargo_exe, cargo_process, paths, process, project}; +use std::fs; +use std::path::Path; +use std::str::from_utf8; + +#[cargo_test] +fn help() { + cargo_process("").run(); + cargo_process("help").run(); + cargo_process("-h").run(); + cargo_process("help build").run(); + cargo_process("build -h").run(); + cargo_process("help help").run(); + // Ensure that help output goes to stdout, not stderr. + cargo_process("search --help").with_stderr("").run(); + cargo_process("search --help") + .with_stdout_contains("[..] --frozen [..]") + .run(); +} + +#[cargo_test] +fn help_external_subcommand() { + // Check that `help external-subcommand` forwards the --help flag to the + // given subcommand. + Package::new("cargo-fake-help", "1.0.0") + .file( + "src/main.rs", + r#" + fn main() { + if ::std::env::args().nth(2) == Some(String::from("--help")) { + println!("fancy help output"); + } + } + "#, + ) + .publish(); + cargo_process("install cargo-fake-help").run(); + cargo_process("help fake-help") + .with_stdout("fancy help output\n") + .run(); +} + +#[cargo_test] +fn z_flags_help() { + // Test that the output of `cargo -Z help` shows a different help screen with + // all the `-Z` flags. + cargo_process("-Z help") + .with_stdout_contains( + " -Z allow-features[..]-- Allow *only* the listed unstable features", + ) + .run(); +} + +fn help_with_man(display_command: &str) { + // Build a "man" process that just echoes the contents. + let p = project() + .at(display_command) + .file("Cargo.toml", &basic_manifest(display_command, "1.0.0")) + .file( + "src/main.rs", + &r#" + fn main() { + eprintln!("custom __COMMAND__"); + let path = std::env::args().skip(1).next().unwrap(); + let mut f = std::fs::File::open(path).unwrap(); + std::io::copy(&mut f, &mut std::io::stdout()).unwrap(); + } + "# + .replace("__COMMAND__", display_command), + ) + .build(); + p.cargo("build").run(); + + help_with_man_and_path(display_command, "build", "build", &p.target_debug_dir()); +} + +fn help_with_man_and_path( + display_command: &str, + subcommand: &str, + actual_subcommand: &str, + path: &Path, +) { + let contents = if display_command == "man" { + fs::read_to_string(format!("src/etc/man/cargo-{}.1", actual_subcommand)).unwrap() + } else { + fs::read_to_string(format!( + "src/doc/man/generated_txt/cargo-{}.txt", + actual_subcommand + )) + .unwrap() + }; + + let output = process(&cargo_exe()) + .arg("help") + .arg(subcommand) + .env("PATH", path) + .exec_with_output() + .unwrap(); + assert!(output.status.success()); + let stderr = from_utf8(&output.stderr).unwrap(); + if display_command.is_empty() { + assert_eq!(stderr, ""); + } else { + assert_eq!(stderr, format!("custom {}\n", display_command)); + } + let stdout = from_utf8(&output.stdout).unwrap(); + assert_eq!(stdout, contents); +} + +fn help_with_stdout_and_path(subcommand: &str, path: &Path) -> String { + let output = process(&cargo_exe()) + .arg("help") + .arg(subcommand) + .env("PATH", path) + .exec_with_output() + .unwrap(); + assert!(output.status.success()); + let stderr = from_utf8(&output.stderr).unwrap(); + assert_eq!(stderr, ""); + let stdout = from_utf8(&output.stdout).unwrap(); + stdout.to_string() +} + +#[cargo_test] +fn help_man() { + // Checks that `help command` displays the man page using the given command. + help_with_man("man"); + help_with_man("less"); + help_with_man("more"); + + // Check with no commands in PATH. + help_with_man_and_path("", "build", "build", Path::new("")); +} + +#[cargo_test] +fn help_alias() { + // Check that `help some_alias` will resolve. + help_with_man_and_path("", "b", "build", Path::new("")); + + let config = paths::root().join(".cargo/config"); + fs::create_dir_all(config.parent().unwrap()).unwrap(); + fs::write( + config, + r#" + [alias] + empty-alias = "" + simple-alias = "build" + complex-alias = ["build", "--release"] + "#, + ) + .unwrap(); + + // The `empty-alias` returns an error. + cargo_process("help empty-alias") + .env("PATH", Path::new("")) + .with_stderr_contains("[..]The subcommand 'empty-alias' wasn't recognized[..]") + .run_expect_error(); + + // Because `simple-alias` aliases a subcommand with no arguments, help shows the manpage. + help_with_man_and_path("", "simple-alias", "build", Path::new("")); + + // Help for `complex-alias` displays the full alias command. + let out = help_with_stdout_and_path("complex-alias", Path::new("")); + assert_eq!(out, "`complex-alias` is aliased to `build --release`\n"); +} + +#[cargo_test] +fn alias_z_flag_help() { + cargo_process("build -Z help") + .with_stdout_contains( + " -Z allow-features[..]-- Allow *only* the listed unstable features", + ) + .run(); + + cargo_process("run -Z help") + .with_stdout_contains( + " -Z allow-features[..]-- Allow *only* the listed unstable features", + ) + .run(); + + cargo_process("check -Z help") + .with_stdout_contains( + " -Z allow-features[..]-- Allow *only* the listed unstable features", + ) + .run(); + + cargo_process("test -Z help") + .with_stdout_contains( + " -Z allow-features[..]-- Allow *only* the listed unstable features", + ) + .run(); + + cargo_process("b -Z help") + .with_stdout_contains( + " -Z allow-features[..]-- Allow *only* the listed unstable features", + ) + .run(); + + cargo_process("r -Z help") + .with_stdout_contains( + " -Z allow-features[..]-- Allow *only* the listed unstable features", + ) + .run(); + + cargo_process("c -Z help") + .with_stdout_contains( + " -Z allow-features[..]-- Allow *only* the listed unstable features", + ) + .run(); + + cargo_process("t -Z help") + .with_stdout_contains( + " -Z allow-features[..]-- Allow *only* the listed unstable features", + ) + .run(); +} diff --git a/tests/testsuite/https.rs b/tests/testsuite/https.rs new file mode 100644 index 0000000..c7aec91 --- /dev/null +++ b/tests/testsuite/https.rs @@ -0,0 +1,152 @@ +//! Network tests for https transport. +//! +//! Note that these tests will generally require setting CARGO_CONTAINER_TESTS +//! or CARGO_PUBLIC_NETWORK_TESTS. + +use cargo_test_support::containers::Container; +use cargo_test_support::project; + +#[cargo_test(container_test)] +fn self_signed_should_fail() { + // Cargo should not allow a connection to a self-signed certificate. + let apache = Container::new("apache").launch(); + let port = apache.port_mappings[&443]; + let url = format!("https://127.0.0.1:{port}/repos/bar.git"); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {{ git = "{url}" }} + "# + ), + ) + .file("src/lib.rs", "") + .build(); + // I think the text here depends on the curl backend. + let err_msg = if cfg!(target_os = "macos") { + "untrusted connection error; class=Ssl (16); code=Certificate (-17)" + } else if cfg!(unix) { + "the SSL certificate is invalid; class=Ssl (16); code=Certificate (-17)" + } else if cfg!(windows) { + "user cancelled certificate check; class=Http (34); code=Certificate (-17)" + } else { + panic!("target not supported"); + }; + p.cargo("fetch") + .with_status(101) + .with_stderr(&format!( + "\ +[UPDATING] git repository `https://127.0.0.1:[..]/repos/bar.git` +error: failed to get `bar` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update https://127.0.0.1:[..]/repos/bar.git + +Caused by: + failed to clone into: [ROOT]/home/.cargo/git/db/bar-[..] + +Caused by: + network failure seems to have happened + if a proxy or similar is necessary `net.git-fetch-with-cli` may help here + https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli + +Caused by: + {err_msg} +" + )) + .run(); +} + +#[cargo_test(container_test)] +fn self_signed_with_cacert() { + // When using cainfo, that should allow a connection to a self-signed cert. + + if cfg!(target_os = "macos") { + // This test only seems to work with the + // curl-sys/force-system-lib-on-osx feature enabled. For some reason + // SecureTransport doesn't seem to like the self-signed certificate. + // It works if the certificate is manually approved via Keychain + // Access. The system libcurl is built with a LibreSSL fallback which + // is used when CAINFO is set, which seems to work correctly. This + // could use some more investigation. The official Rust binaries use + // curl-sys/force-system-lib-on-osx so it is mostly an issue for local + // testing. + // + // The error is: + // [60] SSL peer certificate or SSH remote key was not OK (SSL: + // certificate verification failed (result: 5)); class=Net (12) + let curl_v = curl::Version::get(); + if curl_v.vendored() { + eprintln!( + "vendored curl not supported on macOS, \ + set curl-sys/force-system-lib-on-osx to enable" + ); + return; + } + } + + let apache = Container::new("apache").launch(); + let port = apache.port_mappings[&443]; + let url = format!("https://127.0.0.1:{port}/repos/bar.git"); + let server_crt = apache.read_file("/usr/local/apache2/conf/server.crt"); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {{ git = "{url}" }} + "# + ), + ) + .file("src/lib.rs", "") + .file( + ".cargo/config.toml", + &format!( + r#" + [http] + cainfo = "server.crt" + "# + ), + ) + .file("server.crt", &server_crt) + .build(); + p.cargo("fetch") + .with_stderr("[UPDATING] git repository `https://127.0.0.1:[..]/repos/bar.git`") + .run(); +} + +#[cargo_test(public_network_test)] +fn github_works() { + // Check that an https connection to github.com works. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = { git = "https://github.com/rust-lang/bitflags.git", tag="1.3.2" } + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("fetch") + .with_stderr("[UPDATING] git repository `https://github.com/rust-lang/bitflags.git`") + .run(); +} diff --git a/tests/testsuite/inheritable_workspace_fields.rs b/tests/testsuite/inheritable_workspace_fields.rs new file mode 100644 index 0000000..1fc4873 --- /dev/null +++ b/tests/testsuite/inheritable_workspace_fields.rs @@ -0,0 +1,1702 @@ +//! Tests for inheriting Cargo.toml fields with field.workspace = true +use cargo_test_support::registry::{Dependency, Package, RegistryBuilder}; +use cargo_test_support::{ + basic_lib_manifest, basic_manifest, git, path2url, paths, project, publish, registry, +}; + +#[cargo_test] +fn permit_additional_workspace_fields() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + [workspace.package] + version = "1.2.3" + authors = ["Rustaceans"] + description = "This is a crate" + documentation = "https://www.rust-lang.org/learn" + readme = "README.md" + homepage = "https://www.rust-lang.org" + repository = "https://github.com/example/example" + license = "MIT" + license-file = "LICENSE" + keywords = ["cli"] + categories = ["development-tools"] + publish = false + edition = "2018" + rust-version = "1.60" + exclude = ["foo.txt"] + include = ["bar.txt", "**/*.rs", "Cargo.toml", "LICENSE", "README.md"] + + [workspace.package.badges] + gitlab = { repository = "https://gitlab.com/rust-lang/rust", branch = "master" } + + [workspace.dependencies] + dep = "0.1" + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = ".." + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + // Should not warn about unused fields. + .with_stderr( + "\ +[CHECKING] bar v0.1.0 ([CWD]/bar) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("check").run(); + let lockfile = p.read_lockfile(); + assert!(!lockfile.contains("dep")); +} + +#[cargo_test] +fn deny_optional_dependencies() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + + [workspace.dependencies] + dep1 = { version = "0.1", optional = true } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = ".." + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]foo/Cargo.toml` + +Caused by: + dep1 is optional, but workspace dependencies cannot be optional +", + ) + .run(); +} + +#[cargo_test] +fn inherit_own_workspace_fields() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + let p = project().build(); + + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + badges.workspace = true + + [package] + name = "foo" + version.workspace = true + authors.workspace = true + description.workspace = true + documentation.workspace = true + homepage.workspace = true + repository.workspace = true + license.workspace = true + keywords.workspace = true + categories.workspace = true + publish.workspace = true + edition.workspace = true + rust-version.workspace = true + exclude.workspace = true + include.workspace = true + + [workspace] + members = [] + [workspace.package] + version = "1.2.3" + authors = ["Rustaceans"] + description = "This is a crate" + documentation = "https://www.rust-lang.org/learn" + homepage = "https://www.rust-lang.org" + repository = "https://github.com/example/example" + license = "MIT" + keywords = ["cli"] + categories = ["development-tools"] + publish = true + edition = "2018" + rust-version = "1.60" + exclude = ["foo.txt"] + include = ["bar.txt", "**/*.rs", "Cargo.toml"] + [workspace.package.badges] + gitlab = { repository = "https://gitlab.com/rust-lang/rust", branch = "master" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("foo.txt", "") // should be ignored when packaging + .file("bar.txt", "") // should be included when packaging + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[WARNING] [..] +[..] +[VERIFYING] foo v1.2.3 [..] +[COMPILING] foo v1.2.3 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] foo v1.2.3 [..] +[UPDATING] [..] +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": ["Rustaceans"], + "badges": { + "gitlab": { "branch": "master", "repository": "https://gitlab.com/rust-lang/rust" } + }, + "categories": ["development-tools"], + "deps": [], + "description": "This is a crate", + "documentation": "https://www.rust-lang.org/learn", + "features": {}, + "homepage": "https://www.rust-lang.org", + "keywords": ["cli"], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": "https://github.com/example/example", + "vers": "1.2.3" + } + "#, + "foo-1.2.3.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "src/main.rs", + ".cargo_vcs_info.json", + "bar.txt", + ], + &[( + "Cargo.toml", + &format!( + r#"{} +[package] +edition = "2018" +rust-version = "1.60" +name = "foo" +version = "1.2.3" +authors = ["Rustaceans"] +exclude = ["foo.txt"] +include = [ + "bar.txt", + "**/*.rs", + "Cargo.toml", +] +publish = true +description = "This is a crate" +homepage = "https://www.rust-lang.org" +documentation = "https://www.rust-lang.org/learn" +keywords = ["cli"] +categories = ["development-tools"] +license = "MIT" +repository = "https://github.com/example/example" + +[badges.gitlab] +branch = "master" +repository = "https://gitlab.com/rust-lang/rust" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ), + )], + ); +} + +#[cargo_test] +fn inherit_own_dependencies() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.2.0" + authors = [] + + [dependencies] + dep.workspace = true + + [build-dependencies] + dep-build.workspace = true + + [dev-dependencies] + dep-dev.workspace = true + + [workspace] + members = [] + + [workspace.dependencies] + dep = "0.1" + dep-build = "0.8" + dep-dev = "0.5.2" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("dep", "0.1.2").publish(); + Package::new("dep-build", "0.8.2").publish(); + Package::new("dep-dev", "0.5.2").publish(); + + p.cargo("check") + // Unordered because the download order is nondeterministic. + .with_stderr_unordered( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] dep v0.1.2 ([..]) +[DOWNLOADED] dep-build v0.8.2 ([..]) +[CHECKING] dep v0.1.2 +[CHECKING] bar v0.2.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("check").run(); + let lockfile = p.read_lockfile(); + assert!(lockfile.contains("dep")); + assert!(lockfile.contains("dep-dev")); + assert!(lockfile.contains("dep-build")); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[WARNING] [..] +[..] +[PACKAGING] bar v0.2.0 [..] +[UPDATING] [..] +[VERIFYING] bar v0.2.0 [..] +[COMPILING] dep v0.1.2 +[COMPILING] bar v0.2.0 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] bar v0.2.0 [..] +[UPDATING] [..] +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [ + { + "default_features": true, + "features": [], + "kind": "normal", + "name": "dep", + "optional": false, + "target": null, + "version_req": "^0.1" + }, + { + "default_features": true, + "features": [], + "kind": "dev", + "name": "dep-dev", + "optional": false, + "target": null, + "version_req": "^0.5.2" + }, + { + "default_features": true, + "features": [], + "kind": "build", + "name": "dep-build", + "optional": false, + "target": null, + "version_req": "^0.8" + } + ], + "description": null, + "documentation": null, + "features": {}, + "homepage": null, + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "name": "bar", + "readme": null, + "readme_file": null, + "repository": null, + "vers": "0.2.0" + } + "#, + "bar-0.2.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"], + &[( + "Cargo.toml", + &format!( + r#"{} +[package] +name = "bar" +version = "0.2.0" +authors = [] + +[dependencies.dep] +version = "0.1" + +[dev-dependencies.dep-dev] +version = "0.5.2" + +[build-dependencies.dep-build] +version = "0.8" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ), + )], + ); +} + +#[cargo_test] +fn inherit_own_detailed_dependencies() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.2.0" + authors = [] + + [dependencies] + dep.workspace = true + + [workspace] + members = [] + + [workspace.dependencies] + dep = { version = "0.1.2", features = ["testing"] } + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("dep", "0.1.2") + .feature("testing", &vec![]) + .publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] dep v0.1.2 ([..]) +[CHECKING] dep v0.1.2 +[CHECKING] bar v0.2.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("check").run(); + let lockfile = p.read_lockfile(); + assert!(lockfile.contains("dep")); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[WARNING] [..] +[..] +[PACKAGING] bar v0.2.0 [..] +[UPDATING] [..] +[VERIFYING] bar v0.2.0 [..] +[COMPILING] dep v0.1.2 +[COMPILING] bar v0.2.0 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] bar v0.2.0 [..] +[UPDATING] [..] +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [ + { + "default_features": true, + "features": ["testing"], + "kind": "normal", + "name": "dep", + "optional": false, + "target": null, + "version_req": "^0.1.2" + } + ], + "description": null, + "documentation": null, + "features": {}, + "homepage": null, + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "name": "bar", + "readme": null, + "readme_file": null, + "repository": null, + "vers": "0.2.0" + } + "#, + "bar-0.2.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"], + &[( + "Cargo.toml", + &format!( + r#"{} +[package] +name = "bar" +version = "0.2.0" +authors = [] + +[dependencies.dep] +version = "0.1.2" +features = ["testing"] +"#, + cargo::core::package::MANIFEST_PREAMBLE + ), + )], + ); +} + +#[cargo_test] +fn inherit_from_own_undefined_field() { + registry::init(); + + let p = project().build(); + + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.2.5" + authors = ["rustaceans"] + description.workspace = true + + [workspace] + members = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + error inheriting `description` from workspace root manifest's `workspace.package.description` + +Caused by: + `workspace.package.description` was not defined +", + ) + .run(); +} + +#[cargo_test] +fn inherited_dependencies_union_features() { + Package::new("dep", "0.1.0") + .feature("fancy", &["fancy_dep"]) + .feature("dancy", &["dancy_dep"]) + .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) + .add_dep(Dependency::new("dancy_dep", "0.6").optional(true)) + .file("src/lib.rs", "") + .publish(); + + Package::new("fancy_dep", "0.2.4").publish(); + Package::new("dancy_dep", "0.6.8").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.2.0" + authors = [] + [dependencies] + dep = { workspace = true, features = ["dancy"] } + + [workspace] + members = [] + [workspace.dependencies] + dep = { version = "0.1", features = ["fancy"] } + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] fancy_dep v0.2.4 ([..]) +[DOWNLOADED] dep v0.1.0 ([..]) +[DOWNLOADED] dancy_dep v0.6.8 ([..]) +[CHECKING] [..] +[CHECKING] [..] +[CHECKING] dep v0.1.0 +[CHECKING] bar v0.2.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + let lockfile = p.read_lockfile(); + assert!(lockfile.contains("dep")); + assert!(lockfile.contains("fancy_dep")); + assert!(lockfile.contains("dancy_dep")); +} + +#[cargo_test] +fn inherit_workspace_fields() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + let p = project().build(); + + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + [workspace.package] + version = "1.2.3" + authors = ["Rustaceans"] + description = "This is a crate" + documentation = "https://www.rust-lang.org/learn" + readme = "README.md" + homepage = "https://www.rust-lang.org" + repository = "https://github.com/example/example" + license = "MIT" + license-file = "LICENSE" + keywords = ["cli"] + categories = ["development-tools"] + publish = true + edition = "2018" + rust-version = "1.60" + exclude = ["foo.txt"] + include = ["bar.txt", "**/*.rs", "Cargo.toml", "LICENSE", "README.md"] + [workspace.package.badges] + gitlab = { repository = "https://gitlab.com/rust-lang/rust", branch = "master" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + badges.workspace = true + [package] + name = "bar" + workspace = ".." + version.workspace = true + authors.workspace = true + description.workspace = true + documentation.workspace = true + readme.workspace = true + homepage.workspace = true + repository.workspace = true + license.workspace = true + license-file.workspace = true + keywords.workspace = true + categories.workspace = true + publish.workspace = true + edition.workspace = true + rust-version.workspace = true + exclude.workspace = true + include.workspace = true + "#, + ) + .file("LICENSE", "license") + .file("README.md", "README.md") + .file("bar/src/main.rs", "fn main() {}") + .file("bar/foo.txt", "") // should be ignored when packaging + .file("bar/bar.txt", "") // should be included when packaging + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .cwd("bar") + .with_stderr( + "\ +[UPDATING] [..] +[WARNING] [..] +[..] +[VERIFYING] bar v1.2.3 [..] +[WARNING] [..] +[..] +[..] +[..] +[COMPILING] bar v1.2.3 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] bar v1.2.3 [..] +[UPDATING] [..] +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": ["Rustaceans"], + "badges": { + "gitlab": { "branch": "master", "repository": "https://gitlab.com/rust-lang/rust" } + }, + "categories": ["development-tools"], + "deps": [], + "description": "This is a crate", + "documentation": "https://www.rust-lang.org/learn", + "features": {}, + "homepage": "https://www.rust-lang.org", + "keywords": ["cli"], + "license": "MIT", + "license_file": "../LICENSE", + "links": null, + "name": "bar", + "readme": "README.md", + "readme_file": "../README.md", + "repository": "https://github.com/example/example", + "vers": "1.2.3" + } + "#, + "bar-1.2.3.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "src/main.rs", + "README.md", + "LICENSE", + ".cargo_vcs_info.json", + "bar.txt", + ], + &[( + "Cargo.toml", + &format!( + r#"{} +[package] +edition = "2018" +rust-version = "1.60" +name = "bar" +version = "1.2.3" +authors = ["Rustaceans"] +exclude = ["foo.txt"] +include = [ + "bar.txt", + "**/*.rs", + "Cargo.toml", + "LICENSE", + "README.md", +] +publish = true +description = "This is a crate" +homepage = "https://www.rust-lang.org" +documentation = "https://www.rust-lang.org/learn" +readme = "README.md" +keywords = ["cli"] +categories = ["development-tools"] +license = "MIT" +license-file = "LICENSE" +repository = "https://github.com/example/example" + +[badges.gitlab] +branch = "master" +repository = "https://gitlab.com/rust-lang/rust" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ), + )], + ); +} + +#[cargo_test] +fn inherit_dependencies() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + [workspace.dependencies] + dep = "0.1" + dep-build = "0.8" + dep-dev = "0.5.2" + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + workspace = ".." + name = "bar" + version = "0.2.0" + authors = [] + [dependencies] + dep.workspace = true + [build-dependencies] + dep-build.workspace = true + [dev-dependencies] + dep-dev.workspace = true + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + Package::new("dep", "0.1.2").publish(); + Package::new("dep-build", "0.8.2").publish(); + Package::new("dep-dev", "0.5.2").publish(); + + p.cargo("check") + // Unordered because the download order is nondeterministic. + .with_stderr_unordered( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] dep v0.1.2 ([..]) +[DOWNLOADED] dep-build v0.8.2 ([..]) +[CHECKING] dep v0.1.2 +[CHECKING] bar v0.2.0 ([CWD]/bar) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("check").run(); + let lockfile = p.read_lockfile(); + assert!(lockfile.contains("dep")); + assert!(lockfile.contains("dep-dev")); + assert!(lockfile.contains("dep-build")); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .cwd("bar") + .with_stderr( + "\ +[UPDATING] [..] +[WARNING] [..] +[..] +[PACKAGING] bar v0.2.0 [..] +[UPDATING] [..] +[VERIFYING] bar v0.2.0 [..] +[COMPILING] dep v0.1.2 +[COMPILING] bar v0.2.0 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] bar v0.2.0 [..] +[UPDATING] [..] +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [ + { + "default_features": true, + "features": [], + "kind": "normal", + "name": "dep", + "optional": false, + "target": null, + "version_req": "^0.1" + }, + { + "default_features": true, + "features": [], + "kind": "dev", + "name": "dep-dev", + "optional": false, + "target": null, + "version_req": "^0.5.2" + }, + { + "default_features": true, + "features": [], + "kind": "build", + "name": "dep-build", + "optional": false, + "target": null, + "version_req": "^0.8" + } + ], + "description": null, + "documentation": null, + "features": {}, + "homepage": null, + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "name": "bar", + "readme": null, + "readme_file": null, + "repository": null, + "vers": "0.2.0" + } + "#, + "bar-0.2.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"], + &[( + "Cargo.toml", + &format!( + r#"{} +[package] +name = "bar" +version = "0.2.0" +authors = [] + +[dependencies.dep] +version = "0.1" + +[dev-dependencies.dep-dev] +version = "0.5.2" + +[build-dependencies.dep-build] +version = "0.8" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ), + )], + ); +} + +#[cargo_test] +fn inherit_target_dependencies() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + [workspace.dependencies] + dep = "0.1" + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + workspace = ".." + name = "bar" + version = "0.2.0" + authors = [] + [target.'cfg(unix)'.dependencies] + dep.workspace = true + [target.'cfg(windows)'.dependencies] + dep.workspace = true + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + Package::new("dep", "0.1.2").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] dep v0.1.2 ([..]) +[CHECKING] dep v0.1.2 +[CHECKING] bar v0.2.0 ([CWD]/bar) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + let lockfile = p.read_lockfile(); + assert!(lockfile.contains("dep")); +} + +#[cargo_test] +fn inherit_dependency_override_optional() { + Package::new("dep", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + [workspace.dependencies] + dep = "0.1.0" + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + workspace = ".." + name = "bar" + version = "0.2.0" + authors = [] + [dependencies] + dep = { workspace = true, optional = true } + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[CHECKING] bar v0.2.0 ([CWD]/bar) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn inherit_dependency_features() { + Package::new("dep", "0.1.0") + .feature("fancy", &["fancy_dep"]) + .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) + .file("src/lib.rs", "") + .publish(); + + Package::new("fancy_dep", "0.2.4").publish(); + Package::new("dancy_dep", "0.6.8").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.2.0" + authors = [] + [dependencies] + dep = { workspace = true, features = ["fancy"] } + + [workspace] + members = [] + [workspace.dependencies] + dep = "0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] fancy_dep v0.2.4 ([..]) +[DOWNLOADED] dep v0.1.0 ([..]) +[CHECKING] fancy_dep v0.2.4 +[CHECKING] dep v0.1.0 +[CHECKING] bar v0.2.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + let lockfile = p.read_lockfile(); + assert!(lockfile.contains("dep")); + assert!(lockfile.contains("fancy_dep")); +} + +#[cargo_test] +fn inherit_detailed_dependencies() { + let git_project = git::new("detailed", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("detailed")) + .file( + "src/detailed.rs", + r#" + pub fn hello() -> &'static str { + "hello world" + } + "#, + ) + }); + + // Make a new branch based on the current HEAD commit + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let head = repo.head().unwrap().target().unwrap(); + let head = repo.find_commit(head).unwrap(); + repo.branch("branchy", &head, true).unwrap(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [workspace] + members = ["bar"] + [workspace.dependencies] + detailed = {{ git = '{}', branch = "branchy" }} + "#, + git_project.url() + ), + ) + .file( + "bar/Cargo.toml", + r#" + [package] + workspace = ".." + name = "bar" + version = "0.2.0" + authors = [] + [dependencies] + detailed.workspace = true + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + let git_root = git_project.root(); + + p.cargo("check") + .with_stderr(&format!( + "\ +[UPDATING] git repository `{}`\n\ +[CHECKING] detailed v0.5.0 ({}?branch=branchy#[..])\n\ +[CHECKING] bar v0.2.0 ([CWD]/bar)\n\ +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n", + path2url(&git_root), + path2url(&git_root), + )) + .run(); +} + +#[cargo_test] +fn inherit_path_dependencies() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + [workspace.dependencies] + dep = { path = "dep" } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + workspace = ".." + name = "bar" + version = "0.2.0" + authors = [] + [dependencies] + dep.workspace = true + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .file("dep/Cargo.toml", &basic_manifest("dep", "0.9.0")) + .file("dep/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] dep v0.9.0 ([CWD]/dep) +[CHECKING] bar v0.2.0 ([CWD]/bar) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + let lockfile = p.read_lockfile(); + assert!(lockfile.contains("dep")); +} + +#[cargo_test] +fn error_workspace_false() { + registry::init(); + + let p = project().build(); + + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + workspace = ".." + version = "1.2.3" + authors = ["rustaceans"] + description = { workspace = false } + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .cwd("bar") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + `workspace` cannot be false + in `package.description.workspace` +", + ) + .run(); +} + +#[cargo_test] +fn error_workspace_dependency_looked_for_workspace_itself() { + registry::init(); + + let p = project().build(); + + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "1.2.3" + + [dependencies] + dep.workspace = true + + [workspace] + members = [] + + [workspace.dependencies] + dep.workspace = true + + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[WARNING] [CWD]/Cargo.toml: unused manifest key: workspace.dependencies.dep.workspace +[WARNING] [CWD]/Cargo.toml: dependency (dep) specified without providing a local path, Git repository, or version to use. This will be considered an error in future versions +[UPDATING] `dummy-registry` index +[ERROR] no matching package named `dep` found +location searched: registry `crates-io` +required by package `bar v1.2.3 ([CWD])` +", + ) + .run(); +} + +#[cargo_test] +fn error_malformed_workspace_root() { + registry::init(); + + let p = project().build(); + + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [workspace] + members = [invalid toml + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + workspace = ".." + version = "1.2.3" + authors = ["rustaceans"] + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .cwd("bar") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + [..] + +Caused by: + [..] + | + 3 | members = [invalid toml + | ^ + invalid array + expected `]` +", + ) + .run(); +} + +#[cargo_test] +fn error_no_root_workspace() { + registry::init(); + + let p = project().build(); + + let _ = git::repo(&paths::root().join("foo")) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + workspace = ".." + version = "1.2.3" + authors = ["rustaceans"] + description.workspace = true + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .cwd("bar") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]/Cargo.toml` + +Caused by: + error inheriting `description` from workspace root manifest's `workspace.package.description` + +Caused by: + root of a workspace inferred but wasn't a root: [..]/Cargo.toml +", + ) + .run(); +} + +#[cargo_test] +fn error_inherit_unspecified_dependency() { + let p = project().build(); + + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + workspace = ".." + version = "1.2.3" + authors = ["rustaceans"] + [dependencies] + foo.workspace = true + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .cwd("bar") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + error inheriting `foo` from workspace root manifest's `workspace.dependencies.foo` + +Caused by: + `workspace.dependencies` was not defined +", + ) + .run(); +} + +#[cargo_test] +fn warn_inherit_def_feat_true_member_def_feat_false() { + Package::new("dep", "0.1.0") + .feature("default", &["fancy_dep"]) + .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) + .file("src/lib.rs", "") + .publish(); + + Package::new("fancy_dep", "0.2.4").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.2.0" + authors = [] + [dependencies] + dep = { workspace = true, default-features = false } + + [workspace] + members = [] + [workspace.dependencies] + dep = { version = "0.1.0", default-features = true } + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] [CWD]/Cargo.toml: `default-features` is ignored for dep, since `default-features` was \ +true for `workspace.dependencies.dep`, this could become a hard error in the future +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] fancy_dep v0.2.4 ([..]) +[DOWNLOADED] dep v0.1.0 ([..]) +[CHECKING] fancy_dep v0.2.4 +[CHECKING] dep v0.1.0 +[CHECKING] bar v0.2.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn warn_inherit_simple_member_def_feat_false() { + Package::new("dep", "0.1.0") + .feature("default", &["fancy_dep"]) + .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) + .file("src/lib.rs", "") + .publish(); + + Package::new("fancy_dep", "0.2.4").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.2.0" + authors = [] + [dependencies] + dep = { workspace = true, default-features = false } + + [workspace] + members = [] + [workspace.dependencies] + dep = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] [CWD]/Cargo.toml: `default-features` is ignored for dep, since `default-features` was \ +not specified for `workspace.dependencies.dep`, this could become a hard error in the future +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] fancy_dep v0.2.4 ([..]) +[DOWNLOADED] dep v0.1.0 ([..]) +[CHECKING] fancy_dep v0.2.4 +[CHECKING] dep v0.1.0 +[CHECKING] bar v0.2.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn inherit_def_feat_false_member_def_feat_true() { + Package::new("dep", "0.1.0") + .feature("default", &["fancy_dep"]) + .add_dep(Dependency::new("fancy_dep", "0.2").optional(true)) + .file("src/lib.rs", "") + .publish(); + + Package::new("fancy_dep", "0.2.4").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.2.0" + authors = [] + [dependencies] + dep = { workspace = true, default-features = true } + + [workspace] + members = [] + [workspace.dependencies] + dep = { version = "0.1.0", default-features = false } + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] fancy_dep v0.2.4 ([..]) +[DOWNLOADED] dep v0.1.0 ([..]) +[CHECKING] fancy_dep v0.2.4 +[CHECKING] dep v0.1.0 +[CHECKING] bar v0.2.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn cannot_inherit_in_patch() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [] + + [workspace.dependencies] + bar = { path = "bar" } + + [package] + name = "foo" + version = "0.2.0" + + [patch.crates-io] + bar.workspace = true + + [dependencies] + bar = "0.1.0" + + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[WARNING] [CWD]/Cargo.toml: unused manifest key: patch.crates-io.bar.workspace +[WARNING] [CWD]/Cargo.toml: dependency (bar) specified without providing a local path, Git repository, or version to use. This will be considered an error in future versions +[UPDATING] `dummy-registry` index +[ERROR] failed to resolve patches for `https://github.com/rust-lang/crates.io-index` + +Caused by: + patch for `bar` in `https://github.com/rust-lang/crates.io-index` points to the same source, but patches must point to different sources +", + ) + .run(); +} + +#[cargo_test] +fn warn_inherit_unused_manifest_key_dep() { + Package::new("dep", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [] + [workspace.dependencies] + dep = { version = "0.1", wxz = "wxz" } + + [package] + name = "bar" + version = "0.2.0" + authors = [] + + [dependencies] + dep = { workspace = true, wxz = "wxz" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] [CWD]/Cargo.toml: unused manifest key: workspace.dependencies.dep.wxz +[WARNING] [CWD]/Cargo.toml: unused manifest key: dependencies.dep.wxz +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] dep v0.1.0 ([..]) +[CHECKING] [..] +[CHECKING] bar v0.2.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn warn_inherit_unused_manifest_key_package() { + Package::new("dep", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + badges = { workspace = true, xyz = "abc"} + + [workspace] + members = [] + [workspace.package] + version = "1.2.3" + authors = ["Rustaceans"] + description = "This is a crate" + documentation = "https://www.rust-lang.org/learn" + homepage = "https://www.rust-lang.org" + repository = "https://github.com/example/example" + license = "MIT" + keywords = ["cli"] + categories = ["development-tools"] + publish = true + edition = "2018" + rust-version = "1.60" + exclude = ["foo.txt"] + include = ["bar.txt", "**/*.rs", "Cargo.toml"] + [workspace.package.badges] + gitlab = { repository = "https://gitlab.com/rust-lang/rust", branch = "master" } + + [package] + name = "bar" + version = { workspace = true, xyz = "abc"} + authors = { workspace = true, xyz = "abc"} + description = { workspace = true, xyz = "abc"} + documentation = { workspace = true, xyz = "abc"} + homepage = { workspace = true, xyz = "abc"} + repository = { workspace = true, xyz = "abc"} + license = { workspace = true, xyz = "abc"} + keywords = { workspace = true, xyz = "abc"} + categories = { workspace = true, xyz = "abc"} + publish = { workspace = true, xyz = "abc"} + edition = { workspace = true, xyz = "abc"} + rust-version = { workspace = true, xyz = "abc"} + exclude = { workspace = true, xyz = "abc"} + include = { workspace = true, xyz = "abc"} + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.authors.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.categories.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.description.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.documentation.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.edition.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.exclude.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.homepage.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.include.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.keywords.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.license.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.publish.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.repository.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.rust-version.xyz +[WARNING] [CWD]/Cargo.toml: unused manifest key: package.version.xyz +[CHECKING] bar v1.2.3 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} diff --git a/tests/testsuite/init/auto_git/in b/tests/testsuite/init/auto_git/in new file mode 120000 index 0000000..1202506 --- /dev/null +++ b/tests/testsuite/init/auto_git/in @@ -0,0 +1 @@ +../empty_dir \ No newline at end of file diff --git a/tests/testsuite/init/auto_git/mod.rs b/tests/testsuite/init/auto_git/mod.rs new file mode 100644 index 0000000..68c2175 --- /dev/null +++ b/tests/testsuite/init/auto_git/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); + assert!(project_root.join(".git").is_dir()); +} diff --git a/tests/testsuite/init/auto_git/out/.gitignore b/tests/testsuite/init/auto_git/out/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/tests/testsuite/init/auto_git/out/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/tests/testsuite/init/auto_git/out/Cargo.toml b/tests/testsuite/init/auto_git/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/auto_git/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/auto_git/out/src/lib.rs b/tests/testsuite/init/auto_git/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/auto_git/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/auto_git/stderr.log b/tests/testsuite/init/auto_git/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/auto_git/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/auto_git/stdout.log b/tests/testsuite/init/auto_git/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/bin_already_exists_explicit/in/src/main.rs b/tests/testsuite/init/bin_already_exists_explicit/in/src/main.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_explicit/in/src/main.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_explicit/mod.rs b/tests/testsuite/init/bin_already_exists_explicit/mod.rs new file mode 100644 index 0000000..326bd21 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_explicit/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --bin --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/bin_already_exists_explicit/out/Cargo.toml b/tests/testsuite/init/bin_already_exists_explicit/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_explicit/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/bin_already_exists_explicit/out/src/main.rs b/tests/testsuite/init/bin_already_exists_explicit/out/src/main.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_explicit/out/src/main.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_explicit/stderr.log b/tests/testsuite/init/bin_already_exists_explicit/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_explicit/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/bin_already_exists_explicit/stdout.log b/tests/testsuite/init/bin_already_exists_explicit/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/bin_already_exists_explicit_nosrc/in/main.rs b/tests/testsuite/init/bin_already_exists_explicit_nosrc/in/main.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_explicit_nosrc/in/main.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_explicit_nosrc/mod.rs b/tests/testsuite/init/bin_already_exists_explicit_nosrc/mod.rs new file mode 100644 index 0000000..1f16fb6 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_explicit_nosrc/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --bin --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join("src").is_dir()); +} diff --git a/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/Cargo.toml b/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/Cargo.toml new file mode 100644 index 0000000..5c6c915 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[[bin]] +name = "case" +path = "main.rs" diff --git a/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/main.rs b/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/main.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/main.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_explicit_nosrc/stderr.log b/tests/testsuite/init/bin_already_exists_explicit_nosrc/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_explicit_nosrc/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/bin_already_exists_explicit_nosrc/stdout.log b/tests/testsuite/init/bin_already_exists_explicit_nosrc/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/bin_already_exists_implicit/in/src/main.rs b/tests/testsuite/init/bin_already_exists_implicit/in/src/main.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit/in/src/main.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_implicit/mod.rs b/tests/testsuite/init/bin_already_exists_implicit/mod.rs new file mode 100644 index 0000000..12349a0 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/bin_already_exists_implicit/out/Cargo.toml b/tests/testsuite/init/bin_already_exists_implicit/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/bin_already_exists_implicit/out/src/main.rs b/tests/testsuite/init/bin_already_exists_implicit/out/src/main.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit/out/src/main.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_implicit/stderr.log b/tests/testsuite/init/bin_already_exists_implicit/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/bin_already_exists_implicit/stdout.log b/tests/testsuite/init/bin_already_exists_implicit/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/bin_already_exists_implicit_namenosrc/in/case.rs b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/in/case.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/in/case.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_implicit_namenosrc/mod.rs b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/mod.rs new file mode 100644 index 0000000..fe65940 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join("src").is_dir()); +} diff --git a/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/Cargo.toml b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/Cargo.toml new file mode 100644 index 0000000..8da5fe7 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[[bin]] +name = "case" +path = "case.rs" diff --git a/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/case.rs b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/case.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/case.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stderr.log b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stdout.log b/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/bin_already_exists_implicit_namesrc/in/src/case.rs b/tests/testsuite/init/bin_already_exists_implicit_namesrc/in/src/case.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_namesrc/in/src/case.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_implicit_namesrc/mod.rs b/tests/testsuite/init/bin_already_exists_implicit_namesrc/mod.rs new file mode 100644 index 0000000..d3e8e66 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_namesrc/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join("src/main.rs").is_file()); +} diff --git a/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/Cargo.toml b/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/Cargo.toml new file mode 100644 index 0000000..dec0aae --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[[bin]] +name = "case" +path = "src/case.rs" diff --git a/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/src/case.rs b/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/src/case.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/src/case.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_implicit_namesrc/stderr.log b/tests/testsuite/init/bin_already_exists_implicit_namesrc/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_namesrc/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/bin_already_exists_implicit_namesrc/stdout.log b/tests/testsuite/init/bin_already_exists_implicit_namesrc/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/bin_already_exists_implicit_nosrc/in/main.rs b/tests/testsuite/init/bin_already_exists_implicit_nosrc/in/main.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_nosrc/in/main.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_implicit_nosrc/mod.rs b/tests/testsuite/init/bin_already_exists_implicit_nosrc/mod.rs new file mode 100644 index 0000000..fe65940 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_nosrc/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join("src").is_dir()); +} diff --git a/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/Cargo.toml b/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/Cargo.toml new file mode 100644 index 0000000..5c6c915 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[[bin]] +name = "case" +path = "main.rs" diff --git a/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/main.rs b/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/main.rs new file mode 100644 index 0000000..65fdcf8 --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/main.rs @@ -0,0 +1,4 @@ +fn main() { + println!("Check that our file is not overwritten") +} + diff --git a/tests/testsuite/init/bin_already_exists_implicit_nosrc/stderr.log b/tests/testsuite/init/bin_already_exists_implicit_nosrc/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/bin_already_exists_implicit_nosrc/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/bin_already_exists_implicit_nosrc/stdout.log b/tests/testsuite/init/bin_already_exists_implicit_nosrc/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/both_lib_and_bin/mod.rs b/tests/testsuite/init/both_lib_and_bin/mod.rs new file mode 100644 index 0000000..c923232 --- /dev/null +++ b/tests/testsuite/init/both_lib_and_bin/mod.rs @@ -0,0 +1,19 @@ +use cargo_test_support::paths; +use cargo_test_support::prelude::*; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let cwd = paths::root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib --bin") + .current_dir(&cwd) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert!(!cwd.join("Cargo.toml").is_file()); +} diff --git a/tests/testsuite/init/both_lib_and_bin/stderr.log b/tests/testsuite/init/both_lib_and_bin/stderr.log new file mode 100644 index 0000000..9d635a4 --- /dev/null +++ b/tests/testsuite/init/both_lib_and_bin/stderr.log @@ -0,0 +1 @@ +error: can't specify both lib and binary outputs diff --git a/tests/testsuite/init/both_lib_and_bin/stdout.log b/tests/testsuite/init/both_lib_and_bin/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/case.rs b/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/case.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/case.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/lib.rs b/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/lib.rs new file mode 100644 index 0000000..59760b5 --- /dev/null +++ b/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/lib.rs @@ -0,0 +1 @@ +fn f() {} diff --git a/tests/testsuite/init/cant_create_library_when_both_binlib_present/mod.rs b/tests/testsuite/init/cant_create_library_when_both_binlib_present/mod.rs new file mode 100644 index 0000000..5e9e1b9 --- /dev/null +++ b/tests/testsuite/init/cant_create_library_when_both_binlib_present/mod.rs @@ -0,0 +1,18 @@ +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib") + .current_dir(project_root) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); +} diff --git a/tests/testsuite/init/cant_create_library_when_both_binlib_present/stderr.log b/tests/testsuite/init/cant_create_library_when_both_binlib_present/stderr.log new file mode 100644 index 0000000..c08dce9 --- /dev/null +++ b/tests/testsuite/init/cant_create_library_when_both_binlib_present/stderr.log @@ -0,0 +1 @@ +error: cannot have a package with multiple libraries, found both `case.rs` and `lib.rs` diff --git a/tests/testsuite/init/cant_create_library_when_both_binlib_present/stdout.log b/tests/testsuite/init/cant_create_library_when_both_binlib_present/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/confused_by_multiple_lib_files/in/lib.rs b/tests/testsuite/init/confused_by_multiple_lib_files/in/lib.rs new file mode 100644 index 0000000..3211637 --- /dev/null +++ b/tests/testsuite/init/confused_by_multiple_lib_files/in/lib.rs @@ -0,0 +1 @@ +fn f() { println!("lib.rs"); } diff --git a/tests/testsuite/init/confused_by_multiple_lib_files/in/src/lib.rs b/tests/testsuite/init/confused_by_multiple_lib_files/in/src/lib.rs new file mode 100644 index 0000000..f71455a --- /dev/null +++ b/tests/testsuite/init/confused_by_multiple_lib_files/in/src/lib.rs @@ -0,0 +1 @@ +fn f() { println!("src/lib.rs"); } diff --git a/tests/testsuite/init/confused_by_multiple_lib_files/mod.rs b/tests/testsuite/init/confused_by_multiple_lib_files/mod.rs new file mode 100644 index 0000000..d1cba2f --- /dev/null +++ b/tests/testsuite/init/confused_by_multiple_lib_files/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --vcs none") + .current_dir(project_root) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join("Cargo.toml").is_file()); +} diff --git a/tests/testsuite/init/confused_by_multiple_lib_files/out/lib.rs b/tests/testsuite/init/confused_by_multiple_lib_files/out/lib.rs new file mode 100644 index 0000000..3211637 --- /dev/null +++ b/tests/testsuite/init/confused_by_multiple_lib_files/out/lib.rs @@ -0,0 +1 @@ +fn f() { println!("lib.rs"); } diff --git a/tests/testsuite/init/confused_by_multiple_lib_files/out/src/lib.rs b/tests/testsuite/init/confused_by_multiple_lib_files/out/src/lib.rs new file mode 100644 index 0000000..f71455a --- /dev/null +++ b/tests/testsuite/init/confused_by_multiple_lib_files/out/src/lib.rs @@ -0,0 +1 @@ +fn f() { println!("src/lib.rs"); } diff --git a/tests/testsuite/init/confused_by_multiple_lib_files/stderr.log b/tests/testsuite/init/confused_by_multiple_lib_files/stderr.log new file mode 100644 index 0000000..8dbd2aa --- /dev/null +++ b/tests/testsuite/init/confused_by_multiple_lib_files/stderr.log @@ -0,0 +1 @@ +error: cannot have a package with multiple libraries, found both `src/lib.rs` and `lib.rs` diff --git a/tests/testsuite/init/confused_by_multiple_lib_files/stdout.log b/tests/testsuite/init/confused_by_multiple_lib_files/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/creates_binary_when_both_binlib_present/in/case.rs b/tests/testsuite/init/creates_binary_when_both_binlib_present/in/case.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_both_binlib_present/in/case.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/init/creates_binary_when_both_binlib_present/in/lib.rs b/tests/testsuite/init/creates_binary_when_both_binlib_present/in/lib.rs new file mode 100644 index 0000000..59760b5 --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_both_binlib_present/in/lib.rs @@ -0,0 +1 @@ +fn f() {} diff --git a/tests/testsuite/init/creates_binary_when_both_binlib_present/mod.rs b/tests/testsuite/init/creates_binary_when_both_binlib_present/mod.rs new file mode 100644 index 0000000..326bd21 --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_both_binlib_present/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --bin --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/creates_binary_when_both_binlib_present/out/Cargo.toml b/tests/testsuite/init/creates_binary_when_both_binlib_present/out/Cargo.toml new file mode 100644 index 0000000..675c888 --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_both_binlib_present/out/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[[bin]] +name = "case" +path = "case.rs" + +[lib] +name = "case" +path = "lib.rs" diff --git a/tests/testsuite/init/creates_binary_when_both_binlib_present/out/case.rs b/tests/testsuite/init/creates_binary_when_both_binlib_present/out/case.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_both_binlib_present/out/case.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/init/creates_binary_when_both_binlib_present/out/lib.rs b/tests/testsuite/init/creates_binary_when_both_binlib_present/out/lib.rs new file mode 100644 index 0000000..59760b5 --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_both_binlib_present/out/lib.rs @@ -0,0 +1 @@ +fn f() {} diff --git a/tests/testsuite/init/creates_binary_when_both_binlib_present/stderr.log b/tests/testsuite/init/creates_binary_when_both_binlib_present/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_both_binlib_present/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/creates_binary_when_both_binlib_present/stdout.log b/tests/testsuite/init/creates_binary_when_both_binlib_present/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/in/case.rs b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/in/case.rs new file mode 100644 index 0000000..59760b5 --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/in/case.rs @@ -0,0 +1 @@ +fn f() {} diff --git a/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/mod.rs b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/mod.rs new file mode 100644 index 0000000..326bd21 --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --bin --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml new file mode 100644 index 0000000..8da5fe7 --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[[bin]] +name = "case" +path = "case.rs" diff --git a/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/case.rs b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/case.rs new file mode 100644 index 0000000..59760b5 --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/case.rs @@ -0,0 +1 @@ +fn f() {} diff --git a/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stderr.log b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stderr.log new file mode 100644 index 0000000..ec428f3 --- /dev/null +++ b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stderr.log @@ -0,0 +1,2 @@ +warning: file `case.rs` seems to be a library file + Created binary (application) package diff --git a/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stdout.log b/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/in/case.rs b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/in/case.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/in/case.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/mod.rs b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/mod.rs new file mode 100644 index 0000000..59c192c --- /dev/null +++ b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml new file mode 100644 index 0000000..2c04644 --- /dev/null +++ b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[lib] +name = "case" +path = "case.rs" diff --git a/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/case.rs b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/case.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/case.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stderr.log b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stderr.log new file mode 100644 index 0000000..bf070e2 --- /dev/null +++ b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stderr.log @@ -0,0 +1,2 @@ +warning: file `case.rs` seems to be a binary (application) file + Created library package diff --git a/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stdout.log b/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/empty_dir/.keep b/tests/testsuite/init/empty_dir/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/empty_dir/mod.rs b/tests/testsuite/init/empty_dir/mod.rs new file mode 100644 index 0000000..074954f --- /dev/null +++ b/tests/testsuite/init/empty_dir/mod.rs @@ -0,0 +1,7 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::{command_is_available, paths, Project}; +use std::fs; +use std::process::Command; + +use crate::test_root; diff --git a/tests/testsuite/init/explicit_bin_with_git/in b/tests/testsuite/init/explicit_bin_with_git/in new file mode 120000 index 0000000..1202506 --- /dev/null +++ b/tests/testsuite/init/explicit_bin_with_git/in @@ -0,0 +1 @@ +../empty_dir \ No newline at end of file diff --git a/tests/testsuite/init/explicit_bin_with_git/mod.rs b/tests/testsuite/init/explicit_bin_with_git/mod.rs new file mode 100644 index 0000000..7314e95 --- /dev/null +++ b/tests/testsuite/init/explicit_bin_with_git/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --vcs git --bin") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/explicit_bin_with_git/out/.gitignore b/tests/testsuite/init/explicit_bin_with_git/out/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/tests/testsuite/init/explicit_bin_with_git/out/.gitignore @@ -0,0 +1 @@ +/target diff --git a/tests/testsuite/init/explicit_bin_with_git/out/Cargo.toml b/tests/testsuite/init/explicit_bin_with_git/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/explicit_bin_with_git/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/explicit_bin_with_git/out/src/main.rs b/tests/testsuite/init/explicit_bin_with_git/out/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/tests/testsuite/init/explicit_bin_with_git/out/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/tests/testsuite/init/explicit_bin_with_git/stderr.log b/tests/testsuite/init/explicit_bin_with_git/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/explicit_bin_with_git/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/explicit_bin_with_git/stdout.log b/tests/testsuite/init/explicit_bin_with_git/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/formats_source/in/rustfmt.toml b/tests/testsuite/init/formats_source/in/rustfmt.toml new file mode 100644 index 0000000..b196eaa --- /dev/null +++ b/tests/testsuite/init/formats_source/in/rustfmt.toml @@ -0,0 +1 @@ +tab_spaces = 2 diff --git a/tests/testsuite/init/formats_source/mod.rs b/tests/testsuite/init/formats_source/mod.rs new file mode 100644 index 0000000..ac1fb62 --- /dev/null +++ b/tests/testsuite/init/formats_source/mod.rs @@ -0,0 +1,29 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::{process, Project}; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + // This cannot use `requires_rustfmt` because rustfmt is not available in + // the rust-lang/rust environment. Additionally, if running cargo without + // rustup (but with rustup installed), this test also fails due to HOME + // preventing the proxy from choosing a toolchain. + if let Err(e) = process("rustfmt").arg("-V").exec_with_output() { + eprintln!("skipping test, rustfmt not available:\n{e:?}"); + return; + } + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/formats_source/out/Cargo.toml b/tests/testsuite/init/formats_source/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/formats_source/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/formats_source/out/rustfmt.toml b/tests/testsuite/init/formats_source/out/rustfmt.toml new file mode 100644 index 0000000..b196eaa --- /dev/null +++ b/tests/testsuite/init/formats_source/out/rustfmt.toml @@ -0,0 +1 @@ +tab_spaces = 2 diff --git a/tests/testsuite/init/formats_source/out/src/lib.rs b/tests/testsuite/init/formats_source/out/src/lib.rs new file mode 100644 index 0000000..3b9acff --- /dev/null +++ b/tests/testsuite/init/formats_source/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/formats_source/stderr.log b/tests/testsuite/init/formats_source/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/formats_source/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/formats_source/stdout.log b/tests/testsuite/init/formats_source/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/fossil_autodetect/in/.fossil/.keep b/tests/testsuite/init/fossil_autodetect/in/.fossil/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/fossil_autodetect/mod.rs b/tests/testsuite/init/fossil_autodetect/mod.rs new file mode 100644 index 0000000..d45ba86 --- /dev/null +++ b/tests/testsuite/init/fossil_autodetect/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join(".git").is_dir()); +} diff --git a/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/clean-glob b/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/clean-glob new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/clean-glob @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/ignore-glob b/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/ignore-glob new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/ignore-glob @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/tests/testsuite/init/fossil_autodetect/out/Cargo.toml b/tests/testsuite/init/fossil_autodetect/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/fossil_autodetect/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/fossil_autodetect/out/src/lib.rs b/tests/testsuite/init/fossil_autodetect/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/fossil_autodetect/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/fossil_autodetect/stderr.log b/tests/testsuite/init/fossil_autodetect/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/fossil_autodetect/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/fossil_autodetect/stdout.log b/tests/testsuite/init/fossil_autodetect/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/git_autodetect/mod.rs b/tests/testsuite/init/git_autodetect/mod.rs new file mode 100644 index 0000000..aef47bc --- /dev/null +++ b/tests/testsuite/init/git_autodetect/mod.rs @@ -0,0 +1,24 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::paths; +use cargo_test_support::prelude::*; +use std::fs; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project_root = &paths::root().join("foo"); + // Need to create `.git` dir manually because it cannot be tracked under a git repo + fs::create_dir_all(project_root.join(".git")).unwrap(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(project_root.join(".git").is_dir()); +} diff --git a/tests/testsuite/init/git_autodetect/out/.gitignore b/tests/testsuite/init/git_autodetect/out/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/tests/testsuite/init/git_autodetect/out/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/tests/testsuite/init/git_autodetect/out/Cargo.toml b/tests/testsuite/init/git_autodetect/out/Cargo.toml new file mode 100644 index 0000000..1d9cfe3 --- /dev/null +++ b/tests/testsuite/init/git_autodetect/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "foo" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/git_autodetect/out/src/lib.rs b/tests/testsuite/init/git_autodetect/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/git_autodetect/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/git_autodetect/stderr.log b/tests/testsuite/init/git_autodetect/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/git_autodetect/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/git_autodetect/stdout.log b/tests/testsuite/init/git_autodetect/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/in/.gitignore b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/in/.gitignore new file mode 100644 index 0000000..e3a2b04 --- /dev/null +++ b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/in/.gitignore @@ -0,0 +1 @@ +**/some.file \ No newline at end of file diff --git a/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/mod.rs b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/mod.rs new file mode 100644 index 0000000..cd4437c --- /dev/null +++ b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib --edition 2015") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(project_root.join(".git").is_dir()); +} diff --git a/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/.gitignore b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/.gitignore new file mode 100644 index 0000000..e2e02f2 --- /dev/null +++ b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/.gitignore @@ -0,0 +1,6 @@ +**/some.file + +# Added by cargo + +/target +/Cargo.lock diff --git a/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/Cargo.toml b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/Cargo.toml new file mode 100644 index 0000000..a6269fd --- /dev/null +++ b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2015" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/src/lib.rs b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stderr.log b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stdout.log b/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/ignores_failure_to_format_source/in/rustfmt.toml b/tests/testsuite/init/ignores_failure_to_format_source/in/rustfmt.toml new file mode 100644 index 0000000..b196eaa --- /dev/null +++ b/tests/testsuite/init/ignores_failure_to_format_source/in/rustfmt.toml @@ -0,0 +1 @@ +tab_spaces = 2 diff --git a/tests/testsuite/init/ignores_failure_to_format_source/mod.rs b/tests/testsuite/init/ignores_failure_to_format_source/mod.rs new file mode 100644 index 0000000..fd93940 --- /dev/null +++ b/tests/testsuite/init/ignores_failure_to_format_source/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib --vcs none") + .env("PATH", "") // pretend that `rustfmt` is missing + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/ignores_failure_to_format_source/out/Cargo.toml b/tests/testsuite/init/ignores_failure_to_format_source/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/ignores_failure_to_format_source/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/ignores_failure_to_format_source/out/rustfmt.toml b/tests/testsuite/init/ignores_failure_to_format_source/out/rustfmt.toml new file mode 100644 index 0000000..b196eaa --- /dev/null +++ b/tests/testsuite/init/ignores_failure_to_format_source/out/rustfmt.toml @@ -0,0 +1 @@ +tab_spaces = 2 diff --git a/tests/testsuite/init/ignores_failure_to_format_source/out/src/lib.rs b/tests/testsuite/init/ignores_failure_to_format_source/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/ignores_failure_to_format_source/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/ignores_failure_to_format_source/stderr.log b/tests/testsuite/init/ignores_failure_to_format_source/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/ignores_failure_to_format_source/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/ignores_failure_to_format_source/stdout.log b/tests/testsuite/init/ignores_failure_to_format_source/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/inferred_bin_with_git/in/main.rs b/tests/testsuite/init/inferred_bin_with_git/in/main.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/testsuite/init/inferred_bin_with_git/in/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/init/inferred_bin_with_git/mod.rs b/tests/testsuite/init/inferred_bin_with_git/mod.rs new file mode 100644 index 0000000..80bec88 --- /dev/null +++ b/tests/testsuite/init/inferred_bin_with_git/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --vcs git") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/inferred_bin_with_git/out/.gitignore b/tests/testsuite/init/inferred_bin_with_git/out/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/tests/testsuite/init/inferred_bin_with_git/out/.gitignore @@ -0,0 +1 @@ +/target diff --git a/tests/testsuite/init/inferred_bin_with_git/out/Cargo.toml b/tests/testsuite/init/inferred_bin_with_git/out/Cargo.toml new file mode 100644 index 0000000..5c6c915 --- /dev/null +++ b/tests/testsuite/init/inferred_bin_with_git/out/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[[bin]] +name = "case" +path = "main.rs" diff --git a/tests/testsuite/init/inferred_bin_with_git/out/main.rs b/tests/testsuite/init/inferred_bin_with_git/out/main.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/testsuite/init/inferred_bin_with_git/out/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/init/inferred_bin_with_git/stderr.log b/tests/testsuite/init/inferred_bin_with_git/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/inferred_bin_with_git/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/inferred_bin_with_git/stdout.log b/tests/testsuite/init/inferred_bin_with_git/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/inferred_lib_with_git/in/lib.rs b/tests/testsuite/init/inferred_lib_with_git/in/lib.rs new file mode 100644 index 0000000..59760b5 --- /dev/null +++ b/tests/testsuite/init/inferred_lib_with_git/in/lib.rs @@ -0,0 +1 @@ +fn f() {} diff --git a/tests/testsuite/init/inferred_lib_with_git/mod.rs b/tests/testsuite/init/inferred_lib_with_git/mod.rs new file mode 100644 index 0000000..80bec88 --- /dev/null +++ b/tests/testsuite/init/inferred_lib_with_git/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --vcs git") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/inferred_lib_with_git/out/.gitignore b/tests/testsuite/init/inferred_lib_with_git/out/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/tests/testsuite/init/inferred_lib_with_git/out/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/tests/testsuite/init/inferred_lib_with_git/out/Cargo.toml b/tests/testsuite/init/inferred_lib_with_git/out/Cargo.toml new file mode 100644 index 0000000..39e95fe --- /dev/null +++ b/tests/testsuite/init/inferred_lib_with_git/out/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[lib] +name = "case" +path = "lib.rs" diff --git a/tests/testsuite/init/inferred_lib_with_git/out/lib.rs b/tests/testsuite/init/inferred_lib_with_git/out/lib.rs new file mode 100644 index 0000000..59760b5 --- /dev/null +++ b/tests/testsuite/init/inferred_lib_with_git/out/lib.rs @@ -0,0 +1 @@ +fn f() {} diff --git a/tests/testsuite/init/inferred_lib_with_git/stderr.log b/tests/testsuite/init/inferred_lib_with_git/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/inferred_lib_with_git/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/inferred_lib_with_git/stdout.log b/tests/testsuite/init/inferred_lib_with_git/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/invalid_dir_name/mod.rs b/tests/testsuite/init/invalid_dir_name/mod.rs new file mode 100644 index 0000000..2b1be90 --- /dev/null +++ b/tests/testsuite/init/invalid_dir_name/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::paths; +use cargo_test_support::prelude::*; +use std::fs; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let foo = &paths::root().join("foo.bar"); + fs::create_dir_all(foo).unwrap(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init") + .current_dir(foo) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert!(!foo.join("Cargo.toml").is_file()); +} diff --git a/tests/testsuite/init/invalid_dir_name/stderr.log b/tests/testsuite/init/invalid_dir_name/stderr.log new file mode 100644 index 0000000..86d2c66 --- /dev/null +++ b/tests/testsuite/init/invalid_dir_name/stderr.log @@ -0,0 +1,8 @@ +error: invalid character `.` in package name: `foo.bar`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) +If you need a package name to not match the directory name, consider using --name flag. +If you need a binary with the name "foo.bar", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/foo.bar.rs` or change the name in Cargo.toml with: + + [[bin]] + name = "foo.bar" + path = "src/main.rs" + diff --git a/tests/testsuite/init/invalid_dir_name/stdout.log b/tests/testsuite/init/invalid_dir_name/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/lib_already_exists_nosrc/in/lib.rs b/tests/testsuite/init/lib_already_exists_nosrc/in/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/lib_already_exists_nosrc/mod.rs b/tests/testsuite/init/lib_already_exists_nosrc/mod.rs new file mode 100644 index 0000000..d3e8e66 --- /dev/null +++ b/tests/testsuite/init/lib_already_exists_nosrc/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join("src/main.rs").is_file()); +} diff --git a/tests/testsuite/init/lib_already_exists_nosrc/out/Cargo.toml b/tests/testsuite/init/lib_already_exists_nosrc/out/Cargo.toml new file mode 100644 index 0000000..39e95fe --- /dev/null +++ b/tests/testsuite/init/lib_already_exists_nosrc/out/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[lib] +name = "case" +path = "lib.rs" diff --git a/tests/testsuite/init/lib_already_exists_nosrc/out/lib.rs b/tests/testsuite/init/lib_already_exists_nosrc/out/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/lib_already_exists_nosrc/stderr.log b/tests/testsuite/init/lib_already_exists_nosrc/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/lib_already_exists_nosrc/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/lib_already_exists_nosrc/stdout.log b/tests/testsuite/init/lib_already_exists_nosrc/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/lib_already_exists_src/in/src/lib.rs b/tests/testsuite/init/lib_already_exists_src/in/src/lib.rs new file mode 100644 index 0000000..59760b5 --- /dev/null +++ b/tests/testsuite/init/lib_already_exists_src/in/src/lib.rs @@ -0,0 +1 @@ +fn f() {} diff --git a/tests/testsuite/init/lib_already_exists_src/mod.rs b/tests/testsuite/init/lib_already_exists_src/mod.rs new file mode 100644 index 0000000..d3e8e66 --- /dev/null +++ b/tests/testsuite/init/lib_already_exists_src/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join("src/main.rs").is_file()); +} diff --git a/tests/testsuite/init/lib_already_exists_src/out/Cargo.toml b/tests/testsuite/init/lib_already_exists_src/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/lib_already_exists_src/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/lib_already_exists_src/out/src/lib.rs b/tests/testsuite/init/lib_already_exists_src/out/src/lib.rs new file mode 100644 index 0000000..59760b5 --- /dev/null +++ b/tests/testsuite/init/lib_already_exists_src/out/src/lib.rs @@ -0,0 +1 @@ +fn f() {} diff --git a/tests/testsuite/init/lib_already_exists_src/stderr.log b/tests/testsuite/init/lib_already_exists_src/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/lib_already_exists_src/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/lib_already_exists_src/stdout.log b/tests/testsuite/init/lib_already_exists_src/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/mercurial_autodetect/in/.hg/.keep b/tests/testsuite/init/mercurial_autodetect/in/.hg/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/mercurial_autodetect/mod.rs b/tests/testsuite/init/mercurial_autodetect/mod.rs new file mode 100644 index 0000000..d45ba86 --- /dev/null +++ b/tests/testsuite/init/mercurial_autodetect/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join(".git").is_dir()); +} diff --git a/tests/testsuite/init/mercurial_autodetect/out/.hgignore b/tests/testsuite/init/mercurial_autodetect/out/.hgignore new file mode 100644 index 0000000..1ae6a78 --- /dev/null +++ b/tests/testsuite/init/mercurial_autodetect/out/.hgignore @@ -0,0 +1,2 @@ +^target/ +^Cargo.lock$ diff --git a/tests/testsuite/init/mercurial_autodetect/out/Cargo.toml b/tests/testsuite/init/mercurial_autodetect/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/mercurial_autodetect/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/mercurial_autodetect/out/src/lib.rs b/tests/testsuite/init/mercurial_autodetect/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/mercurial_autodetect/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/mercurial_autodetect/stderr.log b/tests/testsuite/init/mercurial_autodetect/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/mercurial_autodetect/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/mercurial_autodetect/stdout.log b/tests/testsuite/init/mercurial_autodetect/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/mod.rs b/tests/testsuite/init/mod.rs new file mode 100644 index 0000000..99df9d3 --- /dev/null +++ b/tests/testsuite/init/mod.rs @@ -0,0 +1,42 @@ +//! Tests for the `cargo init` command. + +mod auto_git; +mod bin_already_exists_explicit; +mod bin_already_exists_explicit_nosrc; +mod bin_already_exists_implicit; +mod bin_already_exists_implicit_namenosrc; +mod bin_already_exists_implicit_namesrc; +mod bin_already_exists_implicit_nosrc; +mod both_lib_and_bin; +mod cant_create_library_when_both_binlib_present; +mod confused_by_multiple_lib_files; +mod creates_binary_when_both_binlib_present; +mod creates_binary_when_instructed_and_has_lib_file; +mod creates_library_when_instructed_and_has_bin_file; +mod explicit_bin_with_git; +mod formats_source; +mod fossil_autodetect; +mod git_autodetect; +mod git_ignore_exists_no_conflicting_entries; +mod ignores_failure_to_format_source; +mod inferred_bin_with_git; +mod inferred_lib_with_git; +mod invalid_dir_name; +mod lib_already_exists_nosrc; +mod lib_already_exists_src; +mod mercurial_autodetect; +mod multibin_project_name_clash; +#[cfg(not(windows))] +mod no_filename; +#[cfg(unix)] +mod path_contains_separator; +mod pijul_autodetect; +mod reserved_name; +mod simple_bin; +mod simple_git; +mod simple_git_ignore_exists; +mod simple_hg; +mod simple_hg_ignore_exists; +mod simple_lib; +mod unknown_flags; +mod with_argument; diff --git a/tests/testsuite/init/multibin_project_name_clash/in/case.rs b/tests/testsuite/init/multibin_project_name_clash/in/case.rs new file mode 100644 index 0000000..b312211 --- /dev/null +++ b/tests/testsuite/init/multibin_project_name_clash/in/case.rs @@ -0,0 +1 @@ +fn main() { println!("foo.rs"); } diff --git a/tests/testsuite/init/multibin_project_name_clash/in/main.rs b/tests/testsuite/init/multibin_project_name_clash/in/main.rs new file mode 100644 index 0000000..7937627 --- /dev/null +++ b/tests/testsuite/init/multibin_project_name_clash/in/main.rs @@ -0,0 +1 @@ +fn main() { println!("main.rs"); } diff --git a/tests/testsuite/init/multibin_project_name_clash/mod.rs b/tests/testsuite/init/multibin_project_name_clash/mod.rs new file mode 100644 index 0000000..fdd4476 --- /dev/null +++ b/tests/testsuite/init/multibin_project_name_clash/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib --vcs none") + .current_dir(project_root) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join("Cargo.toml").is_file()); +} diff --git a/tests/testsuite/init/multibin_project_name_clash/out/case.rs b/tests/testsuite/init/multibin_project_name_clash/out/case.rs new file mode 100644 index 0000000..b312211 --- /dev/null +++ b/tests/testsuite/init/multibin_project_name_clash/out/case.rs @@ -0,0 +1 @@ +fn main() { println!("foo.rs"); } diff --git a/tests/testsuite/init/multibin_project_name_clash/out/main.rs b/tests/testsuite/init/multibin_project_name_clash/out/main.rs new file mode 100644 index 0000000..7937627 --- /dev/null +++ b/tests/testsuite/init/multibin_project_name_clash/out/main.rs @@ -0,0 +1 @@ +fn main() { println!("main.rs"); } diff --git a/tests/testsuite/init/multibin_project_name_clash/stderr.log b/tests/testsuite/init/multibin_project_name_clash/stderr.log new file mode 100644 index 0000000..21a1dab --- /dev/null +++ b/tests/testsuite/init/multibin_project_name_clash/stderr.log @@ -0,0 +1,4 @@ +error: multiple possible binary sources found: + main.rs + case.rs +cannot automatically generate Cargo.toml as the main target would be ambiguous diff --git a/tests/testsuite/init/multibin_project_name_clash/stdout.log b/tests/testsuite/init/multibin_project_name_clash/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/no_filename/mod.rs b/tests/testsuite/init/no_filename/mod.rs new file mode 100644 index 0000000..8edfd28 --- /dev/null +++ b/tests/testsuite/init/no_filename/mod.rs @@ -0,0 +1,16 @@ +use cargo_test_support::paths; +use cargo_test_support::prelude::*; + +use cargo_test_support::curr_dir; + +#[cfg(not(windows))] +#[cargo_test] +fn case() { + snapbox::cmd::Command::cargo_ui() + .arg_line("init /") + .current_dir(paths::root()) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); +} diff --git a/tests/testsuite/init/no_filename/stderr.log b/tests/testsuite/init/no_filename/stderr.log new file mode 100644 index 0000000..bd087ec --- /dev/null +++ b/tests/testsuite/init/no_filename/stderr.log @@ -0,0 +1 @@ +error: cannot auto-detect package name from path "/" ; use --name to override diff --git a/tests/testsuite/init/no_filename/stdout.log b/tests/testsuite/init/no_filename/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/path_contains_separator/in/.keep b/tests/testsuite/init/path_contains_separator/in/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/path_contains_separator/mod.rs b/tests/testsuite/init/path_contains_separator/mod.rs new file mode 100644 index 0000000..0a12f42 --- /dev/null +++ b/tests/testsuite/init/path_contains_separator/mod.rs @@ -0,0 +1,26 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::{t, Project}; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root().join("test:ing"); + + if !project_root.exists() { + t!(std::fs::create_dir(&project_root)); + } + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --bin --vcs none --edition 2015 --name testing") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join(".gitignore").is_file()); +} diff --git a/tests/testsuite/init/path_contains_separator/out/Cargo.toml b/tests/testsuite/init/path_contains_separator/out/Cargo.toml new file mode 100644 index 0000000..11465f1 --- /dev/null +++ b/tests/testsuite/init/path_contains_separator/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "testing" +version = "0.1.0" +edition = "2015" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/path_contains_separator/out/src/main.rs b/tests/testsuite/init/path_contains_separator/out/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/tests/testsuite/init/path_contains_separator/out/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/tests/testsuite/init/path_contains_separator/stderr.log b/tests/testsuite/init/path_contains_separator/stderr.log new file mode 100644 index 0000000..d7947ae --- /dev/null +++ b/tests/testsuite/init/path_contains_separator/stderr.log @@ -0,0 +1,3 @@ +warning: the path `[ROOT]/case/test:ing/.` contains invalid PATH characters (usually `:`, `;`, or `"`) +It is recommended to use a different name to avoid problems. + Created binary (application) package diff --git a/tests/testsuite/init/path_contains_separator/stdout.log b/tests/testsuite/init/path_contains_separator/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/pijul_autodetect/in/.pijul/.keep b/tests/testsuite/init/pijul_autodetect/in/.pijul/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/pijul_autodetect/mod.rs b/tests/testsuite/init/pijul_autodetect/mod.rs new file mode 100644 index 0000000..d45ba86 --- /dev/null +++ b/tests/testsuite/init/pijul_autodetect/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join(".git").is_dir()); +} diff --git a/tests/testsuite/init/pijul_autodetect/out/.ignore b/tests/testsuite/init/pijul_autodetect/out/.ignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/tests/testsuite/init/pijul_autodetect/out/.ignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/tests/testsuite/init/pijul_autodetect/out/Cargo.toml b/tests/testsuite/init/pijul_autodetect/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/pijul_autodetect/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/pijul_autodetect/out/src/lib.rs b/tests/testsuite/init/pijul_autodetect/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/pijul_autodetect/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/pijul_autodetect/stderr.log b/tests/testsuite/init/pijul_autodetect/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/pijul_autodetect/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/pijul_autodetect/stdout.log b/tests/testsuite/init/pijul_autodetect/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/reserved_name/mod.rs b/tests/testsuite/init/reserved_name/mod.rs new file mode 100644 index 0000000..cc65fd0 --- /dev/null +++ b/tests/testsuite/init/reserved_name/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::paths; +use cargo_test_support::prelude::*; +use std::fs; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project_root = &paths::root().join("test"); + fs::create_dir_all(project_root).unwrap(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init") + .current_dir(project_root) + .assert() + .code(101) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert!(!project_root.join("Cargo.toml").is_file()); +} diff --git a/tests/testsuite/init/reserved_name/stderr.log b/tests/testsuite/init/reserved_name/stderr.log new file mode 100644 index 0000000..748971b --- /dev/null +++ b/tests/testsuite/init/reserved_name/stderr.log @@ -0,0 +1,8 @@ +error: the name `test` cannot be used as a package name, it conflicts with Rust's built-in test library +If you need a package name to not match the directory name, consider using --name flag. +If you need a binary with the name "test", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/test.rs` or change the name in Cargo.toml with: + + [[bin]] + name = "test" + path = "src/main.rs" + diff --git a/tests/testsuite/init/reserved_name/stdout.log b/tests/testsuite/init/reserved_name/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/simple_bin/in b/tests/testsuite/init/simple_bin/in new file mode 120000 index 0000000..1202506 --- /dev/null +++ b/tests/testsuite/init/simple_bin/in @@ -0,0 +1 @@ +../empty_dir \ No newline at end of file diff --git a/tests/testsuite/init/simple_bin/mod.rs b/tests/testsuite/init/simple_bin/mod.rs new file mode 100644 index 0000000..eaf0955 --- /dev/null +++ b/tests/testsuite/init/simple_bin/mod.rs @@ -0,0 +1,29 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --bin --vcs none --edition 2015") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join(".gitignore").is_file()); + + snapbox::cmd::Command::cargo_ui() + .current_dir(project_root) + .arg("build") + .assert() + .success(); + assert!(project.bin("case").is_file()); +} diff --git a/tests/testsuite/init/simple_bin/out/Cargo.toml b/tests/testsuite/init/simple_bin/out/Cargo.toml new file mode 100644 index 0000000..a6269fd --- /dev/null +++ b/tests/testsuite/init/simple_bin/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2015" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/simple_bin/out/src/main.rs b/tests/testsuite/init/simple_bin/out/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/tests/testsuite/init/simple_bin/out/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/tests/testsuite/init/simple_bin/stderr.log b/tests/testsuite/init/simple_bin/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/simple_bin/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/simple_bin/stdout.log b/tests/testsuite/init/simple_bin/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/simple_git/in b/tests/testsuite/init/simple_git/in new file mode 120000 index 0000000..1202506 --- /dev/null +++ b/tests/testsuite/init/simple_git/in @@ -0,0 +1 @@ +../empty_dir \ No newline at end of file diff --git a/tests/testsuite/init/simple_git/mod.rs b/tests/testsuite/init/simple_git/mod.rs new file mode 100644 index 0000000..c373fe2 --- /dev/null +++ b/tests/testsuite/init/simple_git/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib --vcs git") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(project_root.join(".git").is_dir()); +} diff --git a/tests/testsuite/init/simple_git/out/.gitignore b/tests/testsuite/init/simple_git/out/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/tests/testsuite/init/simple_git/out/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/tests/testsuite/init/simple_git/out/Cargo.toml b/tests/testsuite/init/simple_git/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/simple_git/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/simple_git/out/src/lib.rs b/tests/testsuite/init/simple_git/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/simple_git/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/simple_git/stderr.log b/tests/testsuite/init/simple_git/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/simple_git/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/simple_git/stdout.log b/tests/testsuite/init/simple_git/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/simple_git_ignore_exists/in/.gitignore b/tests/testsuite/init/simple_git_ignore_exists/in/.gitignore new file mode 100644 index 0000000..d0f753e --- /dev/null +++ b/tests/testsuite/init/simple_git_ignore_exists/in/.gitignore @@ -0,0 +1,2 @@ +/target +**/some.file \ No newline at end of file diff --git a/tests/testsuite/init/simple_git_ignore_exists/mod.rs b/tests/testsuite/init/simple_git_ignore_exists/mod.rs new file mode 100644 index 0000000..142e86e --- /dev/null +++ b/tests/testsuite/init/simple_git_ignore_exists/mod.rs @@ -0,0 +1,28 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib --edition 2015") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(project_root.join(".git").is_dir()); + + snapbox::cmd::Command::cargo_ui() + .current_dir(project_root) + .arg("build") + .assert() + .success(); +} diff --git a/tests/testsuite/init/simple_git_ignore_exists/out/.gitignore b/tests/testsuite/init/simple_git_ignore_exists/out/.gitignore new file mode 100644 index 0000000..4447742 --- /dev/null +++ b/tests/testsuite/init/simple_git_ignore_exists/out/.gitignore @@ -0,0 +1,9 @@ +/target +**/some.file + +# Added by cargo +# +# already existing elements were commented out + +#/target +/Cargo.lock diff --git a/tests/testsuite/init/simple_git_ignore_exists/out/Cargo.toml b/tests/testsuite/init/simple_git_ignore_exists/out/Cargo.toml new file mode 100644 index 0000000..a6269fd --- /dev/null +++ b/tests/testsuite/init/simple_git_ignore_exists/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2015" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/simple_git_ignore_exists/out/src/lib.rs b/tests/testsuite/init/simple_git_ignore_exists/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/simple_git_ignore_exists/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/simple_git_ignore_exists/stderr.log b/tests/testsuite/init/simple_git_ignore_exists/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/simple_git_ignore_exists/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/simple_git_ignore_exists/stdout.log b/tests/testsuite/init/simple_git_ignore_exists/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/simple_hg/in b/tests/testsuite/init/simple_hg/in new file mode 120000 index 0000000..1202506 --- /dev/null +++ b/tests/testsuite/init/simple_hg/in @@ -0,0 +1 @@ +../empty_dir \ No newline at end of file diff --git a/tests/testsuite/init/simple_hg/mod.rs b/tests/testsuite/init/simple_hg/mod.rs new file mode 100644 index 0000000..1d67654 --- /dev/null +++ b/tests/testsuite/init/simple_hg/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test(requires_hg)] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib --vcs hg") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join(".git").is_dir()); +} diff --git a/tests/testsuite/init/simple_hg/out/.hgignore b/tests/testsuite/init/simple_hg/out/.hgignore new file mode 100644 index 0000000..1ae6a78 --- /dev/null +++ b/tests/testsuite/init/simple_hg/out/.hgignore @@ -0,0 +1,2 @@ +^target/ +^Cargo.lock$ diff --git a/tests/testsuite/init/simple_hg/out/Cargo.toml b/tests/testsuite/init/simple_hg/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/simple_hg/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/simple_hg/out/src/lib.rs b/tests/testsuite/init/simple_hg/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/simple_hg/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/simple_hg/stderr.log b/tests/testsuite/init/simple_hg/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/simple_hg/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/simple_hg/stdout.log b/tests/testsuite/init/simple_hg/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/simple_hg_ignore_exists/in/.hg/.keep b/tests/testsuite/init/simple_hg_ignore_exists/in/.hg/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/simple_hg_ignore_exists/in/.hgignore b/tests/testsuite/init/simple_hg_ignore_exists/in/.hgignore new file mode 100644 index 0000000..0fc474f --- /dev/null +++ b/tests/testsuite/init/simple_hg_ignore_exists/in/.hgignore @@ -0,0 +1 @@ +^/somefile \ No newline at end of file diff --git a/tests/testsuite/init/simple_hg_ignore_exists/mod.rs b/tests/testsuite/init/simple_hg_ignore_exists/mod.rs new file mode 100644 index 0000000..d45ba86 --- /dev/null +++ b/tests/testsuite/init/simple_hg_ignore_exists/mod.rs @@ -0,0 +1,22 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join(".git").is_dir()); +} diff --git a/tests/testsuite/init/simple_hg_ignore_exists/out/.hgignore b/tests/testsuite/init/simple_hg_ignore_exists/out/.hgignore new file mode 100644 index 0000000..2ab1fce --- /dev/null +++ b/tests/testsuite/init/simple_hg_ignore_exists/out/.hgignore @@ -0,0 +1,6 @@ +^/somefile + +# Added by cargo + +^target/ +^Cargo.lock$ diff --git a/tests/testsuite/init/simple_hg_ignore_exists/out/Cargo.toml b/tests/testsuite/init/simple_hg_ignore_exists/out/Cargo.toml new file mode 100644 index 0000000..dcdb8da --- /dev/null +++ b/tests/testsuite/init/simple_hg_ignore_exists/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/simple_hg_ignore_exists/out/src/lib.rs b/tests/testsuite/init/simple_hg_ignore_exists/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/simple_hg_ignore_exists/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/simple_hg_ignore_exists/stderr.log b/tests/testsuite/init/simple_hg_ignore_exists/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/simple_hg_ignore_exists/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/simple_hg_ignore_exists/stdout.log b/tests/testsuite/init/simple_hg_ignore_exists/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/simple_lib/in b/tests/testsuite/init/simple_lib/in new file mode 120000 index 0000000..1202506 --- /dev/null +++ b/tests/testsuite/init/simple_lib/in @@ -0,0 +1 @@ +../empty_dir \ No newline at end of file diff --git a/tests/testsuite/init/simple_lib/mod.rs b/tests/testsuite/init/simple_lib/mod.rs new file mode 100644 index 0000000..d6bae51 --- /dev/null +++ b/tests/testsuite/init/simple_lib/mod.rs @@ -0,0 +1,29 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init --lib --vcs none --edition 2015") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); + assert!(!project_root.join(".gitignore").is_file()); + + snapbox::cmd::Command::cargo_ui() + .current_dir(project_root) + .arg("build") + .assert() + .success(); + assert!(!project.bin("foo").is_file()); +} diff --git a/tests/testsuite/init/simple_lib/out/Cargo.toml b/tests/testsuite/init/simple_lib/out/Cargo.toml new file mode 100644 index 0000000..a6269fd --- /dev/null +++ b/tests/testsuite/init/simple_lib/out/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "case" +version = "0.1.0" +edition = "2015" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/simple_lib/out/src/lib.rs b/tests/testsuite/init/simple_lib/out/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/tests/testsuite/init/simple_lib/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/tests/testsuite/init/simple_lib/stderr.log b/tests/testsuite/init/simple_lib/stderr.log new file mode 100644 index 0000000..f459bf2 --- /dev/null +++ b/tests/testsuite/init/simple_lib/stderr.log @@ -0,0 +1 @@ + Created library package diff --git a/tests/testsuite/init/simple_lib/stdout.log b/tests/testsuite/init/simple_lib/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/unknown_flags/mod.rs b/tests/testsuite/init/unknown_flags/mod.rs new file mode 100644 index 0000000..b68296c --- /dev/null +++ b/tests/testsuite/init/unknown_flags/mod.rs @@ -0,0 +1,16 @@ +use cargo_test_support::paths; +use cargo_test_support::prelude::*; + +use cargo_test_support::curr_dir; + +#[cargo_test] +#[ignore = "temporarily disabled for beta due to clap update"] +fn case() { + snapbox::cmd::Command::cargo_ui() + .arg_line("init foo --flag") + .current_dir(paths::root()) + .assert() + .code(1) + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); +} diff --git a/tests/testsuite/init/unknown_flags/stderr.log b/tests/testsuite/init/unknown_flags/stderr.log new file mode 100644 index 0000000..31e7eb8 --- /dev/null +++ b/tests/testsuite/init/unknown_flags/stderr.log @@ -0,0 +1,7 @@ +error: unexpected argument '--flag' found + + note: to pass '--flag' as a value, use '-- --flag' + +Usage: cargo[EXE] init + +For more information, try '--help'. diff --git a/tests/testsuite/init/unknown_flags/stdout.log b/tests/testsuite/init/unknown_flags/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/with_argument/in/foo/.keep b/tests/testsuite/init/with_argument/in/foo/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/init/with_argument/mod.rs b/tests/testsuite/init/with_argument/mod.rs new file mode 100644 index 0000000..0b5e342 --- /dev/null +++ b/tests/testsuite/init/with_argument/mod.rs @@ -0,0 +1,21 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = &project.root(); + + snapbox::cmd::Command::cargo_ui() + .arg_line("init foo --vcs none") + .current_dir(project_root) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), project_root); +} diff --git a/tests/testsuite/init/with_argument/out/foo/Cargo.toml b/tests/testsuite/init/with_argument/out/foo/Cargo.toml new file mode 100644 index 0000000..1d9cfe3 --- /dev/null +++ b/tests/testsuite/init/with_argument/out/foo/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "foo" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/testsuite/init/with_argument/out/foo/src/main.rs b/tests/testsuite/init/with_argument/out/foo/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/tests/testsuite/init/with_argument/out/foo/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/tests/testsuite/init/with_argument/stderr.log b/tests/testsuite/init/with_argument/stderr.log new file mode 100644 index 0000000..3847e4e --- /dev/null +++ b/tests/testsuite/init/with_argument/stderr.log @@ -0,0 +1 @@ + Created binary (application) package diff --git a/tests/testsuite/init/with_argument/stdout.log b/tests/testsuite/init/with_argument/stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/install.rs b/tests/testsuite/install.rs new file mode 100644 index 0000000..b06f3d3 --- /dev/null +++ b/tests/testsuite/install.rs @@ -0,0 +1,2208 @@ +//! Tests for the `cargo install` command. + +use std::fs::{self, OpenOptions}; +use std::io::prelude::*; +use std::path::Path; + +use cargo_test_support::compare; +use cargo_test_support::cross_compile; +use cargo_test_support::git; +use cargo_test_support::registry::{self, registry_path, Package}; +use cargo_test_support::{ + basic_manifest, cargo_process, no_such_file_err_msg, project, project_in, symlink_supported, t, +}; +use cargo_util::ProcessError; + +use cargo_test_support::install::{ + assert_has_installed_exe, assert_has_not_installed_exe, cargo_home, +}; +use cargo_test_support::paths::{self, CargoPathExt}; +use std::env; +use std::path::PathBuf; + +fn pkg(name: &str, vers: &str) { + Package::new(name, vers) + .file("src/lib.rs", "") + .file( + "src/main.rs", + &format!("extern crate {}; fn main() {{}}", name), + ) + .publish(); +} + +#[cargo_test] +fn simple() { + pkg("foo", "0.0.1"); + + cargo_process("install foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.0.1 (registry [..]) +[INSTALLING] foo v0.0.1 +[COMPILING] foo v0.0.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); + + cargo_process("uninstall foo") + .with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]") + .run(); + assert_has_not_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn simple_with_message_format() { + pkg("foo", "0.0.1"); + + cargo_process("install foo --message-format=json") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.0.1 (registry [..]) +[INSTALLING] foo v0.0.1 +[COMPILING] foo v0.0.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .with_json( + r#" + { + "reason": "compiler-artifact", + "package_id": "foo 0.0.1 ([..])", + "manifest_path": "[..]", + "target": { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "foo", + "src_path": "[..]/foo-0.0.1/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + "profile": "{...}", + "features": [], + "filenames": "{...}", + "executable": null, + "fresh": false + } + + { + "reason": "compiler-artifact", + "package_id": "foo 0.0.1 ([..])", + "manifest_path": "[..]", + "target": { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "foo", + "src_path": "[..]/foo-0.0.1/src/main.rs", + "edition": "2015", + "doc": true, + "doctest": false, + "test": true + }, + "profile": "{...}", + "features": [], + "filenames": "{...}", + "executable": "[..]", + "fresh": false + } + + {"reason":"build-finished","success":true} + "#, + ) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn with_index() { + let registry = registry::init(); + pkg("foo", "0.0.1"); + + cargo_process("install foo --index") + .arg(registry.index_url().as_str()) + .with_stderr(&format!( + "\ +[UPDATING] `{reg}` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.0.1 (registry `{reg}`) +[INSTALLING] foo v0.0.1 (registry `{reg}`) +[COMPILING] foo v0.0.1 (registry `{reg}`) +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v0.0.1 (registry `{reg}`)` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + reg = registry_path().to_str().unwrap() + )) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); + + cargo_process("uninstall foo") + .with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]") + .run(); + assert_has_not_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn multiple_pkgs() { + pkg("foo", "0.0.1"); + pkg("bar", "0.0.2"); + + cargo_process("install foo bar baz") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.2 (registry `dummy-registry`) +[ERROR] could not find `baz` in registry `[..]` with version `*` +[INSTALLING] foo v0.0.1 +[COMPILING] foo v0.0.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) +[INSTALLING] bar v0.0.2 +[COMPILING] bar v0.0.2 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/bar[EXE] +[INSTALLED] package `bar v0.0.2` (executable `bar[EXE]`) +[SUMMARY] Successfully installed foo, bar! Failed to install baz (see error(s) above). +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +[ERROR] some crates failed to install +", + ) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); + assert_has_installed_exe(cargo_home(), "bar"); + + cargo_process("uninstall foo bar") + .with_stderr( + "\ +[REMOVING] [CWD]/home/.cargo/bin/foo[EXE] +[REMOVING] [CWD]/home/.cargo/bin/bar[EXE] +[SUMMARY] Successfully uninstalled foo, bar! +", + ) + .run(); + + assert_has_not_installed_exe(cargo_home(), "foo"); + assert_has_not_installed_exe(cargo_home(), "bar"); +} + +fn path() -> Vec { + env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect() +} + +#[cargo_test] +fn multiple_pkgs_path_set() { + // confirm partial failure results in 101 status code and does not have the + // '[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries' + // even if CARGO_HOME/bin is in the PATH + pkg("foo", "0.0.1"); + pkg("bar", "0.0.2"); + + // add CARGO_HOME/bin to path + let mut path = path(); + path.push(cargo_home().join("bin")); + let new_path = env::join_paths(path).unwrap(); + cargo_process("install foo bar baz") + .env("PATH", new_path) + .with_status(101) + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.2 (registry `dummy-registry`) +[ERROR] could not find `baz` in registry `[..]` with version `*` +[INSTALLING] foo v0.0.1 +[COMPILING] foo v0.0.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`) +[INSTALLING] bar v0.0.2 +[COMPILING] bar v0.0.2 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/bar[EXE] +[INSTALLED] package `bar v0.0.2` (executable `bar[EXE]`) +[SUMMARY] Successfully installed foo, bar! Failed to install baz (see error(s) above). +[ERROR] some crates failed to install +", + ) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); + assert_has_installed_exe(cargo_home(), "bar"); + + cargo_process("uninstall foo bar") + .with_stderr( + "\ +[REMOVING] [CWD]/home/.cargo/bin/foo[EXE] +[REMOVING] [CWD]/home/.cargo/bin/bar[EXE] +[SUMMARY] Successfully uninstalled foo, bar! +", + ) + .run(); + + assert_has_not_installed_exe(cargo_home(), "foo"); + assert_has_not_installed_exe(cargo_home(), "bar"); +} + +#[cargo_test] +fn pick_max_version() { + pkg("foo", "0.1.0"); + pkg("foo", "0.2.0"); + pkg("foo", "0.2.1"); + pkg("foo", "0.2.1-pre.1"); + pkg("foo", "0.3.0-pre.2"); + + cargo_process("install foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.2.1 (registry [..]) +[INSTALLING] foo v0.2.1 +[COMPILING] foo v0.2.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v0.2.1` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn installs_beta_version_by_explicit_name_from_git() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &basic_manifest("foo", "0.3.0-beta.1")) + .file("src/main.rs", "fn main() {}") + .build(); + + cargo_process("install --git") + .arg(p.url().to_string()) + .arg("foo") + .run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn missing() { + pkg("foo", "0.0.1"); + cargo_process("install bar") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +[ERROR] could not find `bar` in registry `[..]` with version `*` +", + ) + .run(); +} + +#[cargo_test] +fn missing_current_working_directory() { + cargo_process("install .") + .with_status(101) + .with_stderr( + "error: To install the binaries for the package in current working \ + directory use `cargo install --path .`. \n\ + Use `cargo build` if you want to simply build the package.", + ) + .run(); +} + +#[cargo_test] +fn bad_version() { + pkg("foo", "0.0.1"); + cargo_process("install foo --version=0.2.0") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +[ERROR] could not find `foo` in registry `[..]` with version `=0.2.0` +", + ) + .run(); +} + +#[cargo_test] +fn bad_paths() { + cargo_process("install") + .with_status(101) + .with_stderr("[ERROR] `[CWD]` is not a crate root; specify a crate to install [..]") + .run(); + + cargo_process("install --path .") + .with_status(101) + .with_stderr("[ERROR] `[CWD]` does not contain a Cargo.toml file[..]") + .run(); + + let toml = paths::root().join("Cargo.toml"); + fs::write(toml, "").unwrap(); + cargo_process("install --path Cargo.toml") + .with_status(101) + .with_stderr("[ERROR] `[CWD]/Cargo.toml` is not a directory[..]") + .run(); + + cargo_process("install --path .") + .with_status(101) + .with_stderr_contains("[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`") + .run(); +} + +#[cargo_test] +fn install_location_precedence() { + pkg("foo", "0.0.1"); + + let root = paths::root(); + let t1 = root.join("t1"); + let t2 = root.join("t2"); + let t3 = root.join("t3"); + let t4 = cargo_home(); + + fs::create_dir(root.join(".cargo")).unwrap(); + fs::write( + root.join(".cargo/config"), + &format!( + "[install] + root = '{}' + ", + t3.display() + ), + ) + .unwrap(); + + println!("install --root"); + + cargo_process("install foo --root") + .arg(&t1) + .env("CARGO_INSTALL_ROOT", &t2) + .run(); + assert_has_installed_exe(&t1, "foo"); + assert_has_not_installed_exe(&t2, "foo"); + + println!("install CARGO_INSTALL_ROOT"); + + cargo_process("install foo") + .env("CARGO_INSTALL_ROOT", &t2) + .run(); + assert_has_installed_exe(&t2, "foo"); + assert_has_not_installed_exe(&t3, "foo"); + + println!("install install.root"); + + cargo_process("install foo").run(); + assert_has_installed_exe(&t3, "foo"); + assert_has_not_installed_exe(&t4, "foo"); + + fs::remove_file(root.join(".cargo/config")).unwrap(); + + println!("install cargo home"); + + cargo_process("install foo").run(); + assert_has_installed_exe(&t4, "foo"); +} + +#[cargo_test] +fn install_path() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + cargo_process("install --path").arg(p.root()).run(); + assert_has_installed_exe(cargo_home(), "foo"); + // path-style installs force a reinstall + p.cargo("install --path .") + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 [..] +[FINISHED] release [..] +[REPLACING] [..]/.cargo/bin/foo[EXE] +[REPLACED] package `foo v0.0.1 [..]` with `foo v0.0.1 [..]` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); +} + +#[cargo_test] +fn install_target_dir() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + p.cargo("install --target-dir td_test") + .with_stderr( + "\ +[WARNING] Using `cargo install` [..] +[INSTALLING] foo v0.0.1 [..] +[COMPILING] foo v0.0.1 [..] +[FINISHED] release [..] +[INSTALLING] [..]foo[EXE] +[INSTALLED] package `foo v0.0.1 [..]foo[..]` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); + + let mut path = p.root(); + path.push("td_test"); + assert!(path.exists()); + + #[cfg(not(windows))] + path.push("release/foo"); + #[cfg(windows)] + path.push("release/foo.exe"); + assert!(path.exists()); +} + +#[cargo_test] +#[cfg(target_os = "linux")] +fn install_path_with_lowercase_cargo_toml() { + let toml = paths::root().join("cargo.toml"); + fs::write(toml, "").unwrap(); + + cargo_process("install --path .") + .with_status(101) + .with_stderr( + "\ +[ERROR] `[CWD]` does not contain a Cargo.toml file, \ +but found cargo.toml please try to rename it to Cargo.toml. --path must point to a directory containing a Cargo.toml file. +", + ) + .run(); +} + +#[cargo_test] +fn install_relative_path_outside_current_ws() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [workspace] + members = ["baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + authors = [] + edition = "2021" + + [dependencies] + foo = "1" + "#, + ) + .file("baz/src/lib.rs", "") + .build(); + + let _bin_project = project_in("bar") + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("install --path ../bar/foo") + .with_stderr(&format!( + "\ +[INSTALLING] foo v0.0.1 ([..]/bar/foo) +[COMPILING] foo v0.0.1 ([..]/bar/foo) +[FINISHED] release [..] +[INSTALLING] {home}/bin/foo[EXE] +[INSTALLED] package `foo v0.0.1 ([..]/bar/foo)` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + home = cargo_home().display(), + )) + .run(); + + // Validate the workspace error message to display available targets. + p.cargo("install --path ../bar/foo --bin") + .with_status(101) + .with_stderr( + "\ +[ERROR] \"--bin\" takes one argument. +Available binaries: + foo + +", + ) + .run(); +} + +#[cargo_test] +fn multiple_crates_error() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("a/src/main.rs", "fn main() {}") + .build(); + + cargo_process("install --git") + .arg(p.url().to_string()) + .with_status(101) + .with_stderr( + "\ +[UPDATING] git repository [..] +[ERROR] multiple packages with binaries found: bar, foo. \ +When installing a git repository, cargo will always search the entire repo for any Cargo.toml. \ +Please specify which to install. +", + ) + .run(); +} + +#[cargo_test] +fn multiple_crates_select() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("a/src/main.rs", "fn main() {}") + .build(); + + cargo_process("install --git") + .arg(p.url().to_string()) + .arg("foo") + .run(); + assert_has_installed_exe(cargo_home(), "foo"); + assert_has_not_installed_exe(cargo_home(), "bar"); + + cargo_process("install --git") + .arg(p.url().to_string()) + .arg("bar") + .run(); + assert_has_installed_exe(cargo_home(), "bar"); +} + +#[cargo_test] +fn multiple_crates_git_all() { + let p = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bin1", "bin2"] + "#, + ) + .file("bin1/Cargo.toml", &basic_manifest("bin1", "0.1.0")) + .file("bin2/Cargo.toml", &basic_manifest("bin2", "0.1.0")) + .file( + "bin1/src/main.rs", + r#"fn main() { println!("Hello, world!"); }"#, + ) + .file( + "bin2/src/main.rs", + r#"fn main() { println!("Hello, world!"); }"#, + ) + .build(); + + cargo_process(&format!("install --git {} bin1 bin2", p.url().to_string())).run(); +} + +#[cargo_test] +fn multiple_crates_auto_binaries() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "a" } + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() {}") + .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("a/src/lib.rs", "") + .build(); + + cargo_process("install --path").arg(p.root()).run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn multiple_crates_auto_examples() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "a" } + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file( + "examples/foo.rs", + " + extern crate bar; + extern crate foo; + fn main() {} + ", + ) + .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("a/src/lib.rs", "") + .build(); + + cargo_process("install --path") + .arg(p.root()) + .arg("--example=foo") + .run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn no_binaries_or_examples() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "a" } + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("a/src/lib.rs", "") + .build(); + + cargo_process("install --path") + .arg(p.root()) + .with_status(101) + .with_stderr("[ERROR] no packages found with binaries or examples") + .run(); +} + +#[cargo_test] +fn no_binaries() { + let p = project() + .file("src/lib.rs", "") + .file("examples/foo.rs", "fn main() {}") + .build(); + + cargo_process("install --path") + .arg(p.root()) + .arg("foo") + .with_status(101) + .with_stderr( + "\ +[ERROR] there is nothing to install in `foo v0.0.1 ([..])`, because it has no binaries[..] +[..] +To use a library crate, add it as a dependency to a Cargo project with `cargo add`.", + ) + .run(); +} + +#[cargo_test] +fn examples() { + let p = project() + .file("src/lib.rs", "") + .file("examples/foo.rs", "extern crate foo; fn main() {}") + .build(); + + cargo_process("install --path") + .arg(p.root()) + .arg("--example=foo") + .run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn install_force() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + cargo_process("install --path").arg(p.root()).run(); + + let p = project() + .at("foo2") + .file("Cargo.toml", &basic_manifest("foo", "0.2.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + cargo_process("install --force --path") + .arg(p.root()) + .with_stderr( + "\ +[INSTALLING] foo v0.2.0 ([..]) +[COMPILING] foo v0.2.0 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [CWD]/home/.cargo/bin/foo[EXE] +[REPLACED] package `foo v0.0.1 ([..]/foo)` with `foo v0.2.0 ([..]/foo2)` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + + cargo_process("install --list") + .with_stdout( + "\ +foo v0.2.0 ([..]): + foo[..] +", + ) + .run(); +} + +#[cargo_test] +fn install_force_partial_overlap() { + let p = project() + .file("src/bin/foo-bin1.rs", "fn main() {}") + .file("src/bin/foo-bin2.rs", "fn main() {}") + .build(); + + cargo_process("install --path").arg(p.root()).run(); + + let p = project() + .at("foo2") + .file("Cargo.toml", &basic_manifest("foo", "0.2.0")) + .file("src/bin/foo-bin2.rs", "fn main() {}") + .file("src/bin/foo-bin3.rs", "fn main() {}") + .build(); + + cargo_process("install --force --path") + .arg(p.root()) + .with_stderr( + "\ +[INSTALLING] foo v0.2.0 ([..]) +[COMPILING] foo v0.2.0 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/foo-bin3[EXE] +[REPLACING] [CWD]/home/.cargo/bin/foo-bin2[EXE] +[REMOVING] executable `[..]/bin/foo-bin1[EXE]` from previous version foo v0.0.1 [..] +[INSTALLED] package `foo v0.2.0 ([..]/foo2)` (executable `foo-bin3[EXE]`) +[REPLACED] package `foo v0.0.1 ([..]/foo)` with `foo v0.2.0 ([..]/foo2)` (executable `foo-bin2[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + + cargo_process("install --list") + .with_stdout( + "\ +foo v0.2.0 ([..]): + foo-bin2[..] + foo-bin3[..] +", + ) + .run(); +} + +#[cargo_test] +fn install_force_bin() { + let p = project() + .file("src/bin/foo-bin1.rs", "fn main() {}") + .file("src/bin/foo-bin2.rs", "fn main() {}") + .build(); + + cargo_process("install --path").arg(p.root()).run(); + + let p = project() + .at("foo2") + .file("Cargo.toml", &basic_manifest("foo", "0.2.0")) + .file("src/bin/foo-bin1.rs", "fn main() {}") + .file("src/bin/foo-bin2.rs", "fn main() {}") + .build(); + + cargo_process("install --force --bin foo-bin2 --path") + .arg(p.root()) + .with_stderr( + "\ +[INSTALLING] foo v0.2.0 ([..]) +[COMPILING] foo v0.2.0 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [CWD]/home/.cargo/bin/foo-bin2[EXE] +[REPLACED] package `foo v0.0.1 ([..]/foo)` with `foo v0.2.0 ([..]/foo2)` (executable `foo-bin2[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + + cargo_process("install --list") + .with_stdout( + "\ +foo v0.0.1 ([..]): + foo-bin1[..] +foo v0.2.0 ([..]): + foo-bin2[..] +", + ) + .run(); +} + +#[cargo_test] +fn compile_failure() { + let p = project().file("src/main.rs", "").build(); + + cargo_process("install --path") + .arg(p.root()) + .with_status(101) + .with_stderr_contains( + "\ +[ERROR] could not compile `foo` due to previous error +[ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be \ + found at `[..]target` +", + ) + .run(); +} + +#[cargo_test] +fn git_repo() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + // Use `--locked` to test that we don't even try to write a lock file. + cargo_process("install --locked --git") + .arg(p.url().to_string()) + .with_stderr( + "\ +[UPDATING] git repository `[..]` +[WARNING] no Cargo.lock file published in foo v0.1.0 ([..]) +[INSTALLING] foo v0.1.0 ([..]) +[COMPILING] foo v0.1.0 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v0.1.0 ([..]/foo#[..])` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +#[cfg(target_os = "linux")] +fn git_repo_with_lowercase_cargo_toml() { + let p = git::repo(&paths::root().join("foo")) + .file("cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + cargo_process("install --git") + .arg(p.url().to_string()) + .with_status(101) + .with_stderr( + "\ +[UPDATING] git repository [..] +[ERROR] Could not find Cargo.toml in `[..]`, but found cargo.toml please try to rename it to Cargo.toml +", + ) + .run(); +} + +#[cargo_test] +fn list() { + pkg("foo", "0.0.1"); + pkg("bar", "0.2.1"); + pkg("bar", "0.2.2"); + + cargo_process("install --list").with_stdout("").run(); + + cargo_process("install bar --version =0.2.1").run(); + cargo_process("install foo").run(); + cargo_process("install --list") + .with_stdout( + "\ +bar v0.2.1: + bar[..] +foo v0.0.1: + foo[..] +", + ) + .run(); +} + +#[cargo_test] +fn list_error() { + pkg("foo", "0.0.1"); + cargo_process("install foo").run(); + cargo_process("install --list") + .with_stdout( + "\ +foo v0.0.1: + foo[..] +", + ) + .run(); + let mut worldfile_path = cargo_home(); + worldfile_path.push(".crates.toml"); + let mut worldfile = OpenOptions::new() + .write(true) + .open(worldfile_path) + .expect(".crates.toml should be there"); + worldfile.write_all(b"\x00").unwrap(); + drop(worldfile); + cargo_process("install --list --verbose") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse crate metadata at `[..]` + +Caused by: + invalid TOML found for metadata + +Caused by: + TOML parse error at line 1, column 1 + | + 1 | [..] + | ^ + invalid key +", + ) + .run(); +} + +#[cargo_test] +fn uninstall_pkg_does_not_exist() { + cargo_process("uninstall foo") + .with_status(101) + .with_stderr("[ERROR] package ID specification `foo` did not match any packages") + .run(); +} + +#[cargo_test] +fn uninstall_bin_does_not_exist() { + pkg("foo", "0.0.1"); + + cargo_process("install foo").run(); + cargo_process("uninstall foo --bin=bar") + .with_status(101) + .with_stderr("[ERROR] binary `bar[..]` not installed as part of `foo v0.0.1`") + .run(); +} + +#[cargo_test] +fn uninstall_piecemeal() { + let p = project() + .file("src/bin/foo.rs", "fn main() {}") + .file("src/bin/bar.rs", "fn main() {}") + .build(); + + cargo_process("install --path").arg(p.root()).run(); + assert_has_installed_exe(cargo_home(), "foo"); + assert_has_installed_exe(cargo_home(), "bar"); + + cargo_process("uninstall foo --bin=bar") + .with_stderr("[REMOVING] [..]bar[..]") + .run(); + + assert_has_installed_exe(cargo_home(), "foo"); + assert_has_not_installed_exe(cargo_home(), "bar"); + + cargo_process("uninstall foo --bin=foo") + .with_stderr("[REMOVING] [..]foo[..]") + .run(); + assert_has_not_installed_exe(cargo_home(), "foo"); + + cargo_process("uninstall foo") + .with_status(101) + .with_stderr("[ERROR] package ID specification `foo` did not match any packages") + .run(); +} + +#[cargo_test] +fn subcommand_works_out_of_the_box() { + Package::new("cargo-foo", "1.0.0") + .file("src/main.rs", r#"fn main() { println!("bar"); }"#) + .publish(); + cargo_process("install cargo-foo").run(); + cargo_process("foo").with_stdout("bar\n").run(); + cargo_process("--list") + .with_stdout_contains(" foo\n") + .run(); +} + +#[cargo_test] +fn installs_from_cwd_by_default() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + p.cargo("install") + .with_stderr_contains( + "warning: Using `cargo install` to install the binaries for the \ + package in current working directory is deprecated, \ + use `cargo install --path .` instead. \ + Use `cargo build` if you want to simply build the package.", + ) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn installs_from_cwd_with_2018_warnings() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + edition = "2018" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("install") + .with_status(101) + .with_stderr_contains( + "error: Using `cargo install` to install the binaries for the \ + package in current working directory is no longer supported, \ + use `cargo install --path .` instead. \ + Use `cargo build` if you want to simply build the package.", + ) + .run(); + assert_has_not_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn uninstall_cwd() { + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("install --path .") + .with_stderr(&format!( + "\ +[INSTALLING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] {home}/bin/foo[EXE] +[INSTALLED] package `foo v0.0.1 ([..]/foo)` (executable `foo[EXE]`) +[WARNING] be sure to add `{home}/bin` to your PATH to be able to run the installed binaries", + home = cargo_home().display(), + )) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); + + p.cargo("uninstall") + .with_stdout("") + .with_stderr(&format!( + "[REMOVING] {home}/bin/foo[EXE]", + home = cargo_home().display() + )) + .run(); + assert_has_not_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn uninstall_cwd_not_installed() { + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("uninstall") + .with_status(101) + .with_stdout("") + .with_stderr("error: package `foo v0.0.1 ([CWD])` is not installed") + .run(); +} + +#[cargo_test] +fn uninstall_cwd_no_project() { + cargo_process("uninstall") + .with_status(101) + .with_stdout("") + .with_stderr(format!( + "\ +[ERROR] failed to read `[CWD]/Cargo.toml` + +Caused by: + {err_msg}", + err_msg = no_such_file_err_msg(), + )) + .run(); +} + +#[cargo_test] +fn do_not_rebuilds_on_local_install() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + p.cargo("build --release").run(); + cargo_process("install --path") + .arg(p.root()) + .with_stderr( + "\ +[INSTALLING] [..] +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..] +[INSTALLED] package `foo v0.0.1 ([..]/foo)` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + + assert!(p.build_dir().exists()); + assert!(p.release_bin("foo").exists()); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn reports_unsuccessful_subcommand_result() { + Package::new("cargo-fail", "1.0.0") + .file("src/main.rs", "fn main() { panic!(); }") + .publish(); + cargo_process("install cargo-fail").run(); + cargo_process("--list") + .with_stdout_contains(" fail\n") + .run(); + cargo_process("fail") + .with_status(101) + .with_stderr_contains("thread '[..]' panicked at 'explicit panic', [..]") + .run(); +} + +#[cargo_test] +fn git_with_lockfile() { + let p = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "fn main() {}") + .file( + "Cargo.lock", + r#" + [[package]] + name = "foo" + version = "0.1.0" + dependencies = [ "bar 0.1.0" ] + + [[package]] + name = "bar" + version = "0.1.0" + "#, + ) + .build(); + + cargo_process("install --git") + .arg(p.url().to_string()) + .run(); +} + +#[cargo_test] +fn q_silences_warnings() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + cargo_process("install -q --path") + .arg(p.root()) + .with_stderr("") + .run(); +} + +#[cargo_test] +fn readonly_dir() { + pkg("foo", "0.0.1"); + + let root = paths::root(); + let dir = &root.join("readonly"); + fs::create_dir(root.join("readonly")).unwrap(); + let mut perms = fs::metadata(dir).unwrap().permissions(); + perms.set_readonly(true); + fs::set_permissions(dir, perms).unwrap(); + + cargo_process("install foo").cwd(dir).run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn use_path_workspace() { + Package::new("foo", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [workspace] + members = ["baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + authors = [] + + [dependencies] + foo = "1" + "#, + ) + .file("baz/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + let lock = p.read_lockfile(); + p.cargo("install").run(); + let lock2 = p.read_lockfile(); + assert_eq!(lock, lock2, "different lockfiles"); +} + +#[cargo_test] +fn path_install_workspace_root_despite_default_members() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "ws-root" + version = "0.1.0" + authors = [] + + [workspace] + members = ["ws-member"] + default-members = ["ws-member"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "ws-member/Cargo.toml", + r#" + [package] + name = "ws-member" + version = "0.1.0" + authors = [] + "#, + ) + .file("ws-member/src/main.rs", "fn main() {}") + .build(); + + p.cargo("install --path") + .arg(p.root()) + .arg("ws-root") + .with_stderr_contains( + "[INSTALLED] package `ws-root v0.1.0 ([..])` (executable `ws-root[EXE]`)", + ) + // Particularly avoid "Installed package `ws-root v0.1.0 ([..]])` (executable `ws-member`)": + .with_stderr_does_not_contain("ws-member") + .run(); +} + +#[cargo_test] +fn dev_dependencies_no_check() { + Package::new("foo", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dev-dependencies] + baz = "1.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr_contains("[..] no matching package named `baz` found") + .run(); + p.cargo("install").run(); +} + +#[cargo_test] +fn dev_dependencies_lock_file_untouched() { + Package::new("foo", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dev-dependencies] + bar = { path = "a" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("a/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + let lock = p.read_lockfile(); + p.cargo("install").run(); + let lock2 = p.read_lockfile(); + assert!(lock == lock2, "different lockfiles"); +} + +#[cargo_test] +fn install_target_native() { + pkg("foo", "0.1.0"); + + cargo_process("install foo --target") + .arg(cargo_test_support::rustc_host()) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn install_target_foreign() { + if cross_compile::disabled() { + return; + } + + pkg("foo", "0.1.0"); + + cargo_process("install foo --target") + .arg(cross_compile::alternate()) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn vers_precise() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + cargo_process("install foo --vers 0.1.1") + .with_stderr_contains("[DOWNLOADED] foo v0.1.1 (registry [..])") + .run(); +} + +#[cargo_test] +fn version_precise() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + cargo_process("install foo --version 0.1.1") + .with_stderr_contains("[DOWNLOADED] foo v0.1.1 (registry [..])") + .run(); +} + +#[cargo_test] +fn inline_version_precise() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + cargo_process("install foo@0.1.1") + .with_stderr_contains("[DOWNLOADED] foo v0.1.1 (registry [..])") + .run(); +} + +#[cargo_test] +fn inline_version_multiple() { + pkg("foo", "0.1.0"); + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + pkg("bar", "0.2.0"); + pkg("bar", "0.2.1"); + pkg("bar", "0.2.2"); + + cargo_process("install foo@0.1.1 bar@0.2.1") + .with_stderr_contains("[DOWNLOADED] foo v0.1.1 (registry [..])") + .with_stderr_contains("[DOWNLOADED] bar v0.2.1 (registry [..])") + .run(); +} + +#[cargo_test] +fn inline_version_without_name() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + cargo_process("install @0.1.1") + .with_status(101) + .with_stderr("error: missing crate name for `@0.1.1`") + .run(); +} + +#[cargo_test] +fn inline_and_explicit_version() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + cargo_process("install foo@0.1.1 --version 0.1.1") + .with_status(101) + .with_stderr("error: cannot specify both `@0.1.1` and `--version`") + .run(); +} + +#[cargo_test] +fn not_both_vers_and_version() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + cargo_process("install foo --version 0.1.1 --vers 0.1.2") + .with_status(1) + .with_stderr_contains( + "\ +[ERROR] the argument '--version ' cannot be used multiple times +", + ) + .run(); +} + +#[cargo_test] +fn test_install_git_cannot_be_a_base_url() { + cargo_process("install --git github.com:rust-lang/rustfmt.git") + .with_status(101) + .with_stderr( + "\ +[ERROR] invalid url `github.com:rust-lang/rustfmt.git`: cannot-be-a-base-URLs are not supported", + ) + .run(); +} + +#[cargo_test] +fn uninstall_multiple_and_specifying_bin() { + cargo_process("uninstall foo bar --bin baz") + .with_status(101) + .with_stderr("\ +[ERROR] A binary can only be associated with a single installed package, specifying multiple specs with --bin is redundant.") + .run(); +} + +#[cargo_test] +fn uninstall_with_empty_package_option() { + cargo_process("uninstall -p") + .with_status(101) + .with_stderr( + "\ +[ERROR] \"--package \" requires a SPEC format value. +Run `cargo help pkgid` for more information about SPEC format. +", + ) + .run(); +} + +#[cargo_test] +fn uninstall_multiple_and_some_pkg_does_not_exist() { + pkg("foo", "0.0.1"); + + cargo_process("install foo").run(); + + cargo_process("uninstall foo bar") + .with_status(101) + .with_stderr( + "\ +[REMOVING] [CWD]/home/.cargo/bin/foo[EXE] +error: package ID specification `bar` did not match any packages +[SUMMARY] Successfully uninstalled foo! Failed to uninstall bar (see error(s) above). +error: some packages failed to uninstall +", + ) + .run(); + + assert_has_not_installed_exe(cargo_home(), "foo"); + assert_has_not_installed_exe(cargo_home(), "bar"); +} + +#[cargo_test] +fn custom_target_dir_for_git_source() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + cargo_process("install --git") + .arg(p.url().to_string()) + .run(); + assert!(!paths::root().join("target/release").is_dir()); + + cargo_process("install --force --git") + .arg(p.url().to_string()) + .env("CARGO_TARGET_DIR", "target") + .run(); + assert!(paths::root().join("target/release").is_dir()); +} + +#[cargo_test] +fn install_respects_lock_file() { + // `cargo install` now requires --locked to use a Cargo.lock. + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.1.1") + .file("src/lib.rs", "not rust") + .publish(); + Package::new("foo", "0.1.0") + .dep("bar", "0.1") + .file("src/lib.rs", "") + .file( + "src/main.rs", + "extern crate foo; extern crate bar; fn main() {}", + ) + .file( + "Cargo.lock", + r#" +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foo" +version = "0.1.0" +dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] +"#, + ) + .publish(); + + cargo_process("install foo") + .with_stderr_contains("[..]not rust[..]") + .with_status(101) + .run(); + cargo_process("install --locked foo").run(); +} + +#[cargo_test] +fn install_path_respects_lock_file() { + // --path version of install_path_respects_lock_file, --locked is required + // to use Cargo.lock. + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.1.1") + .file("src/lib.rs", "not rust") + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() {}") + .file( + "Cargo.lock", + r#" +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foo" +version = "0.1.0" +dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] +"#, + ) + .build(); + + p.cargo("install --path .") + .with_stderr_contains("[..]not rust[..]") + .with_status(101) + .run(); + p.cargo("install --path . --locked").run(); +} + +#[cargo_test] +fn lock_file_path_deps_ok() { + Package::new("bar", "0.1.0").publish(); + + Package::new("foo", "0.1.0") + .dep("bar", "0.1") + .file("src/lib.rs", "") + .file( + "src/main.rs", + "extern crate foo; extern crate bar; fn main() {}", + ) + .file( + "Cargo.lock", + r#" + [[package]] + name = "bar" + version = "0.1.0" + + [[package]] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar 0.1.0", + ] + "#, + ) + .publish(); + + cargo_process("install foo").run(); +} + +#[cargo_test] +fn install_empty_argument() { + // Bug 5229 + cargo_process("install") + .arg("") + .with_status(1) + .with_stderr_contains("[ERROR] a value is required for '[crate]...' but none was supplied") + .run(); +} + +#[cargo_test] +fn git_repo_replace() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + let repo = git2::Repository::open(&p.root()).unwrap(); + let old_rev = repo.revparse_single("HEAD").unwrap().id(); + cargo_process("install --git") + .arg(p.url().to_string()) + .run(); + git::commit(&repo); + let new_rev = repo.revparse_single("HEAD").unwrap().id(); + let mut path = paths::home(); + path.push(".cargo/.crates.toml"); + + assert_ne!(old_rev, new_rev); + assert!(fs::read_to_string(path.clone()) + .unwrap() + .contains(&format!("{}", old_rev))); + cargo_process("install --force --git") + .arg(p.url().to_string()) + .run(); + assert!(fs::read_to_string(path) + .unwrap() + .contains(&format!("{}", new_rev))); +} + +#[cargo_test] +fn workspace_uses_workspace_target_dir() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + + [dependencies] + bar = { path = 'bar' } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --release").cwd("bar").run(); + cargo_process("install --path") + .arg(p.root().join("bar")) + .with_stderr( + "[INSTALLING] [..] +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..] +[INSTALLED] package `bar v0.1.0 ([..]/bar)` (executable `bar[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); +} + +#[cargo_test] +fn install_ignores_local_cargo_config() { + pkg("bar", "0.0.1"); + + let p = project() + .file( + ".cargo/config", + r#" + [build] + target = "non-existing-target" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("install bar").run(); + assert_has_installed_exe(cargo_home(), "bar"); +} + +#[cargo_test] +fn install_ignores_unstable_table_in_local_cargo_config() { + pkg("bar", "0.0.1"); + + let p = project() + .file( + ".cargo/config", + r#" + [unstable] + build-std = ["core"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("install bar") + .masquerade_as_nightly_cargo(&["build-std"]) + .run(); + assert_has_installed_exe(cargo_home(), "bar"); +} + +#[cargo_test] +fn install_global_cargo_config() { + pkg("bar", "0.0.1"); + + let config = cargo_home().join("config"); + let mut toml = fs::read_to_string(&config).unwrap_or_default(); + + toml.push_str( + r#" + [build] + target = 'nonexistent' + "#, + ); + fs::write(&config, toml).unwrap(); + + cargo_process("install bar") + .with_status(101) + .with_stderr_contains("[..]--target nonexistent[..]") + .run(); +} + +#[cargo_test] +fn install_path_config() { + project() + .file( + ".cargo/config", + r#" + [build] + target = 'nonexistent' + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + cargo_process("install --path foo") + .with_status(101) + .with_stderr_contains("[..]--target nonexistent[..]") + .run(); +} + +#[cargo_test] +fn install_version_req() { + // Try using a few versionreq styles. + pkg("foo", "0.0.3"); + pkg("foo", "1.0.4"); + pkg("foo", "1.0.5"); + cargo_process("install foo --version=*") + .with_stderr_does_not_contain("[WARNING][..]is not a valid semver[..]") + .with_stderr_contains("[INSTALLING] foo v1.0.5") + .run(); + cargo_process("uninstall foo").run(); + cargo_process("install foo --version=^1.0") + .with_stderr_does_not_contain("[WARNING][..]is not a valid semver[..]") + .with_stderr_contains("[INSTALLING] foo v1.0.5") + .run(); + cargo_process("uninstall foo").run(); + cargo_process("install foo --version=0.0.*") + .with_stderr_does_not_contain("[WARNING][..]is not a valid semver[..]") + .with_stderr_contains("[INSTALLING] foo v0.0.3") + .run(); +} + +#[cargo_test] +fn git_install_reads_workspace_manifest() { + let p = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bin1"] + + [profile.release] + incremental = 3 + "#, + ) + .file("bin1/Cargo.toml", &basic_manifest("bin1", "0.1.0")) + .file( + "bin1/src/main.rs", + r#"fn main() { println!("Hello, world!"); }"#, + ) + .build(); + + cargo_process(&format!("install --git {}", p.url().to_string())) + .with_status(101) + .with_stderr_contains(" invalid type: integer `3`[..]") + .run(); +} + +#[cargo_test] +fn install_git_with_symlink_home() { + // Ensure that `cargo install` with a git repo is OK when CARGO_HOME is a + // symlink, and uses an build script. + if !symlink_supported() { + return; + } + let p = git::new("foo", |p| { + p.file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + // This triggers discover_git_and_list_files for detecting changed files. + .file("build.rs", "fn main() {}") + }); + #[cfg(unix)] + use std::os::unix::fs::symlink; + #[cfg(windows)] + use std::os::windows::fs::symlink_dir as symlink; + + let actual = paths::root().join("actual-home"); + t!(std::fs::create_dir(&actual)); + t!(symlink(&actual, paths::home().join(".cargo"))); + cargo_process("install --git") + .arg(p.url().to_string()) + .with_stderr( + "\ +[UPDATING] git repository [..] +[INSTALLING] foo v1.0.0 [..] +[COMPILING] foo v1.0.0 [..] +[FINISHED] [..] +[INSTALLING] [..]home/.cargo/bin/foo[..] +[INSTALLED] package `foo [..] +[WARNING] be sure to add [..] +", + ) + .run(); +} + +#[cargo_test] +fn install_yanked_cargo_package() { + Package::new("baz", "0.0.1").yanked(true).publish(); + cargo_process("install baz --version 0.0.1") + .with_status(101) + .with_stderr_contains( + "\ +[ERROR] cannot install package `baz`, it has been yanked from registry `crates-io` +", + ) + .run(); +} + +#[cargo_test] +fn install_cargo_package_in_a_patched_workspace() { + pkg("foo", "0.1.0"); + pkg("fizz", "1.0.0"); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [workspace] + members = ["baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + authors = [] + + [dependencies] + fizz = "1" + + [patch.crates-io] + fizz = { version = "=1.0.0" } + "#, + ) + .file("baz/src/lib.rs", "") + .build(); + + let stderr = "\ +[WARNING] patch for the non root package will be ignored, specify patch at the workspace root: +package: [..]/foo/baz/Cargo.toml +workspace: [..]/foo/Cargo.toml +"; + p.cargo("check").with_stderr_contains(&stderr).run(); + + // A crate installation must not emit any message from a workspace under + // current working directory. + // See https://github.com/rust-lang/cargo/issues/8619 + p.cargo("install foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.1.0 (registry [..]) +[INSTALLING] foo v0.1.0 +[COMPILING] foo v0.1.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]foo[EXE] +[INSTALLED] package `foo v0.1.0` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn locked_install_without_published_lockfile() { + Package::new("foo", "0.1.0") + .file("src/main.rs", "//! Some docs\nfn main() {}") + .publish(); + + cargo_process("install foo --locked") + .with_stderr_contains("[WARNING] no Cargo.lock file published in foo v0.1.0") + .run(); +} + +#[cargo_test] +fn install_semver_metadata() { + // Check trying to install a package that uses semver metadata. + // This uses alt registry because the bug this is exercising doesn't + // trigger with a replaced source. + registry::alt_init(); + Package::new("foo", "1.0.0+abc") + .alternative(true) + .file("src/main.rs", "fn main() {}") + .publish(); + + cargo_process("install foo --registry alternative --version 1.0.0+abc").run(); + cargo_process("install foo --registry alternative") + .with_stderr("\ +[UPDATING] `alternative` index +[IGNORED] package `foo v1.0.0+abc (registry `alternative`)` is already installed, use --force to override +[WARNING] be sure to add [..] +") + .run(); + // "Updating" is not displayed here due to the --version fast-path. + cargo_process("install foo --registry alternative --version 1.0.0+abc") + .with_stderr("\ +[IGNORED] package `foo v1.0.0+abc (registry `alternative`)` is already installed, use --force to override +[WARNING] be sure to add [..] +") + .run(); + cargo_process("install foo --registry alternative --version 1.0.0 --force") + .with_stderr( + "\ +[UPDATING] `alternative` index +[INSTALLING] foo v1.0.0+abc (registry `alternative`) +[COMPILING] foo v1.0.0+abc (registry `alternative`) +[FINISHED] [..] +[REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] +[REPLACED] package [..] +[WARNING] be sure to add [..] +", + ) + .run(); + // Check that from a fresh cache will work without metadata, too. + paths::home().join(".cargo/registry").rm_rf(); + paths::home().join(".cargo/bin").rm_rf(); + cargo_process("install foo --registry alternative --version 1.0.0") + .with_stderr( + "\ +[UPDATING] `alternative` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v1.0.0+abc (registry `alternative`) +[INSTALLING] foo v1.0.0+abc (registry `alternative`) +[COMPILING] foo v1.0.0+abc (registry `alternative`) +[FINISHED] [..] +[INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v1.0.0+abc (registry `alternative`)` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); +} + +#[cargo_test] +fn no_auto_fix_note() { + Package::new("auto_fix", "0.0.1") + .file("src/lib.rs", "use std::io;") + .file( + "src/main.rs", + &format!("extern crate {}; use std::io; fn main() {{}}", "auto_fix"), + ) + .publish(); + + // This should not contain a suggestion to run `cargo fix` + // + // This is checked by matching the full output as `with_stderr_does_not_contain` + // can be brittle + cargo_process("install auto_fix") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] auto_fix v0.0.1 (registry [..]) +[INSTALLING] auto_fix v0.0.1 +[COMPILING] auto_fix v0.0.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/auto_fix[EXE] +[INSTALLED] package `auto_fix v0.0.1` (executable `auto_fix[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + assert_has_installed_exe(cargo_home(), "auto_fix"); + + cargo_process("uninstall auto_fix") + .with_stderr("[REMOVING] [CWD]/home/.cargo/bin/auto_fix[EXE]") + .run(); + assert_has_not_installed_exe(cargo_home(), "auto_fix"); +} + +#[cargo_test] +fn failed_install_retains_temp_directory() { + // Verifies that the temporary directory persists after a build failure. + Package::new("foo", "0.0.1") + .file("src/main.rs", "x") + .publish(); + let err = cargo_process("install foo").exec_with_output().unwrap_err(); + let err = err.downcast::().unwrap(); + let stderr = String::from_utf8(err.stderr.unwrap()).unwrap(); + compare::match_contains( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) +[INSTALLING] foo v0.0.1 +[COMPILING] foo v0.0.1 +", + &stderr, + None, + ) + .unwrap(); + compare::match_contains( + "error: failed to compile `foo v0.0.1`, intermediate artifacts can be found at `[..]`", + &stderr, + None, + ) + .unwrap(); + + // Find the path in the output. + let start = stderr.find("found at `").unwrap() + 10; + let end = stderr[start..].find('\n').unwrap() - 1; + let path = Path::new(&stderr[start..(end + start)]); + assert!(path.exists()); + assert!(path.join("release/deps").exists()); +} + +#[cargo_test] +fn sparse_install() { + // Checks for an issue where uninstalling something corrupted + // the SourceIds of sparse registries. + // See https://github.com/rust-lang/cargo/issues/11751 + let _registry = registry::RegistryBuilder::new().http_index().build(); + + pkg("foo", "0.0.1"); + pkg("bar", "0.0.1"); + + cargo_process("install foo --registry dummy-registry") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.0.1 (registry `dummy-registry`) +[INSTALLING] foo v0.0.1 (registry `dummy-registry`) +[UPDATING] `dummy-registry` index +[COMPILING] foo v0.0.1 (registry `dummy-registry`) +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v0.0.1 (registry `dummy-registry`)` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + assert_has_installed_exe(cargo_home(), "foo"); + let assert_v1 = |expected| { + let v1 = fs::read_to_string(paths::home().join(".cargo/.crates.toml")).unwrap(); + compare::assert_match_exact(expected, &v1); + }; + assert_v1( + r#"[v1] +"foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"] +"#, + ); + cargo_process("install bar").run(); + assert_has_installed_exe(cargo_home(), "bar"); + assert_v1( + r#"[v1] +"bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = ["bar[EXE]"] +"foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"] +"#, + ); + + cargo_process("uninstall bar") + .with_stderr("[REMOVING] [CWD]/home/.cargo/bin/bar[EXE]") + .run(); + assert_has_not_installed_exe(cargo_home(), "bar"); + assert_v1( + r#"[v1] +"foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"] +"#, + ); + cargo_process("uninstall foo") + .with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]") + .run(); + assert_has_not_installed_exe(cargo_home(), "foo"); + assert_v1( + r#"[v1] +"#, + ); +} diff --git a/tests/testsuite/install_upgrade.rs b/tests/testsuite/install_upgrade.rs new file mode 100644 index 0000000..ae641ba --- /dev/null +++ b/tests/testsuite/install_upgrade.rs @@ -0,0 +1,862 @@ +//! Tests for `cargo install` where it upgrades a package if it is out-of-date. + +use cargo::core::PackageId; +use std::collections::BTreeSet; +use std::env; +use std::fs; +use std::path::PathBuf; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use cargo_test_support::install::{cargo_home, exe}; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::{self, Package}; +use cargo_test_support::{ + basic_manifest, cargo_process, cross_compile, execs, git, process, project, Execs, +}; + +fn pkg_maybe_yanked(name: &str, vers: &str, yanked: bool) { + Package::new(name, vers) + .yanked(yanked) + .file( + "src/main.rs", + r#"fn main() { println!("{}", env!("CARGO_PKG_VERSION")) }"#, + ) + .publish(); +} + +// Helper for publishing a package. +fn pkg(name: &str, vers: &str) { + pkg_maybe_yanked(name, vers, false) +} + +fn v1_path() -> PathBuf { + cargo_home().join(".crates.toml") +} + +fn v2_path() -> PathBuf { + cargo_home().join(".crates2.json") +} + +fn load_crates1() -> toml::Value { + toml::from_str(&fs::read_to_string(v1_path()).unwrap()).unwrap() +} + +fn load_crates2() -> serde_json::Value { + serde_json::from_str(&fs::read_to_string(v2_path()).unwrap()).unwrap() +} + +fn installed_exe(name: &str) -> PathBuf { + cargo_home().join("bin").join(exe(name)) +} + +/// Helper for executing binaries installed by cargo. +fn installed_process(name: &str) -> Execs { + static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + thread_local!(static UNIQUE_ID: usize = NEXT_ID.fetch_add(1, Ordering::SeqCst)); + + // This copies the executable to a unique name so that it may be safely + // replaced on Windows. See Project::rename_run for details. + let src = installed_exe(name); + let dst = installed_exe(&UNIQUE_ID.with(|my_id| format!("{}-{}", name, my_id))); + // Note: Cannot use copy. On Linux, file descriptors may be left open to + // the executable as other tests in other threads are constantly spawning + // new processes (see https://github.com/rust-lang/cargo/pull/5557 for + // more). + fs::rename(&src, &dst) + .unwrap_or_else(|e| panic!("Failed to rename `{:?}` to `{:?}`: {}", src, dst, e)); + // Leave behind a fake file so that reinstall duplicate check works. + fs::write(src, "").unwrap(); + let p = process(dst); + execs().with_process_builder(p) +} + +/// Check that the given package name/version has the following bins listed in +/// the trackers. Also verifies that both trackers are in sync and valid. +/// Pass in an empty `bins` list to assert that the package is *not* installed. +fn validate_trackers(name: &str, version: &str, bins: &[&str]) { + let v1 = load_crates1(); + let v1_table = v1.get("v1").unwrap().as_table().unwrap(); + let v2 = load_crates2(); + let v2_table = v2["installs"].as_object().unwrap(); + assert_eq!(v1_table.len(), v2_table.len()); + // Convert `bins` to a BTreeSet. + let bins: BTreeSet = bins + .iter() + .map(|b| format!("{}{}", b, env::consts::EXE_SUFFIX)) + .collect(); + // Check every entry matches between v1 and v2. + for (pkg_id_str, v1_bins) in v1_table { + let pkg_id: PackageId = toml::Value::from(pkg_id_str.to_string()) + .try_into() + .unwrap(); + let v1_bins: BTreeSet = v1_bins + .as_array() + .unwrap() + .iter() + .map(|b| b.as_str().unwrap().to_string()) + .collect(); + if pkg_id.name().as_str() == name && pkg_id.version().to_string() == version { + if bins.is_empty() { + panic!( + "Expected {} to not be installed, but found: {:?}", + name, v1_bins + ); + } else { + assert_eq!(bins, v1_bins); + } + } + let pkg_id_value = serde_json::to_value(&pkg_id).unwrap(); + let pkg_id_str = pkg_id_value.as_str().unwrap(); + let v2_info = v2_table + .get(pkg_id_str) + .expect("v2 missing v1 pkg") + .as_object() + .unwrap(); + let v2_bins = v2_info["bins"].as_array().unwrap(); + let v2_bins: BTreeSet = v2_bins + .iter() + .map(|b| b.as_str().unwrap().to_string()) + .collect(); + assert_eq!(v1_bins, v2_bins); + } +} + +#[cargo_test] +fn registry_upgrade() { + // Installing and upgrading from a registry. + pkg("foo", "1.0.0"); + cargo_process("install foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v1.0.0 (registry [..]) +[INSTALLING] foo v1.0.0 +[COMPILING] foo v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v1.0.0` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); + installed_process("foo").with_stdout("1.0.0").run(); + validate_trackers("foo", "1.0.0", &["foo"]); + + cargo_process("install foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[IGNORED] package `foo v1.0.0` is already installed[..] +[WARNING] be sure to add [..] +", + ) + .run(); + + pkg("foo", "1.0.1"); + + cargo_process("install foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v1.0.1 (registry [..]) +[INSTALLING] foo v1.0.1 +[COMPILING] foo v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [CWD]/home/.cargo/bin/foo[EXE] +[REPLACED] package `foo v1.0.0` with `foo v1.0.1` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); + + installed_process("foo").with_stdout("1.0.1").run(); + validate_trackers("foo", "1.0.1", &["foo"]); + + cargo_process("install foo --version=1.0.0") + .with_stderr_contains("[COMPILING] foo v1.0.0") + .run(); + installed_process("foo").with_stdout("1.0.0").run(); + validate_trackers("foo", "1.0.0", &["foo"]); + + cargo_process("install foo --version=^1.0") + .with_stderr_contains("[COMPILING] foo v1.0.1") + .run(); + installed_process("foo").with_stdout("1.0.1").run(); + validate_trackers("foo", "1.0.1", &["foo"]); + + cargo_process("install foo --version=^1.0") + .with_stderr_contains("[IGNORED] package `foo v1.0.1` is already installed[..]") + .run(); +} + +#[cargo_test] +fn uninstall() { + // Basic uninstall test. + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + cargo_process("uninstall foo").run(); + let data = load_crates2(); + assert_eq!(data["installs"].as_object().unwrap().len(), 0); + let v1_table = load_crates1(); + assert_eq!(v1_table.get("v1").unwrap().as_table().unwrap().len(), 0); +} + +#[cargo_test] +fn upgrade_force() { + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + cargo_process("install foo --force") + .with_stderr( + "\ +[UPDATING] `[..]` index +[INSTALLING] foo v1.0.0 +[COMPILING] foo v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [..]/.cargo/bin/foo[EXE] +[REPLACED] package `foo v1.0.0` with `foo v1.0.0` (executable `foo[EXE]`) +[WARNING] be sure to add `[..]/.cargo/bin` to your PATH [..] +", + ) + .run(); + validate_trackers("foo", "1.0.0", &["foo"]); +} + +#[cargo_test] +fn ambiguous_version_no_longer_allowed() { + // Non-semver-requirement is not allowed for `--version`. + pkg("foo", "1.0.0"); + cargo_process("install foo --version=1.0") + .with_stderr( + "\ +[ERROR] the `--version` provided, `1.0`, is not a valid semver version: cannot parse '1.0' as a semver + +if you want to specify semver range, add an explicit qualifier, like ^1.0 +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn path_is_always_dirty() { + // --path should always reinstall. + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("install --path .").run(); + p.cargo("install --path .") + .with_stderr_contains("[REPLACING] [..]/foo[EXE]") + .run(); +} + +#[cargo_test] +fn fails_for_conflicts_unknown() { + // If an untracked file is in the way, it should fail. + pkg("foo", "1.0.0"); + let exe = installed_exe("foo"); + exe.parent().unwrap().mkdir_p(); + fs::write(exe, "").unwrap(); + cargo_process("install foo") + .with_stderr_contains("[ERROR] binary `foo[EXE]` already exists in destination") + .with_status(101) + .run(); +} + +#[cargo_test] +fn fails_for_conflicts_known() { + // If the same binary exists in another package, it should fail. + pkg("foo", "1.0.0"); + Package::new("bar", "1.0.0") + .file("src/bin/foo.rs", "fn main() {}") + .publish(); + cargo_process("install foo").run(); + cargo_process("install bar") + .with_stderr_contains( + "[ERROR] binary `foo[EXE]` already exists in destination as part of `foo v1.0.0`", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn supports_multiple_binary_names() { + // Can individually install with --bin or --example + Package::new("foo", "1.0.0") + .file("src/main.rs", r#"fn main() { println!("foo"); }"#) + .file("src/bin/a.rs", r#"fn main() { println!("a"); }"#) + .file("examples/ex1.rs", r#"fn main() { println!("ex1"); }"#) + .publish(); + cargo_process("install foo --bin foo").run(); + installed_process("foo").with_stdout("foo").run(); + assert!(!installed_exe("a").exists()); + assert!(!installed_exe("ex1").exists()); + validate_trackers("foo", "1.0.0", &["foo"]); + cargo_process("install foo --bin a").run(); + installed_process("a").with_stdout("a").run(); + assert!(!installed_exe("ex1").exists()); + validate_trackers("foo", "1.0.0", &["a", "foo"]); + cargo_process("install foo --example ex1").run(); + installed_process("ex1").with_stdout("ex1").run(); + validate_trackers("foo", "1.0.0", &["a", "ex1", "foo"]); + cargo_process("uninstall foo --bin foo").run(); + assert!(!installed_exe("foo").exists()); + assert!(installed_exe("ex1").exists()); + validate_trackers("foo", "1.0.0", &["a", "ex1"]); + cargo_process("uninstall foo").run(); + assert!(!installed_exe("ex1").exists()); + assert!(!installed_exe("a").exists()); +} + +#[cargo_test] +fn v1_already_installed_fresh() { + // Install with v1, then try to install again with v2. + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + cargo_process("install foo") + .with_stderr_contains("[IGNORED] package `foo v1.0.0` is already installed[..]") + .run(); +} + +#[cargo_test] +fn v1_already_installed_dirty() { + // Install with v1, then install a new version with v2. + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + pkg("foo", "1.0.1"); + cargo_process("install foo") + .with_stderr_contains("[COMPILING] foo v1.0.1") + .with_stderr_contains("[REPLACING] [..]/foo[EXE]") + .run(); + validate_trackers("foo", "1.0.1", &["foo"]); +} + +#[cargo_test] +fn change_features_rebuilds() { + Package::new("foo", "1.0.0") + .file( + "src/main.rs", + r#" + fn main() { + if cfg!(feature = "f1") { + println!("f1"); + } + if cfg!(feature = "f2") { + println!("f2"); + } + } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [features] + f1 = [] + f2 = [] + default = ["f1"] + "#, + ) + .publish(); + cargo_process("install foo").run(); + installed_process("foo").with_stdout("f1").run(); + cargo_process("install foo --no-default-features").run(); + installed_process("foo").with_stdout("").run(); + cargo_process("install foo --all-features").run(); + installed_process("foo").with_stdout("f1\nf2").run(); + cargo_process("install foo --no-default-features --features=f1").run(); + installed_process("foo").with_stdout("f1").run(); +} + +#[cargo_test] +fn change_profile_rebuilds() { + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + cargo_process("install foo --debug") + .with_stderr_contains("[COMPILING] foo v1.0.0") + .with_stderr_contains("[REPLACING] [..]foo[EXE]") + .run(); + cargo_process("install foo --debug") + .with_stderr_contains("[IGNORED] package `foo v1.0.0` is already installed[..]") + .run(); +} + +#[cargo_test] +fn change_target_rebuilds() { + if cross_compile::disabled() { + return; + } + pkg("foo", "1.0.0"); + cargo_process("install foo").run(); + let target = cross_compile::alternate(); + cargo_process("install foo -v --target") + .arg(&target) + .with_stderr_contains("[COMPILING] foo v1.0.0") + .with_stderr_contains("[REPLACING] [..]foo[EXE]") + .with_stderr_contains(&format!("[..]--target {}[..]", target)) + .run(); +} + +#[cargo_test] +fn change_bin_sets_rebuilds() { + // Changing which bins in a multi-bin project should reinstall. + Package::new("foo", "1.0.0") + .file("src/main.rs", "fn main() { }") + .file("src/bin/x.rs", "fn main() { }") + .file("src/bin/y.rs", "fn main() { }") + .publish(); + cargo_process("install foo --bin x").run(); + assert!(installed_exe("x").exists()); + assert!(!installed_exe("y").exists()); + assert!(!installed_exe("foo").exists()); + validate_trackers("foo", "1.0.0", &["x"]); + cargo_process("install foo --bin y") + .with_stderr_contains("[INSTALLED] package `foo v1.0.0` (executable `y[EXE]`)") + .run(); + assert!(installed_exe("x").exists()); + assert!(installed_exe("y").exists()); + assert!(!installed_exe("foo").exists()); + validate_trackers("foo", "1.0.0", &["x", "y"]); + cargo_process("install foo") + .with_stderr_contains("[INSTALLED] package `foo v1.0.0` (executable `foo[EXE]`)") + .with_stderr_contains( + "[REPLACED] package `foo v1.0.0` with `foo v1.0.0` (executables `x[EXE]`, `y[EXE]`)", + ) + .run(); + assert!(installed_exe("x").exists()); + assert!(installed_exe("y").exists()); + assert!(installed_exe("foo").exists()); + validate_trackers("foo", "1.0.0", &["foo", "x", "y"]); +} + +#[cargo_test] +fn forwards_compatible() { + // Unknown fields should be preserved. + pkg("foo", "1.0.0"); + pkg("bar", "1.0.0"); + cargo_process("install foo").run(); + let key = "foo 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"; + let v2 = cargo_home().join(".crates2.json"); + let mut data = load_crates2(); + data["newfield"] = serde_json::Value::Bool(true); + data["installs"][key]["moreinfo"] = serde_json::Value::String("shazam".to_string()); + fs::write(&v2, serde_json::to_string(&data).unwrap()).unwrap(); + cargo_process("install bar").run(); + let data: serde_json::Value = serde_json::from_str(&fs::read_to_string(&v2).unwrap()).unwrap(); + assert_eq!(data["newfield"].as_bool().unwrap(), true); + assert_eq!( + data["installs"][key]["moreinfo"].as_str().unwrap(), + "shazam" + ); +} + +#[cargo_test] +fn v2_syncs() { + // V2 inherits the installs from V1. + pkg("one", "1.0.0"); + pkg("two", "1.0.0"); + pkg("three", "1.0.0"); + let p = project() + .file("src/bin/x.rs", "fn main() {}") + .file("src/bin/y.rs", "fn main() {}") + .build(); + cargo_process("install one").run(); + validate_trackers("one", "1.0.0", &["one"]); + p.cargo("install --path .").run(); + validate_trackers("foo", "1.0.0", &["x", "y"]); + // v1 add/remove + cargo_process("install two").run(); + cargo_process("uninstall one").run(); + // This should pick up that `two` was added, `one` was removed. + cargo_process("install three").run(); + validate_trackers("three", "1.0.0", &["three"]); + cargo_process("install --list") + .with_stdout( + "\ +foo v0.0.1 ([..]/foo): + x[EXE] + y[EXE] +three v1.0.0: + three[EXE] +two v1.0.0: + two[EXE] +", + ) + .run(); + cargo_process("install one").run(); + installed_process("one").with_stdout("1.0.0").run(); + validate_trackers("one", "1.0.0", &["one"]); + cargo_process("install two") + .with_stderr_contains("[IGNORED] package `two v1.0.0` is already installed[..]") + .run(); + // v1 remove + p.cargo("uninstall --bin x").run(); + pkg("x", "1.0.0"); + pkg("y", "1.0.0"); + // This should succeed because `x` was removed in V1. + cargo_process("install x").run(); + validate_trackers("x", "1.0.0", &["x"]); + // This should fail because `y` still exists in a different package. + cargo_process("install y") + .with_stderr_contains( + "[ERROR] binary `y[EXE]` already exists in destination \ + as part of `foo v0.0.1 ([..])`", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn upgrade_git() { + let git_project = git::new("foo", |project| project.file("src/main.rs", "fn main() {}")); + // install + cargo_process("install --git") + .arg(git_project.url().to_string()) + .run(); + // Check install stays fresh. + cargo_process("install --git") + .arg(git_project.url().to_string()) + .with_stderr_contains( + "[IGNORED] package `foo v0.0.1 (file://[..]/foo#[..])` is \ + already installed,[..]", + ) + .run(); + // Modify a file. + let repo = git2::Repository::open(git_project.root()).unwrap(); + git_project.change_file("src/main.rs", r#"fn main() {println!("onomatopoeia");}"#); + git::add(&repo); + git::commit(&repo); + // Install should reinstall. + cargo_process("install --git") + .arg(git_project.url().to_string()) + .with_stderr_contains("[COMPILING] foo v0.0.1 ([..])") + .with_stderr_contains("[REPLACING] [..]/foo[EXE]") + .run(); + installed_process("foo").with_stdout("onomatopoeia").run(); + // Check install stays fresh. + cargo_process("install --git") + .arg(git_project.url().to_string()) + .with_stderr_contains( + "[IGNORED] package `foo v0.0.1 (file://[..]/foo#[..])` is \ + already installed,[..]", + ) + .run(); +} + +#[cargo_test] +fn switch_sources() { + // Installing what appears to be the same thing, but from different + // sources should reinstall. + registry::alt_init(); + pkg("foo", "1.0.0"); + Package::new("foo", "1.0.0") + .file("src/main.rs", r#"fn main() { println!("alt"); }"#) + .alternative(true) + .publish(); + let p = project() + .at("foo-local") // so it doesn't use the same directory as the git project + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", r#"fn main() { println!("local"); }"#) + .build(); + let git_project = git::new("foo", |project| { + project.file("src/main.rs", r#"fn main() { println!("git"); }"#) + }); + + cargo_process("install foo").run(); + installed_process("foo").with_stdout("1.0.0").run(); + cargo_process("install foo --registry alternative").run(); + installed_process("foo").with_stdout("alt").run(); + p.cargo("install --path .").run(); + installed_process("foo").with_stdout("local").run(); + cargo_process("install --git") + .arg(git_project.url().to_string()) + .run(); + installed_process("foo").with_stdout("git").run(); +} + +#[cargo_test] +fn multiple_report() { + // Testing the full output that indicates installed/ignored/replaced/summary. + pkg("one", "1.0.0"); + pkg("two", "1.0.0"); + fn three(vers: &str) { + Package::new("three", vers) + .file("src/main.rs", "fn main() { }") + .file("src/bin/x.rs", "fn main() { }") + .file("src/bin/y.rs", "fn main() { }") + .publish(); + } + three("1.0.0"); + cargo_process("install one two three") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] one v1.0.0 (registry `[..]`) +[DOWNLOADING] crates ... +[DOWNLOADED] two v1.0.0 (registry `[..]`) +[DOWNLOADING] crates ... +[DOWNLOADED] three v1.0.0 (registry `[..]`) +[INSTALLING] one v1.0.0 +[COMPILING] one v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/one[EXE] +[INSTALLED] package `one v1.0.0` (executable `one[EXE]`) +[INSTALLING] two v1.0.0 +[COMPILING] two v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/two[EXE] +[INSTALLED] package `two v1.0.0` (executable `two[EXE]`) +[INSTALLING] three v1.0.0 +[COMPILING] three v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/three[EXE] +[INSTALLING] [..]/.cargo/bin/x[EXE] +[INSTALLING] [..]/.cargo/bin/y[EXE] +[INSTALLED] package `three v1.0.0` (executables `three[EXE]`, `x[EXE]`, `y[EXE]`) +[SUMMARY] Successfully installed one, two, three! +[WARNING] be sure to add `[..]/.cargo/bin` to your PATH [..] +", + ) + .run(); + pkg("foo", "1.0.1"); + pkg("bar", "1.0.1"); + three("1.0.1"); + cargo_process("install one two three") + .with_stderr( + "\ +[UPDATING] `[..]` index +[IGNORED] package `one v1.0.0` is already installed, use --force to override +[IGNORED] package `two v1.0.0` is already installed, use --force to override +[DOWNLOADING] crates ... +[DOWNLOADED] three v1.0.1 (registry `[..]`) +[INSTALLING] three v1.0.1 +[COMPILING] three v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [..]/.cargo/bin/three[EXE] +[REPLACING] [..]/.cargo/bin/x[EXE] +[REPLACING] [..]/.cargo/bin/y[EXE] +[REPLACED] package `three v1.0.0` with `three v1.0.1` (executables `three[EXE]`, `x[EXE]`, `y[EXE]`) +[SUMMARY] Successfully installed one, two, three! +[WARNING] be sure to add `[..]/.cargo/bin` to your PATH [..] +", + ) + .run(); + cargo_process("uninstall three") + .with_stderr( + "\ +[REMOVING] [..]/.cargo/bin/three[EXE] +[REMOVING] [..]/.cargo/bin/x[EXE] +[REMOVING] [..]/.cargo/bin/y[EXE] +", + ) + .run(); + cargo_process("install three --bin x") + .with_stderr( + "\ +[UPDATING] `[..]` index +[INSTALLING] three v1.0.1 +[COMPILING] three v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/x[EXE] +[INSTALLED] package `three v1.0.1` (executable `x[EXE]`) +[WARNING] be sure to add `[..]/.cargo/bin` to your PATH [..] +", + ) + .run(); + cargo_process("install three") + .with_stderr( + "\ +[UPDATING] `[..]` index +[INSTALLING] three v1.0.1 +[COMPILING] three v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/three[EXE] +[INSTALLING] [..]/.cargo/bin/y[EXE] +[REPLACING] [..]/.cargo/bin/x[EXE] +[INSTALLED] package `three v1.0.1` (executables `three[EXE]`, `y[EXE]`) +[REPLACED] package `three v1.0.1` with `three v1.0.1` (executable `x[EXE]`) +[WARNING] be sure to add `[..]/.cargo/bin` to your PATH [..] +", + ) + .run(); +} + +#[cargo_test] +fn no_track() { + pkg("foo", "1.0.0"); + cargo_process("install --no-track foo").run(); + assert!(!v1_path().exists()); + assert!(!v2_path().exists()); + cargo_process("install --no-track foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[ERROR] binary `foo[EXE]` already exists in destination `[..]/.cargo/bin/foo[EXE]` +Add --force to overwrite +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn deletes_orphaned() { + // When an executable is removed from a project, upgrading should remove it. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("src/bin/other.rs", "fn main() {}") + .file("examples/ex1.rs", "fn main() {}") + .build(); + p.cargo("install --path . --bins --examples").run(); + assert!(installed_exe("other").exists()); + + // Remove a binary, add a new one, and bump the version. + fs::remove_file(p.root().join("src/bin/other.rs")).unwrap(); + p.change_file("examples/ex2.rs", "fn main() {}"); + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.2.0" + "#, + ); + p.cargo("install --path . --bins --examples") + .with_stderr( + "\ +[INSTALLING] foo v0.2.0 [..] +[COMPILING] foo v0.2.0 [..] +[FINISHED] release [..] +[INSTALLING] [..]/.cargo/bin/ex2[EXE] +[REPLACING] [..]/.cargo/bin/ex1[EXE] +[REPLACING] [..]/.cargo/bin/foo[EXE] +[REMOVING] executable `[..]/.cargo/bin/other[EXE]` from previous version foo v0.1.0 [..] +[INSTALLED] package `foo v0.2.0 [..]` (executable `ex2[EXE]`) +[REPLACED] package `foo v0.1.0 [..]` with `foo v0.2.0 [..]` (executables `ex1[EXE]`, `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); + assert!(!installed_exe("other").exists()); + validate_trackers("foo", "0.2.0", &["foo", "ex1", "ex2"]); + // 0.1.0 should not have any entries. + validate_trackers("foo", "0.1.0", &[]); +} + +#[cargo_test] +fn already_installed_exact_does_not_update() { + pkg("foo", "1.0.0"); + cargo_process("install foo --version=1.0.0").run(); + cargo_process("install foo --version=1.0.0") + .with_stderr( + "\ +[IGNORED] package `foo v1.0.0` is already installed[..] +[WARNING] be sure to add [..] +", + ) + .run(); + + cargo_process("install foo --version=>=1.0.0") + .with_stderr( + "\ +[UPDATING] `[..]` index +[IGNORED] package `foo v1.0.0` is already installed[..] +[WARNING] be sure to add [..] +", + ) + .run(); + pkg("foo", "1.0.1"); + cargo_process("install foo --version=>=1.0.0") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v1.0.1 (registry [..]) +[INSTALLING] foo v1.0.1 +[COMPILING] foo v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [CWD]/home/.cargo/bin/foo[EXE] +[REPLACED] package `foo v1.0.0` with `foo v1.0.1` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); +} + +#[cargo_test] +fn already_installed_updates_yank_status_on_upgrade() { + pkg("foo", "1.0.0"); + pkg_maybe_yanked("foo", "1.0.1", true); + cargo_process("install foo --version=1.0.0").run(); + + cargo_process("install foo --version=1.0.1") + .with_status(101) + .with_stderr_contains( + "\ +[ERROR] cannot install package `foo`, it has been yanked from registry `crates-io` +", + ) + .run(); + + pkg_maybe_yanked("foo", "1.0.1", false); + + pkg("foo", "1.0.1"); + cargo_process("install foo --version=1.0.1") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v1.0.1 (registry [..]) +[INSTALLING] foo v1.0.1 +[COMPILING] foo v1.0.1 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [CWD]/home/.cargo/bin/foo[EXE] +[REPLACED] package `foo v1.0.0` with `foo v1.0.1` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); +} + +#[cargo_test] +fn partially_already_installed_does_one_update() { + pkg("foo", "1.0.0"); + cargo_process("install foo --version=1.0.0").run(); + pkg("bar", "1.0.0"); + pkg("baz", "1.0.0"); + cargo_process("install foo bar baz --version=1.0.0") + .with_stderr( + "\ +[IGNORED] package `foo v1.0.0` is already installed[..] +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 (registry [..]) +[DOWNLOADING] crates ... +[DOWNLOADED] baz v1.0.0 (registry [..]) +[INSTALLING] bar v1.0.0 +[COMPILING] bar v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/bar[EXE] +[INSTALLED] package `bar v1.0.0` (executable `bar[EXE]`) +[INSTALLING] baz v1.0.0 +[COMPILING] baz v1.0.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/baz[EXE] +[INSTALLED] package `baz v1.0.0` (executable `baz[EXE]`) +[SUMMARY] Successfully installed foo, bar, baz! +[WARNING] be sure to add [..] +", + ) + .run(); +} diff --git a/tests/testsuite/jobserver.rs b/tests/testsuite/jobserver.rs new file mode 100644 index 0000000..9ccff14 --- /dev/null +++ b/tests/testsuite/jobserver.rs @@ -0,0 +1,250 @@ +//! Tests for the jobserver protocol. + +use cargo_util::is_ci; +use std::net::TcpListener; +use std::process::Command; +use std::thread; + +use cargo_test_support::install::{assert_has_installed_exe, cargo_home}; +use cargo_test_support::{cargo_exe, project}; + +const EXE_CONTENT: &str = r#" +use std::env; + +fn main() { + let var = env::var("CARGO_MAKEFLAGS").unwrap(); + let arg = var.split(' ') + .find(|p| p.starts_with("--jobserver")) + .unwrap(); + let val = &arg[arg.find('=').unwrap() + 1..]; + validate(val); +} + +#[cfg(unix)] +fn validate(s: &str) { + use std::fs::{self, File}; + use std::io::*; + use std::os::unix::prelude::*; + + if let Some((r, w)) = s.split_once(',') { + // `--jobserver-auth=R,W` + unsafe { + let mut read = File::from_raw_fd(r.parse().unwrap()); + let mut write = File::from_raw_fd(w.parse().unwrap()); + + let mut buf = [0]; + assert_eq!(read.read(&mut buf).unwrap(), 1); + assert_eq!(write.write(&buf).unwrap(), 1); + } + } else { + // `--jobserver-auth=fifo:PATH` is the default since GNU Make 4.4 + let (_, path) = s.split_once(':').expect("fifo:PATH"); + assert!(fs::metadata(path).unwrap().file_type().is_fifo()); + } +} + +#[cfg(windows)] +fn validate(_: &str) { + // a little too complicated for a test... +} +"#; + +#[cargo_test] +fn jobserver_exists() { + let p = project() + .file("build.rs", EXE_CONTENT) + .file("src/lib.rs", "") + .build(); + + // Explicitly use `-j2` to ensure that there's eventually going to be a + // token to read from `validate` above, since running the build script + // itself consumes a token. + p.cargo("check -j2").run(); +} + +#[cargo_test] +fn external_subcommand_inherits_jobserver() { + let make = if cfg!(windows) { + "mingw32-make" + } else { + "make" + }; + if Command::new(make).arg("--version").output().is_err() { + return; + } + + let name = "cargo-jobserver-check"; + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "{name}" + version = "0.0.1" + "# + ), + ) + .file("src/main.rs", EXE_CONTENT) + .file( + "Makefile", + "\ +all: +\t+$(CARGO) jobserver-check +", + ) + .build(); + + p.cargo("install --path .").run(); + assert_has_installed_exe(cargo_home(), name); + + p.process(make).env("CARGO", cargo_exe()).arg("-j2").run(); +} + +#[cargo_test] +fn makes_jobserver_used() { + let make = if cfg!(windows) { + "mingw32-make" + } else { + "make" + }; + if !is_ci() && Command::new(make).arg("--version").output().is_err() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + d1 = { path = "d1" } + d2 = { path = "d2" } + d3 = { path = "d3" } + "#, + ) + .file("src/lib.rs", "") + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + build = "../dbuild.rs" + "#, + ) + .file("d1/src/lib.rs", "") + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.1" + authors = [] + build = "../dbuild.rs" + "#, + ) + .file("d2/src/lib.rs", "") + .file( + "d3/Cargo.toml", + r#" + [package] + name = "d3" + version = "0.0.1" + authors = [] + build = "../dbuild.rs" + "#, + ) + .file("d3/src/lib.rs", "") + .file( + "dbuild.rs", + r#" + use std::net::TcpStream; + use std::env; + use std::io::Read; + + fn main() { + let addr = env::var("ADDR").unwrap(); + let mut stream = TcpStream::connect(addr).unwrap(); + let mut v = Vec::new(); + stream.read_to_end(&mut v).unwrap(); + } + "#, + ) + .file( + "Makefile", + "\ +all: +\t+$(CARGO) build +", + ) + .build(); + + let l = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = l.local_addr().unwrap(); + + let child = thread::spawn(move || { + let a1 = l.accept().unwrap(); + let a2 = l.accept().unwrap(); + l.set_nonblocking(true).unwrap(); + + for _ in 0..1000 { + assert!(l.accept().is_err()); + thread::yield_now(); + } + + drop(a1); + l.set_nonblocking(false).unwrap(); + let a3 = l.accept().unwrap(); + + drop((a2, a3)); + }); + + p.process(make) + .env("CARGO", cargo_exe()) + .env("ADDR", addr.to_string()) + .arg("-j2") + .run(); + child.join().unwrap(); +} + +#[cargo_test] +fn jobserver_and_j() { + let make = if cfg!(windows) { + "mingw32-make" + } else { + "make" + }; + if !is_ci() && Command::new(make).arg("--version").output().is_err() { + return; + } + + let p = project() + .file("src/lib.rs", "") + .file( + "Makefile", + "\ +all: +\t+$(CARGO) build -j2 +", + ) + .build(); + + p.process(make) + .env("CARGO", cargo_exe()) + .arg("-j2") + .with_stderr( + "\ +warning: a `-j` argument was passed to Cargo but Cargo is also configured \ +with an external jobserver in its environment, ignoring the `-j` parameter +[COMPILING] [..] +[FINISHED] [..] +", + ) + .run(); +} diff --git a/tests/testsuite/list_availables.rs b/tests/testsuite/list_availables.rs new file mode 100644 index 0000000..6bbbeb1 --- /dev/null +++ b/tests/testsuite/list_availables.rs @@ -0,0 +1,232 @@ +//! Tests for packages/target filter flags giving suggestions on which +//! packages/targets are available. + +use cargo_test_support::project; + +const EXAMPLE: u8 = 1 << 0; +const BIN: u8 = 1 << 1; +const TEST: u8 = 1 << 2; +const BENCH: u8 = 1 << 3; +const PACKAGE: u8 = 1 << 4; + +fn list_availables_test(command: &str, targets: u8) { + let full_project = project() + .file("examples/a.rs", "fn main() { }") + .file("examples/b.rs", "fn main() { }") + .file("benches/bench1.rs", "") + .file("benches/bench2.rs", "") + .file("tests/test1.rs", "") + .file("tests/test2.rs", "") + .file("src/main.rs", "fn main() { }") + .file("Cargo.lock", "") // for `cargo pkgid` + .build(); + + if targets & EXAMPLE != 0 { + full_project + .cargo(&format!("{} --example", command)) + .with_stderr( + "\ +error: \"--example\" takes one argument. +Available examples: + a + b + +", + ) + .with_status(101) + .run(); + } + + if targets & BIN != 0 { + full_project + .cargo(&format!("{} --bin", command)) + .with_stderr( + "\ +error: \"--bin\" takes one argument. +Available binaries: + foo + +", + ) + .with_status(101) + .run(); + } + + if targets & BENCH != 0 { + full_project + .cargo(&format!("{} --bench", command)) + .with_stderr( + "\ +error: \"--bench\" takes one argument. +Available benches: + bench1 + bench2 + +", + ) + .with_status(101) + .run(); + } + + if targets & TEST != 0 { + full_project + .cargo(&format!("{} --test", command)) + .with_stderr( + "\ +error: \"--test\" takes one argument. +Available tests: + test1 + test2 + +", + ) + .with_status(101) + .run(); + } + + if targets & PACKAGE != 0 { + full_project + .cargo(&format!("{} -p", command)) + .with_stderr( + "\ +[ERROR] \"--package \" requires a SPEC format value, \ +which can be any package ID specifier in the dependency graph. +Run `cargo help pkgid` for more information about SPEC format. + +Possible packages/workspace members: + foo + +", + ) + .with_status(101) + .run(); + } + + let empty_project = project().file("src/lib.rs", "").build(); + + if targets & EXAMPLE != 0 { + empty_project + .cargo(&format!("{} --example", command)) + .with_stderr( + "\ +error: \"--example\" takes one argument. +No examples available. + +", + ) + .with_status(101) + .run(); + } + + if targets & BIN != 0 { + empty_project + .cargo(&format!("{} --bin", command)) + .with_stderr( + "\ +error: \"--bin\" takes one argument. +No binaries available. + +", + ) + .with_status(101) + .run(); + } + + if targets & BENCH != 0 { + empty_project + .cargo(&format!("{} --bench", command)) + .with_stderr( + "\ +error: \"--bench\" takes one argument. +No benches available. + +", + ) + .with_status(101) + .run(); + } + + if targets & TEST != 0 { + empty_project + .cargo(&format!("{} --test", command)) + .with_stderr( + "\ +error: \"--test\" takes one argument. +No tests available. + +", + ) + .with_status(101) + .run(); + } +} + +#[cargo_test] +fn build_list_availables() { + list_availables_test("build", EXAMPLE | BIN | TEST | BENCH | PACKAGE); +} + +#[cargo_test] +fn check_list_availables() { + list_availables_test("check", EXAMPLE | BIN | TEST | BENCH | PACKAGE); +} + +#[cargo_test] +fn doc_list_availables() { + list_availables_test("doc", BIN | PACKAGE); +} + +#[cargo_test] +fn fix_list_availables() { + list_availables_test("fix", EXAMPLE | BIN | TEST | BENCH | PACKAGE); +} + +#[cargo_test] +fn run_list_availables() { + list_availables_test("run", EXAMPLE | BIN | PACKAGE); +} + +#[cargo_test] +fn test_list_availables() { + list_availables_test("test", EXAMPLE | BIN | TEST | BENCH | PACKAGE); +} + +#[cargo_test] +fn bench_list_availables() { + list_availables_test("bench", EXAMPLE | BIN | TEST | BENCH | PACKAGE); +} + +#[cargo_test] +fn install_list_availables() { + list_availables_test("install", EXAMPLE | BIN); +} + +#[cargo_test] +fn rustdoc_list_availables() { + list_availables_test("rustdoc", EXAMPLE | BIN | TEST | BENCH | PACKAGE); +} + +#[cargo_test] +fn rustc_list_availables() { + list_availables_test("rustc", EXAMPLE | BIN | TEST | BENCH | PACKAGE); +} + +#[cargo_test] +fn pkgid_list_availables() { + list_availables_test("pkgid", PACKAGE); +} + +#[cargo_test] +fn tree_list_availables() { + list_availables_test("tree", PACKAGE); +} + +#[cargo_test] +fn clean_list_availables() { + list_availables_test("clean", PACKAGE); +} + +#[cargo_test] +fn update_list_availables() { + list_availables_test("update", PACKAGE); +} diff --git a/tests/testsuite/local_registry.rs b/tests/testsuite/local_registry.rs new file mode 100644 index 0000000..374ea93 --- /dev/null +++ b/tests/testsuite/local_registry.rs @@ -0,0 +1,528 @@ +//! Tests for local-registry sources. + +use cargo_test_support::paths::{self, CargoPathExt}; +use cargo_test_support::registry::{registry_path, Package}; +use cargo_test_support::{basic_manifest, project, t}; +use std::fs; + +fn setup() { + let root = paths::root(); + t!(fs::create_dir(&root.join(".cargo"))); + t!(fs::write( + root.join(".cargo/config"), + r#" + [source.crates-io] + registry = 'https://wut' + replace-with = 'my-awesome-local-registry' + + [source.my-awesome-local-registry] + local-registry = 'registry' + "# + )); +} + +#[cargo_test] +fn simple() { + setup(); + Package::new("bar", "0.0.1") + .local(true) + .file("src/lib.rs", "pub fn bar() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.0.1" + "#, + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("build") + .with_stderr( + "\ +[UNPACKING] bar v0.0.1 ([..]) +[COMPILING] bar v0.0.1 +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] [..] +", + ) + .run(); + p.cargo("build").with_stderr("[FINISHED] [..]").run(); + p.cargo("test").run(); +} + +#[cargo_test] +fn not_found() { + setup(); + // Publish a package so that the directory hierarchy is created. + // Note, however, that we declare a dependency on baZ. + Package::new("bar", "0.0.1").local(true).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + baz = "0.0.1" + "#, + ) + .file( + "src/lib.rs", + "extern crate baz; pub fn foo() { baz::bar(); }", + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] no matching package named `baz` found +location searched: registry `crates-io` +required by package `foo v0.0.1 ([..]/foo)` +", + ) + .run(); +} + +#[cargo_test] +fn depend_on_yanked() { + setup(); + Package::new("bar", "0.0.1").local(true).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Run cargo to create lock file. + p.cargo("check").run(); + + registry_path().join("index").join("3").rm_rf(); + Package::new("bar", "0.0.1") + .local(true) + .yanked(true) + .publish(); + + p.cargo("check") + .with_stderr( + "\ +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn multiple_versions() { + setup(); + Package::new("bar", "0.0.1").local(true).publish(); + Package::new("bar", "0.1.0") + .local(true) + .file("src/lib.rs", "pub fn bar() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UNPACKING] bar v0.1.0 ([..]) +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] [..] +", + ) + .run(); + + Package::new("bar", "0.2.0") + .local(true) + .file("src/lib.rs", "pub fn bar() {}") + .publish(); + + p.cargo("update -v") + .with_stderr("[UPDATING] bar v0.1.0 -> v0.2.0") + .run(); +} + +#[cargo_test] +fn multiple_names() { + setup(); + Package::new("bar", "0.0.1") + .local(true) + .file("src/lib.rs", "pub fn bar() {}") + .publish(); + Package::new("baz", "0.1.0") + .local(true) + .file("src/lib.rs", "pub fn baz() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + baz = "*" + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate bar; + extern crate baz; + pub fn foo() { + bar::bar(); + baz::baz(); + } + "#, + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UNPACKING] [..] +[UNPACKING] [..] +[CHECKING] [..] +[CHECKING] [..] +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn interdependent() { + setup(); + Package::new("bar", "0.0.1") + .local(true) + .file("src/lib.rs", "pub fn bar() {}") + .publish(); + Package::new("baz", "0.1.0") + .local(true) + .dep("bar", "*") + .file("src/lib.rs", "extern crate bar; pub fn baz() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + baz = "*" + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate bar; + extern crate baz; + pub fn foo() { + bar::bar(); + baz::baz(); + } + "#, + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UNPACKING] [..] +[UNPACKING] [..] +[CHECKING] bar v0.0.1 +[CHECKING] baz v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn path_dep_rewritten() { + setup(); + Package::new("bar", "0.0.1") + .local(true) + .file("src/lib.rs", "pub fn bar() {}") + .publish(); + Package::new("baz", "0.1.0") + .local(true) + .dep("bar", "*") + .file( + "Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "bar", version = "*" } + "#, + ) + .file("src/lib.rs", "extern crate bar; pub fn baz() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + baz = "*" + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate bar; + extern crate baz; + pub fn foo() { + bar::bar(); + baz::baz(); + } + "#, + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UNPACKING] [..] +[UNPACKING] [..] +[CHECKING] bar v0.0.1 +[CHECKING] baz v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn invalid_dir_bad() { + setup(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [source.crates-io] + registry = 'https://wut' + replace-with = 'my-awesome-local-directory' + + [source.my-awesome-local-directory] + local-registry = '/path/to/nowhere' + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 [..]` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update registry `crates-io` + +Caused by: + failed to update replaced source registry `crates-io` + +Caused by: + local registry path is not a directory: [..]path[..]to[..]nowhere +", + ) + .run(); +} + +#[cargo_test] +fn different_directory_replacing_the_registry_is_bad() { + setup(); + + // Move our test's .cargo/config to a temporary location and publish a + // registry package we're going to use first. + let config = paths::root().join(".cargo"); + let config_tmp = paths::root().join(".cargo-old"); + t!(fs::rename(&config, &config_tmp)); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Generate a lock file against the crates.io registry + Package::new("bar", "0.0.1").publish(); + p.cargo("check").run(); + + // Switch back to our directory source, and now that we're replacing + // crates.io make sure that this fails because we're replacing with a + // different checksum + config.rm_rf(); + t!(fs::rename(&config_tmp, &config)); + Package::new("bar", "0.0.1") + .file("src/lib.rs", "invalid") + .local(true) + .publish(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] checksum for `bar v0.0.1` changed between lock files + +this could be indicative of a few possible errors: + + * the lock file is corrupt + * a replacement source in use (e.g., a mirror) returned a different checksum + * the source itself may be corrupt in one way or another + +unable to verify that `bar v0.0.1` is the same as when the lockfile was generated + +", + ) + .run(); +} + +#[cargo_test] +fn crates_io_registry_url_is_optional() { + let root = paths::root(); + t!(fs::create_dir(&root.join(".cargo"))); + t!(fs::write( + root.join(".cargo/config"), + r#" + [source.crates-io] + replace-with = 'my-awesome-local-registry' + + [source.my-awesome-local-registry] + local-registry = 'registry' + "# + )); + + Package::new("bar", "0.0.1") + .local(true) + .file("src/lib.rs", "pub fn bar() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.0.1" + "#, + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("build") + .with_stderr( + "\ +[UNPACKING] bar v0.0.1 ([..]) +[COMPILING] bar v0.0.1 +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] [..] +", + ) + .run(); + p.cargo("build").with_stderr("[FINISHED] [..]").run(); + p.cargo("test").run(); +} diff --git a/tests/testsuite/locate_project.rs b/tests/testsuite/locate_project.rs new file mode 100644 index 0000000..7e8ceb4 --- /dev/null +++ b/tests/testsuite/locate_project.rs @@ -0,0 +1,76 @@ +//! Tests for the `cargo locate-project` command. + +use cargo_test_support::project; + +#[cargo_test] +fn simple() { + let p = project().build(); + + p.cargo("locate-project") + .with_json(r#"{"root": "[ROOT]/foo/Cargo.toml"}"#) + .run(); +} + +#[cargo_test] +fn message_format() { + let p = project().build(); + + p.cargo("locate-project --message-format plain") + .with_stdout("[ROOT]/foo/Cargo.toml") + .run(); + + p.cargo("locate-project --message-format json") + .with_json(r#"{"root": "[ROOT]/foo/Cargo.toml"}"#) + .run(); + + p.cargo("locate-project --message-format cryptic") + .with_stderr("error: invalid message format specifier: `cryptic`") + .with_status(101) + .run(); +} + +#[cargo_test] +fn workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "outer" + version = "0.0.0" + + [workspace] + members = ["inner"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "inner/Cargo.toml", + r#" + [package] + name = "inner" + version = "0.0.0" + "#, + ) + .file("inner/src/lib.rs", "") + .build(); + + let outer_manifest = r#"{"root": "[ROOT]/foo/Cargo.toml"}"#; + let inner_manifest = r#"{"root": "[ROOT]/foo/inner/Cargo.toml"}"#; + + p.cargo("locate-project").with_json(outer_manifest).run(); + + p.cargo("locate-project") + .cwd("inner") + .with_json(inner_manifest) + .run(); + + p.cargo("locate-project --workspace") + .with_json(outer_manifest) + .run(); + + p.cargo("locate-project --workspace") + .cwd("inner") + .with_json(outer_manifest) + .run(); +} diff --git a/tests/testsuite/lockfile_compat.rs b/tests/testsuite/lockfile_compat.rs new file mode 100644 index 0000000..aad8723 --- /dev/null +++ b/tests/testsuite/lockfile_compat.rs @@ -0,0 +1,890 @@ +//! Tests for supporting older versions of the Cargo.lock file format. + +use cargo_test_support::compare::assert_match_exact; +use cargo_test_support::git; +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_lib_manifest, basic_manifest, project}; + +#[cargo_test] +fn oldest_lockfile_still_works() { + let cargo_commands = vec!["build", "update"]; + for cargo_command in cargo_commands { + oldest_lockfile_still_works_with_command(cargo_command); + } +} + +fn oldest_lockfile_still_works_with_command(cargo_command: &str) { + Package::new("bar", "0.1.0").publish(); + + let expected_lockfile = r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "[..]" + +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar", +] +"#; + + let old_lockfile = r#" +[root] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +"#; + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file("Cargo.lock", old_lockfile) + .build(); + + p.cargo(cargo_command).run(); + + let lock = p.read_lockfile(); + assert_match_exact(expected_lockfile, &lock); +} + +#[cargo_test] +fn frozen_flag_preserves_old_lockfile() { + let cksum = Package::new("bar", "0.1.0").publish(); + + let old_lockfile = format!( + r#"[root] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{}" +"#, + cksum, + ); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file("Cargo.lock", &old_lockfile) + .build(); + + p.cargo("check --locked").run(); + + let lock = p.read_lockfile(); + assert_match_exact(&old_lockfile, &lock); +} + +#[cargo_test] +fn totally_wild_checksums_works() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file( + "Cargo.lock", + r#" +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum baz 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "checksum" +"checksum bar 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "checksum" +"#, + ); + + let p = p.build(); + + p.cargo("check").run(); + + let lock = p.read_lockfile(); + assert_match_exact( + r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "[..]" + +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar", +] +"#, + &lock, + ); +} + +#[cargo_test] +fn wrong_checksum_is_an_error() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file( + "Cargo.lock", + r#" +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "checksum" +"#, + ); + + let p = p.build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `[..]` index +error: checksum for `bar v0.1.0` changed between lock files + +this could be indicative of a few possible errors: + + * the lock file is corrupt + * a replacement source in use (e.g., a mirror) returned a different checksum + * the source itself may be corrupt in one way or another + +unable to verify that `bar v0.1.0` is the same as when the lockfile was generated + +", + ) + .run(); +} + +// If the checksum is unlisted in the lock file (e.g., ) yet we can +// calculate it (e.g., it's a registry dep), then we should in theory just fill +// it in. +#[cargo_test] +fn unlisted_checksum_is_bad_if_we_calculate() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file( + "Cargo.lock", + r#" +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "" +"#, + ); + let p = p.build(); + + p.cargo("fetch") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `[..]` index +error: checksum for `bar v0.1.0` was not previously calculated, but a checksum \ +could now be calculated + +this could be indicative of a few possible situations: + + * the source `[..]` did not previously support checksums, + but was replaced with one that does + * newer Cargo implementations know how to checksum this source, but this + older implementation does not + * the lock file is corrupt + +", + ) + .run(); +} + +// If the checksum is listed in the lock file yet we cannot calculate it (e.g., +// Git dependencies as of today), then make sure we choke. +#[cargo_test] +fn listed_checksum_bad_if_we_cannot_compute() { + let git = git::new("bar", |p| { + p.file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = {{ git = '{}' }} + "#, + git.url() + ), + ) + .file("src/lib.rs", "") + .file( + "Cargo.lock", + &format!( + r#" +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar 0.1.0 (git+{0})" +] + +[[package]] +name = "bar" +version = "0.1.0" +source = "git+{0}" + +[metadata] +"checksum bar 0.1.0 (git+{0})" = "checksum" +"#, + git.url() + ), + ); + + let p = p.build(); + + p.cargo("fetch") + .with_status(101) + .with_stderr( + "\ +[UPDATING] git repository `[..]` +error: checksum for `bar v0.1.0 ([..])` could not be calculated, but a \ +checksum is listed in the existing lock file[..] + +this could be indicative of a few possible situations: + + * the source `[..]` supports checksums, + but was replaced with one that doesn't + * the lock file is corrupt + +unable to verify that `bar v0.1.0 ([..])` is the same as when the lockfile was generated + +", + ) + .run(); +} + +#[cargo_test] +fn current_lockfile_format() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", ""); + let p = p.build(); + + p.cargo("check").run(); + + let actual = p.read_lockfile(); + + let expected = "\ +# This file is automatically @generated by Cargo.\n# It is not intended for manual editing. +version = 3 + +[[package]] +name = \"bar\" +version = \"0.1.0\" +source = \"registry+https://github.com/rust-lang/crates.io-index\" +checksum = \"[..]\" + +[[package]] +name = \"foo\" +version = \"0.0.1\" +dependencies = [ + \"bar\", +] +"; + assert_match_exact(expected, &actual); +} + +#[cargo_test] +fn lockfile_without_root() { + Package::new("bar", "0.1.0").publish(); + + let lockfile = r#" +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar", +] +"#; + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file("Cargo.lock", lockfile); + + let p = p.build(); + + p.cargo("check").run(); + + let lock = p.read_lockfile(); + assert_match_exact( + r#"# [..] +# [..] +version = 3 + +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "[..]" + +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar", +] +"#, + &lock, + ); +} + +#[cargo_test] +fn locked_correct_error() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", ""); + let p = p.build(); + + p.cargo("check --locked") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `[..]` index +error: the lock file [CWD]/Cargo.lock needs to be updated but --locked was passed to prevent this +If you want to try to generate the lock file without accessing the network, \ +remove the --locked flag and use --offline instead. +", + ) + .run(); +} + +#[cargo_test] +fn v2_format_preserved() { + let cksum = Package::new("bar", "0.1.0").publish(); + + let lockfile = format!( + r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "{}" + +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar", +] +"#, + cksum + ); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file("Cargo.lock", &lockfile) + .build(); + + p.cargo("fetch").run(); + + let lock = p.read_lockfile(); + assert_match_exact(&lockfile, &lock); +} + +#[cargo_test] +fn v2_path_and_crates_io() { + let cksum010 = Package::new("a", "0.1.0").publish(); + let cksum020 = Package::new("a", "0.2.0").publish(); + + let lockfile = format!( + r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "a" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "{}" + +[[package]] +name = "a" +version = "0.2.0" + +[[package]] +name = "a" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "{}" + +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "a 0.1.0", + "a 0.2.0", + "a 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] +"#, + cksum010, cksum020, + ); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = 'a' } + b = { version = "0.1", package = 'a' } + c = { version = "0.2", package = 'a' } + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.2.0" + "#, + ) + .file("a/src/lib.rs", "") + .file("Cargo.lock", &lockfile) + .build(); + + p.cargo("fetch").run(); + p.cargo("fetch").run(); + + let lock = p.read_lockfile(); + assert_match_exact(&lockfile, &lock); +} + +#[cargo_test] +fn v3_and_git() { + let (git_project, repo) = git::new_repo("dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file("src/lib.rs", "") + }); + let head_id = repo.head().unwrap().target().unwrap(); + + let lockfile = format!( + r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "dep1" +version = "0.5.0" +source = "git+{}?branch=master#{}" + +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "dep1", +] +"#, + git_project.url(), + head_id, + ); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + dep1 = {{ git = '{}', branch = 'master' }} + "#, + git_project.url(), + ), + ) + .file("src/lib.rs", "") + .file("Cargo.lock", "version = 3") + .build(); + + p.cargo("fetch").run(); + + let lock = p.read_lockfile(); + assert_match_exact(&lockfile, &lock); +} + +#[cargo_test] +fn lock_from_the_future() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#, + ) + .file("src/lib.rs", "") + .file("Cargo.lock", "version = 10000000") + .build(); + + p.cargo("fetch") + .with_stderr( + "\ +error: failed to parse lock file at: [..] + +Caused by: + lock file version `10000000` was found, but this version of Cargo does not \ + understand this lock file, perhaps Cargo needs to be updated? +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn preserve_old_format_if_no_update_needed() { + let cksum = Package::new("bar", "0.1.0").publish(); + let lockfile = format!( + r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foo" +version = "0.0.1" +dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{}" +"#, + cksum + ); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file("Cargo.lock", &lockfile) + .build(); + + p.cargo("check --locked").run(); +} + +#[cargo_test] +fn same_name_version_different_sources() { + let cksum = Package::new("foo", "0.1.0").publish(); + let (git_project, repo) = git::new_repo("dep1", |project| { + project + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + }); + let head_id = repo.head().unwrap().target().unwrap(); + + // Lockfile was generated with Rust 1.51 + let lockfile = format!( + r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "foo" +version = "0.1.0" +dependencies = [ + "foo 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "foo 0.1.0 (git+{url})", +] + +[[package]] +name = "foo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "{cksum}" + +[[package]] +name = "foo" +version = "0.1.0" +source = "git+{url}#{sha}" +"#, + sha = head_id, + url = git_project.url(), + cksum = cksum + ); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + foo = "0.1.0" + foo2 = {{ git = '{}', package = 'foo' }} + "#, + git_project.url(), + ), + ) + .file("src/lib.rs", "") + .file("Cargo.lock", &lockfile) + .build(); + + p.cargo("check").run(); + + assert_eq!(p.read_file("Cargo.lock"), lockfile); +} + +#[cargo_test] +fn bad_data_in_lockfile_error_meg() { + Package::new("bar", "0.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.0.0" + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "Cargo.lock", + r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1b9346248cf3391ead604c4407258d327c28e37209f6d56127598165165dda" + +[[package]] +name = "test" +version = "0.0.0" +dependencies = [ + "bar", +]"#, + ) + .build(); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[..] +[ERROR] failed to select a version for the requirement `bar = \"*\"` (locked to 0.1.0) +candidate versions found which didn't match: 0.0.1 +location searched: `dummy-registry` index (which is replacing registry `crates-io`) +required by package `test v0.0.0 ([..])` +perhaps a crate was updated and forgotten to be re-vendored? +", + ) + .run(); +} diff --git a/tests/testsuite/login.rs b/tests/testsuite/login.rs new file mode 100644 index 0000000..19387ae --- /dev/null +++ b/tests/testsuite/login.rs @@ -0,0 +1,364 @@ +//! Tests for the `cargo login` command. + +use cargo_test_support::cargo_process; +use cargo_test_support::paths::{self, CargoPathExt}; +use cargo_test_support::registry::{self, RegistryBuilder}; +use cargo_test_support::t; +use std::fs; +use std::path::PathBuf; + +const TOKEN: &str = "test-token"; +const TOKEN2: &str = "test-token2"; +const ORIGINAL_TOKEN: &str = "api-token"; + +fn credentials_toml() -> PathBuf { + paths::home().join(".cargo/credentials.toml") +} + +fn setup_new_credentials() { + setup_new_credentials_at(credentials_toml()); +} + +fn setup_new_credentials_at(config: PathBuf) { + t!(fs::create_dir_all(config.parent().unwrap())); + t!(fs::write( + &config, + format!(r#"token = "{token}""#, token = ORIGINAL_TOKEN) + )); +} + +fn check_token(expected_token: &str, registry: Option<&str>) -> bool { + let credentials = credentials_toml(); + assert!(credentials.is_file()); + + let contents = fs::read_to_string(&credentials).unwrap(); + let toml: toml::Table = contents.parse().unwrap(); + + let token = match registry { + // A registry has been provided, so check that the token exists in a + // table for the registry. + Some(registry) => toml + .get("registries") + .and_then(|registries_table| registries_table.get(registry)) + .and_then(|registry_table| match registry_table.get("token") { + Some(&toml::Value::String(ref token)) => Some(token.as_str().to_string()), + _ => None, + }), + // There is no registry provided, so check the global token instead. + None => toml + .get("registry") + .and_then(|registry_table| registry_table.get("token")) + .and_then(|v| match v { + toml::Value::String(ref token) => Some(token.as_str().to_string()), + _ => None, + }), + }; + + if let Some(token_val) = token { + token_val == expected_token + } else { + false + } +} + +#[cargo_test] +fn registry_credentials() { + let _alternative = RegistryBuilder::new().alternative().build(); + let _alternative2 = RegistryBuilder::new() + .alternative_named("alternative2") + .build(); + + setup_new_credentials(); + + let reg = "alternative"; + + cargo_process("login --registry").arg(reg).arg(TOKEN).run(); + + // Ensure that we have not updated the default token + assert!(check_token(ORIGINAL_TOKEN, None)); + + // Also ensure that we get the new token for the registry + assert!(check_token(TOKEN, Some(reg))); + + let reg2 = "alternative2"; + cargo_process("login --registry") + .arg(reg2) + .arg(TOKEN2) + .run(); + + // Ensure not overwriting 1st alternate registry token with + // 2nd alternate registry token (see rust-lang/cargo#7701). + assert!(check_token(ORIGINAL_TOKEN, None)); + assert!(check_token(TOKEN, Some(reg))); + assert!(check_token(TOKEN2, Some(reg2))); +} + +#[cargo_test] +fn empty_login_token() { + let registry = RegistryBuilder::new() + .no_configure_registry() + .no_configure_token() + .build(); + setup_new_credentials(); + + cargo_process("login") + .replace_crates_io(registry.index_url()) + .with_stdout("please paste the token found on [..]/me below") + .with_stdin("\t\n") + .with_stderr( + "\ +[UPDATING] crates.io index +[ERROR] please provide a non-empty token +", + ) + .with_status(101) + .run(); + + cargo_process("login") + .replace_crates_io(registry.index_url()) + .arg("") + .with_stderr( + "\ +[ERROR] please provide a non-empty token +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn invalid_login_token() { + let registry = RegistryBuilder::new() + .no_configure_registry() + .no_configure_token() + .build(); + setup_new_credentials(); + + let check = |stdin: &str, stderr: &str| { + cargo_process("login") + .replace_crates_io(registry.index_url()) + .with_stdout("please paste the token found on [..]/me below") + .with_stdin(stdin) + .with_stderr(stderr) + .with_status(101) + .run(); + }; + + check( + "😄", + "\ +[UPDATING] crates.io index +[ERROR] token contains invalid characters. +Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header.", + ); + check( + "\u{0016}", + "\ +[ERROR] token contains invalid characters. +Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header.", + ); + check( + "\u{0000}", + "\ +[ERROR] token contains invalid characters. +Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header.", + ); + check( + "你好", + "\ +[ERROR] token contains invalid characters. +Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header.", + ); +} + +#[cargo_test] +fn bad_asymmetric_token_args() { + // These cases are kept brief as the implementation is covered by clap, so this is only smoke testing that we have clap configured correctly. + cargo_process("login --key-subject=foo tok") + .with_stderr_contains( + "error: the argument '--key-subject ' cannot be used with '[token]'", + ) + .with_status(1) + .run(); + + cargo_process("login --generate-keypair tok") + .with_stderr_contains( + "error: the argument '--generate-keypair' cannot be used with '[token]'", + ) + .with_status(1) + .run(); + + cargo_process("login --secret-key tok") + .with_stderr_contains("error: the argument '--secret-key' cannot be used with '[token]'") + .with_status(1) + .run(); + + cargo_process("login --generate-keypair --secret-key") + .with_stderr_contains( + "error: the argument '--generate-keypair' cannot be used with '--secret-key'", + ) + .with_status(1) + .run(); +} + +#[cargo_test] +fn asymmetric_requires_nightly() { + let registry = registry::init(); + cargo_process("login --key-subject=foo") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_contains("[ERROR] the `key-subject` flag is unstable, pass `-Z registry-auth` to enable it\n\ + See https://github.com/rust-lang/cargo/issues/10519 for more information about the `key-subject` flag.") + .run(); + cargo_process("login --generate-keypair") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_contains("[ERROR] the `generate-keypair` flag is unstable, pass `-Z registry-auth` to enable it\n\ + See https://github.com/rust-lang/cargo/issues/10519 for more information about the `generate-keypair` flag.") + .run(); + cargo_process("login --secret-key") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_contains("[ERROR] the `secret-key` flag is unstable, pass `-Z registry-auth` to enable it\n\ + See https://github.com/rust-lang/cargo/issues/10519 for more information about the `secret-key` flag.") + .run(); +} + +#[cargo_test] +fn login_with_no_cargo_dir() { + // Create a config in the root directory because `login` requires the + // index to be updated, and we don't want to hit crates.io. + let registry = registry::init(); + fs::rename(paths::home().join(".cargo"), paths::root().join(".cargo")).unwrap(); + paths::home().rm_rf(); + cargo_process("login foo -v") + .replace_crates_io(registry.index_url()) + .run(); + let credentials = fs::read_to_string(credentials_toml()).unwrap(); + assert_eq!(credentials, "[registry]\ntoken = \"foo\"\n"); +} + +#[cargo_test] +fn login_with_differently_sized_token() { + // Verify that the configuration file gets properly truncated. + let registry = registry::init(); + let credentials = credentials_toml(); + fs::remove_file(&credentials).unwrap(); + cargo_process("login lmaolmaolmao -v") + .replace_crates_io(registry.index_url()) + .run(); + cargo_process("login lmao -v") + .replace_crates_io(registry.index_url()) + .run(); + cargo_process("login lmaolmaolmao -v") + .replace_crates_io(registry.index_url()) + .run(); + let credentials = fs::read_to_string(&credentials).unwrap(); + assert_eq!(credentials, "[registry]\ntoken = \"lmaolmaolmao\"\n"); +} + +#[cargo_test] +fn login_with_token_on_stdin() { + let registry = registry::init(); + let credentials = credentials_toml(); + fs::remove_file(&credentials).unwrap(); + cargo_process("login lmao -v") + .replace_crates_io(registry.index_url()) + .run(); + cargo_process("login") + .replace_crates_io(registry.index_url()) + .with_stdout("please paste the token found on [..]/me below") + .with_stdin("some token") + .run(); + let credentials = fs::read_to_string(&credentials).unwrap(); + assert_eq!(credentials, "[registry]\ntoken = \"some token\"\n"); +} + +#[cargo_test] +fn login_with_asymmetric_token_and_subject_on_stdin() { + let registry = registry::init(); + let credentials = credentials_toml(); + fs::remove_file(&credentials).unwrap(); + cargo_process("login --key-subject=foo --secret-key -v -Z registry-auth") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(registry.index_url()) + .with_stdout( + "\ + please paste the API secret key below +k3.public.AmDwjlyf8jAV3gm5Z7Kz9xAOcsKslt_Vwp5v-emjFzBHLCtcANzTaVEghTNEMj9PkQ", + ) + .with_stdin("k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36") + .run(); + let credentials = fs::read_to_string(&credentials).unwrap(); + assert!(credentials.starts_with("[registry]\n")); + assert!(credentials.contains("secret-key-subject = \"foo\"\n")); + assert!(credentials.contains("secret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n")); +} + +#[cargo_test] +fn login_with_asymmetric_token_on_stdin() { + let registry = registry::init(); + let credentials = credentials_toml(); + fs::remove_file(&credentials).unwrap(); + cargo_process("login --secret-key -v -Z registry-auth") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(registry.index_url()) + .with_stdout( + "\ + please paste the API secret key below +k3.public.AmDwjlyf8jAV3gm5Z7Kz9xAOcsKslt_Vwp5v-emjFzBHLCtcANzTaVEghTNEMj9PkQ", + ) + .with_stdin("k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36") + .run(); + let credentials = fs::read_to_string(&credentials).unwrap(); + assert_eq!(credentials, "[registry]\nsecret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n"); +} + +#[cargo_test] +fn login_with_asymmetric_key_subject_without_key() { + let registry = registry::init(); + let credentials = credentials_toml(); + fs::remove_file(&credentials).unwrap(); + cargo_process("login --key-subject=foo -Z registry-auth") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(registry.index_url()) + .with_stderr_contains("error: need a secret_key to set a key_subject") + .with_status(101) + .run(); + + // ok so add a secret_key to the credentials + cargo_process("login --secret-key -v -Z registry-auth") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(registry.index_url()) + .with_stdout( + "please paste the API secret key below +k3.public.AmDwjlyf8jAV3gm5Z7Kz9xAOcsKslt_Vwp5v-emjFzBHLCtcANzTaVEghTNEMj9PkQ", + ) + .with_stdin("k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36") + .run(); + + // and then it should work + cargo_process("login --key-subject=foo -Z registry-auth") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(registry.index_url()) + .run(); + + let credentials = fs::read_to_string(&credentials).unwrap(); + assert!(credentials.starts_with("[registry]\n")); + assert!(credentials.contains("secret-key-subject = \"foo\"\n")); + assert!(credentials.contains("secret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n")); +} + +#[cargo_test] +fn login_with_generate_asymmetric_token() { + let registry = registry::init(); + let credentials = credentials_toml(); + fs::remove_file(&credentials).unwrap(); + cargo_process("login --generate-keypair -Z registry-auth") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(registry.index_url()) + .with_stdout("k3.public.[..]") + .run(); + let credentials = fs::read_to_string(&credentials).unwrap(); + assert!(credentials.contains("secret-key = \"k3.secret.")); +} diff --git a/tests/testsuite/logout.rs b/tests/testsuite/logout.rs new file mode 100644 index 0000000..92b6f42 --- /dev/null +++ b/tests/testsuite/logout.rs @@ -0,0 +1,91 @@ +//! Tests for the `cargo logout` command. + +use cargo_test_support::install::cargo_home; +use cargo_test_support::registry::TestRegistry; +use cargo_test_support::{cargo_process, registry}; +use std::fs; + +#[cargo_test] +fn gated() { + registry::init(); + cargo_process("logout") + .masquerade_as_nightly_cargo(&["cargo-logout"]) + .with_status(101) + .with_stderr( + "\ +[ERROR] the `cargo logout` command is unstable, pass `-Z unstable-options` to enable it +See https://github.com/rust-lang/cargo/issues/8933 for more information about \ +the `cargo logout` command. +", + ) + .run(); +} + +/// Checks whether or not the token is set for the given token. +fn check_config_token(registry: Option<&str>, should_be_set: bool) { + let credentials = cargo_home().join("credentials.toml"); + let contents = fs::read_to_string(&credentials).unwrap(); + let toml: toml::Table = contents.parse().unwrap(); + if let Some(registry) = registry { + assert_eq!( + toml.get("registries") + .and_then(|registries| registries.get(registry)) + .and_then(|registry| registry.get("token")) + .is_some(), + should_be_set + ); + } else { + assert_eq!( + toml.get("registry") + .and_then(|registry| registry.get("token")) + .is_some(), + should_be_set + ); + } +} + +fn simple_logout_test(registry: &TestRegistry, reg: Option<&str>, flag: &str) { + let msg = reg.unwrap_or("crates-io"); + check_config_token(reg, true); + let mut cargo = cargo_process(&format!("logout -Z unstable-options {}", flag)); + if reg.is_none() { + cargo.replace_crates_io(registry.index_url()); + } + cargo + .masquerade_as_nightly_cargo(&["cargo-logout"]) + .with_stderr(&format!( + "\ +[LOGOUT] token for `{}` has been removed from local storage +", + msg + )) + .run(); + check_config_token(reg, false); + + let mut cargo = cargo_process(&format!("logout -Z unstable-options {}", flag)); + if reg.is_none() { + cargo.replace_crates_io(registry.index_url()); + } + cargo + .masquerade_as_nightly_cargo(&["cargo-logout"]) + .with_stderr(&format!( + "\ +[LOGOUT] not currently logged in to `{}` +", + msg + )) + .run(); + check_config_token(reg, false); +} + +#[cargo_test] +fn default_registry() { + let registry = registry::init(); + simple_logout_test(®istry, None, ""); +} + +#[cargo_test] +fn other_registry() { + let registry = registry::alt_init(); + simple_logout_test(®istry, Some("alternative"), "--registry alternative"); +} diff --git a/tests/testsuite/lto.rs b/tests/testsuite/lto.rs new file mode 100644 index 0000000..40b4f7c --- /dev/null +++ b/tests/testsuite/lto.rs @@ -0,0 +1,850 @@ +use cargo::core::compiler::Lto; +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_manifest, project, Project}; +use std::process::Output; + +#[cargo_test] +fn with_deps() { + Package::new("bar", "0.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.0.0" + + [dependencies] + bar = "*" + + [profile.release] + lto = true + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() {}") + .build(); + p.cargo("build -v --release") + .with_stderr_contains("[..]`rustc[..]--crate-name bar[..]-C linker-plugin-lto[..]`") + .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`") + .run(); +} + +#[cargo_test] +fn shared_deps() { + Package::new("bar", "0.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.0.0" + + [dependencies] + bar = "*" + + [build-dependencies] + bar = "*" + + [profile.release] + lto = true + "#, + ) + .file("build.rs", "extern crate bar; fn main() {}") + .file("src/main.rs", "extern crate bar; fn main() {}") + .build(); + p.cargo("build -v --release") + .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`") + .run(); +} + +#[cargo_test] +fn build_dep_not_ltod() { + Package::new("bar", "0.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.0.0" + + [build-dependencies] + bar = "*" + + [profile.release] + lto = true + "#, + ) + .file("build.rs", "extern crate bar; fn main() {}") + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("build -v --release") + .with_stderr_contains("[..]`rustc[..]--crate-name bar[..]-C embed-bitcode=no[..]`") + .with_stderr_contains("[..]`rustc[..]--crate-name test[..]-C lto[..]`") + .run(); +} + +#[cargo_test] +fn complicated() { + Package::new("dep-shared", "0.0.1") + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + Package::new("dep-normal2", "0.0.1") + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + Package::new("dep-normal", "0.0.1") + .dep("dep-shared", "*") + .dep("dep-normal2", "*") + .file( + "src/lib.rs", + " + pub fn foo() { + dep_shared::foo(); + dep_normal2::foo(); + } + ", + ) + .publish(); + Package::new("dep-build2", "0.0.1") + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + Package::new("dep-build", "0.0.1") + .dep("dep-shared", "*") + .dep("dep-build2", "*") + .file( + "src/lib.rs", + " + pub fn foo() { + dep_shared::foo(); + dep_build2::foo(); + } + ", + ) + .publish(); + Package::new("dep-proc-macro2", "0.0.1") + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + Package::new("dep-proc-macro", "0.0.1") + .proc_macro(true) + .dep("dep-shared", "*") + .dep("dep-proc-macro2", "*") + .file( + "src/lib.rs", + " + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro_attribute] + pub fn foo(_: TokenStream, a: TokenStream) -> TokenStream { + dep_shared::foo(); + dep_proc_macro2::foo(); + a + } + ", + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.0.0" + + [lib] + crate-type = ['cdylib', 'staticlib'] + + [dependencies] + dep-normal = "*" + dep-proc-macro = "*" + + [build-dependencies] + dep-build = "*" + + [profile.release] + lto = true + + # force build deps to share an opt-level with the rest of the + # graph so they only get built once. + [profile.release.build-override] + opt-level = 3 + "#, + ) + .file("build.rs", "fn main() { dep_build::foo() }") + .file( + "src/bin/foo-bin.rs", + "#[dep_proc_macro::foo] fn main() { dep_normal::foo() }", + ) + .file( + "src/lib.rs", + "#[dep_proc_macro::foo] pub fn foo() { dep_normal::foo() }", + ) + .build(); + p.cargo("build -v --release") + // normal deps and their transitive dependencies do not need object + // code, so they should have linker-plugin-lto specified + .with_stderr_contains( + "[..]`rustc[..]--crate-name dep_normal2 [..]-C linker-plugin-lto[..]`", + ) + .with_stderr_contains("[..]`rustc[..]--crate-name dep_normal [..]-C linker-plugin-lto[..]`") + // build dependencies and their transitive deps don't need any bitcode, + // so embedding should be turned off + .with_stderr_contains("[..]`rustc[..]--crate-name dep_build2 [..]-C embed-bitcode=no[..]`") + .with_stderr_contains("[..]`rustc[..]--crate-name dep_build [..]-C embed-bitcode=no[..]`") + .with_stderr_contains( + "[..]`rustc[..]--crate-name build_script_build [..]-C embed-bitcode=no[..]`", + ) + // proc macro deps are the same as build deps here + .with_stderr_contains( + "[..]`rustc[..]--crate-name dep_proc_macro2 [..]-C embed-bitcode=no[..]`", + ) + .with_stderr_contains( + "[..]`rustc[..]--crate-name dep_proc_macro [..]-C embed-bitcode=no[..]`", + ) + .with_stderr_contains( + "[..]`rustc[..]--crate-name foo_bin [..]--crate-type bin[..]-C lto[..]`", + ) + .with_stderr_contains( + "[..]`rustc[..]--crate-name test [..]--crate-type cdylib[..]-C lto[..]`", + ) + .with_stderr_contains("[..]`rustc[..]--crate-name dep_shared [..]`") + .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C lto[..]") + .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C linker-plugin-lto[..]") + .with_stderr_does_not_contain("[..]--crate-name dep_shared[..]-C embed-bitcode[..]") + .run(); +} + +#[cargo_test] +fn off_in_manifest_works() { + Package::new("bar", "0.0.1") + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.0.0" + + [dependencies] + bar = "*" + + [profile.release] + lto = "off" + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file( + "src/main.rs", + "fn main() { + test::foo(); + bar::foo(); + }", + ) + .build(); + p.cargo("build -v --release") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] [..] +[DOWNLOADED] [..] +[COMPILING] bar v0.0.1 +[RUNNING] `rustc --crate-name bar [..]--crate-type lib [..]-C lto=off -C embed-bitcode=no[..] +[COMPILING] test [..] +[RUNNING] `rustc --crate-name test [..]--crate-type lib [..]-C lto=off -C embed-bitcode=no[..] +[RUNNING] `rustc --crate-name test src/main.rs [..]--crate-type bin [..]-C lto=off[..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn between_builds() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.0.0" + + [profile.release] + lto = true + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file("src/main.rs", "fn main() { test::foo() }") + .build(); + p.cargo("build -v --release --lib") + .with_stderr( + "\ +[COMPILING] test [..] +[RUNNING] `rustc [..]--crate-type lib[..]-C linker-plugin-lto[..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("build -v --release") + .with_stderr_contains( + "\ +[COMPILING] test [..] +[RUNNING] `rustc [..]--crate-type bin[..]-C lto[..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn test_all() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + + [profile.release] + lto = true + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("tests/a.rs", "") + .file("tests/b.rs", "") + .build(); + p.cargo("test --release -v") + .with_stderr_contains("[RUNNING] `rustc[..]--crate-name foo[..]-C lto[..]") + .run(); +} + +#[cargo_test] +fn test_all_and_bench() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + + [profile.release] + lto = true + [profile.bench] + lto = true + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("tests/a.rs", "") + .file("tests/b.rs", "") + .build(); + p.cargo("test --release -v") + .with_stderr_contains("[RUNNING] `rustc[..]--crate-name a[..]-C lto[..]") + .with_stderr_contains("[RUNNING] `rustc[..]--crate-name b[..]-C lto[..]") + .with_stderr_contains("[RUNNING] `rustc[..]--crate-name foo[..]-C lto[..]") + .run(); +} + +/// Basic setup: +/// +/// foo v0.0.0 +/// ├── bar v0.0.0 +/// │ ├── registry v0.0.1 +/// │ └── registry-shared v0.0.1 +/// └── registry-shared v0.0.1 +/// +/// Where `bar` will have the given crate types. +fn project_with_dep(crate_types: &str) -> Project { + Package::new("registry", "0.0.1") + .file("src/lib.rs", r#"pub fn foo() { println!("registry"); }"#) + .publish(); + Package::new("registry-shared", "0.0.1") + .file("src/lib.rs", r#"pub fn foo() { println!("shared"); }"#) + .publish(); + + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + + [workspace] + + [dependencies] + bar = { path = 'bar' } + registry-shared = "*" + + [profile.release] + lto = true + "#, + ) + .file( + "src/main.rs", + " + fn main() { + bar::foo(); + registry_shared::foo(); + } + ", + ) + .file( + "bar/Cargo.toml", + &format!( + r#" + [package] + name = "bar" + version = "0.0.0" + + [dependencies] + registry = "*" + registry-shared = "*" + + [lib] + crate-type = [{}] + "#, + crate_types + ), + ) + .file( + "bar/src/lib.rs", + r#" + pub fn foo() { + println!("bar"); + registry::foo(); + registry_shared::foo(); + } + "#, + ) + .file("tests/a.rs", "") + .file("bar/tests/b.rs", "") + .build() +} + +/// Helper for checking which LTO behavior is used for a specific crate. +/// +/// `krate_info` is extra compiler flags used to distinguish this if the same +/// crate name is being built multiple times. +fn verify_lto(output: &Output, krate: &str, krate_info: &str, expected_lto: Lto) { + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + let mut matches = stderr.lines().filter(|line| { + line.contains("Running") + && line.contains(&format!("--crate-name {} ", krate)) + && line.contains(krate_info) + }); + let line = matches.next().unwrap_or_else(|| { + panic!( + "expected to find crate `{}` info: `{}`, not found in output:\n{}", + krate, krate_info, stderr + ); + }); + if let Some(line2) = matches.next() { + panic!( + "found multiple lines matching crate `{}` info: `{}`:\nline1:{}\nline2:{}\noutput:\n{}", + krate, krate_info, line, line2, stderr + ); + } + let actual_lto = if let Some(index) = line.find("-C lto=") { + let s = &line[index..]; + let end = s.find(' ').unwrap(); + let mode = &line[index..index + end]; + if mode == "off" { + Lto::Off + } else { + Lto::Run(Some(mode.into())) + } + } else if line.contains("-C lto") { + Lto::Run(None) + } else if line.contains("-C linker-plugin-lto") { + Lto::OnlyBitcode + } else if line.contains("-C embed-bitcode=no") { + Lto::OnlyObject + } else { + Lto::ObjectAndBitcode + }; + assert_eq!( + actual_lto, expected_lto, + "did not find expected LTO in line: {}", + line + ); +} + +#[cargo_test] +fn cdylib_and_rlib() { + let p = project_with_dep("'cdylib', 'rlib'"); + let output = p.cargo("build --release -v").exec_with_output().unwrap(); + // `registry` is ObjectAndBitcode because it needs Object for the + // rlib, and Bitcode for the cdylib (which doesn't support LTO). + verify_lto( + &output, + "registry", + "--crate-type lib", + Lto::ObjectAndBitcode, + ); + // Same as `registry` + verify_lto( + &output, + "registry_shared", + "--crate-type lib", + Lto::ObjectAndBitcode, + ); + // Same as `registry` + verify_lto( + &output, + "bar", + "--crate-type cdylib --crate-type rlib", + Lto::ObjectAndBitcode, + ); + verify_lto(&output, "foo", "--crate-type bin", Lto::Run(None)); + p.cargo("test --release -v") + .with_stderr_unordered( + "\ +[FRESH] registry v0.0.1 +[FRESH] registry-shared v0.0.1 +[FRESH] bar v0.0.0 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..]-C lto [..]--test[..] +[RUNNING] `rustc --crate-name a [..]-C lto [..]--test[..] +[FINISHED] [..] +[RUNNING] [..] +[RUNNING] [..] +", + ) + .run(); + p.cargo("build --release -v --manifest-path bar/Cargo.toml") + .with_stderr_unordered( + "\ +[FRESH] registry-shared v0.0.1 +[FRESH] registry v0.0.1 +[FRESH] bar v0.0.0 [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("test --release -v --manifest-path bar/Cargo.toml") + .with_stderr_unordered( + "\ +[FRESH] registry-shared v0.0.1 +[FRESH] registry v0.0.1 +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar [..]-C lto[..]--test[..] +[RUNNING] `rustc --crate-name b [..]-C lto[..]--test[..] +[FINISHED] [..] +[RUNNING] [..]target/release/deps/bar-[..] +[RUNNING] [..]target/release/deps/b-[..] +[DOCTEST] bar +[RUNNING] `rustdoc --crate-type cdylib --crate-type rlib --crate-name bar --test [..]-C lto[..] +", + ) + .run(); +} + +#[cargo_test] +fn dylib() { + let p = project_with_dep("'dylib'"); + let output = p.cargo("build --release -v").exec_with_output().unwrap(); + // `registry` is OnlyObject because rustc doesn't support LTO with dylibs. + verify_lto(&output, "registry", "--crate-type lib", Lto::OnlyObject); + // `registry_shared` is both because it is needed by both bar (Object) and + // foo (Bitcode for LTO). + verify_lto( + &output, + "registry_shared", + "--crate-type lib", + Lto::ObjectAndBitcode, + ); + // `bar` is OnlyObject because rustc doesn't support LTO with dylibs. + verify_lto(&output, "bar", "--crate-type dylib", Lto::OnlyObject); + // `foo` is LTO because it is a binary, and the profile specifies `lto=true`. + verify_lto(&output, "foo", "--crate-type bin", Lto::Run(None)); + // `cargo test` should not rebuild dependencies. It builds the test + // executables with `lto=true` because the tests are built with the + // `--release` flag. + p.cargo("test --release -v") + .with_stderr_unordered( + "\ +[FRESH] registry v0.0.1 +[FRESH] registry-shared v0.0.1 +[FRESH] bar v0.0.0 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..]-C lto [..]--test[..] +[RUNNING] `rustc --crate-name a [..]-C lto [..]--test[..] +[FINISHED] [..] +[RUNNING] [..] +[RUNNING] [..] +", + ) + .run(); + // Building just `bar` causes `registry-shared` to get rebuilt because it + // switches to OnlyObject because it is now only being used with a dylib + // which does not support LTO. + // + // `bar` gets rebuilt because `registry_shared` got rebuilt. + p.cargo("build --release -v --manifest-path bar/Cargo.toml") + .with_stderr_unordered( + "\ +[COMPILING] registry-shared v0.0.1 +[FRESH] registry v0.0.1 +[RUNNING] `rustc --crate-name registry_shared [..]-C embed-bitcode=no[..] +[DIRTY] bar v0.0.0 ([..]): dependency info changed +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar [..]--crate-type dylib [..]-C embed-bitcode=no[..] +[FINISHED] [..] +", + ) + .run(); + // Testing just `bar` causes `registry` to get rebuilt because it switches + // to needing both Object (for the `bar` dylib) and Bitcode (for the test + // built with LTO). + // + // `bar` the dylib gets rebuilt because `registry` got rebuilt. + p.cargo("test --release -v --manifest-path bar/Cargo.toml") + .with_stderr_unordered( + "\ +[FRESH] registry-shared v0.0.1 +[COMPILING] registry v0.0.1 +[RUNNING] `rustc --crate-name registry [..] +[DIRTY] bar v0.0.0 ([..]): dependency info changed +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar [..]--crate-type dylib [..]-C embed-bitcode=no[..] +[RUNNING] `rustc --crate-name bar [..]-C lto [..]--test[..] +[RUNNING] `rustc --crate-name b [..]-C lto [..]--test[..] +[FINISHED] [..] +[RUNNING] [..] +[RUNNING] [..] +", + ) + .run(); +} + +#[cargo_test] +// This is currently broken on windows-gnu, see https://github.com/rust-lang/rust/issues/109797 +#[cfg_attr( + all(target_os = "windows", target_env = "gnu"), + ignore = "windows-gnu not working" +)] +fn test_profile() { + Package::new("bar", "0.0.1") + .file("src/lib.rs", "pub fn foo() -> i32 { 123 } ") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [profile.test] + lto = 'thin' + + [dependencies] + bar = "*" + "#, + ) + .file( + "src/lib.rs", + r#" + #[test] + fn t1() { + assert_eq!(123, bar::foo()); + } + "#, + ) + .build(); + + p.cargo("test -v") + // unordered because the two `foo` builds start in parallel + .with_stderr_unordered("\ +[UPDATING] [..] +[DOWNLOADING] [..] +[DOWNLOADED] [..] +[COMPILING] bar v0.0.1 +[RUNNING] `rustc --crate-name bar [..]crate-type lib[..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..]--crate-type lib --emit=dep-info,metadata,link -C linker-plugin-lto[..] +[RUNNING] `rustc --crate-name foo [..]--emit=dep-info,link -C lto=thin [..]--test[..] +[FINISHED] [..] +[RUNNING] [..] +[DOCTEST] foo +[RUNNING] `rustdoc [..] +") + .run(); +} + +#[cargo_test] +fn doctest() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [profile.release] + lto = true + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file( + "src/lib.rs", + r#" + /// Foo! + /// + /// ``` + /// foo::foo(); + /// ``` + pub fn foo() { bar::bar(); } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file( + "bar/src/lib.rs", + r#" + pub fn bar() { println!("hi!"); } + "#, + ) + .build(); + + p.cargo("test --doc --release -v") + .with_stderr_contains("[..]`rustc --crate-name bar[..]-C linker-plugin-lto[..]") + .with_stderr_contains("[..]`rustc --crate-name foo[..]-C linker-plugin-lto[..]") + // embed-bitcode should be harmless here + .with_stderr_contains("[..]`rustdoc [..]-C lto[..]") + .run(); + + // Try with bench profile. + p.cargo("test --doc --release -v") + .env("CARGO_PROFILE_BENCH_LTO", "true") + .with_stderr_unordered( + "\ +[FRESH] bar v0.1.0 [..] +[FRESH] foo v0.1.0 [..] +[FINISHED] release [..] +[DOCTEST] foo +[RUNNING] `rustdoc [..]-C lto[..] +", + ) + .run(); +} + +#[cargo_test] +fn dylib_rlib_bin() { + // dylib+rlib linked with a binary + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [lib] + crate-type = ["dylib", "rlib"] + + [profile.release] + lto = true + "#, + ) + .file("src/lib.rs", "pub fn foo() { println!(\"hi!\"); }") + .file("src/bin/ferret.rs", "fn main() { foo::foo(); }") + .build(); + + let output = p.cargo("build --release -v").exec_with_output().unwrap(); + verify_lto( + &output, + "foo", + "--crate-type dylib --crate-type rlib", + Lto::ObjectAndBitcode, + ); + verify_lto(&output, "ferret", "--crate-type bin", Lto::Run(None)); +} + +#[cargo_test] +fn fresh_swapping_commands() { + // In some rare cases, different commands end up building dependencies + // with different LTO settings. This checks that it doesn't cause the + // cache to thrash in that scenario. + Package::new("bar", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + + [profile.release] + lto = true + "#, + ) + .file("src/lib.rs", "pub fn foo() { println!(\"hi!\"); }") + .build(); + + p.cargo("build --release -v") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 [..] +[COMPILING] bar v1.0.0 +[RUNNING] `rustc --crate-name bar [..]-C linker-plugin-lto[..] +[COMPILING] foo v0.1.0 [..] +[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C linker-plugin-lto[..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("test --release -v") + .with_stderr_unordered( + "\ +[FRESH] bar v1.0.0 +[COMPILING] foo v0.1.0 [..] +[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C lto[..]--test[..] +[FINISHED] [..] +[RUNNING] `[..]/foo[..]` +[DOCTEST] foo +[RUNNING] `rustdoc [..]-C lto[..] +", + ) + .run(); + + p.cargo("build --release -v") + .with_stderr( + "\ +[FRESH] bar v1.0.0 +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("test --release -v --no-run -v") + .with_stderr( + "\ +[FRESH] bar v1.0.0 +[FRESH] foo [..] +[FINISHED] [..] +[EXECUTABLE] `[..]/target/release/deps/foo-[..][EXE]` +", + ) + .run(); +} diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs new file mode 100644 index 0000000..66914e4 --- /dev/null +++ b/tests/testsuite/main.rs @@ -0,0 +1,145 @@ +// See src/cargo/lib.rs for notes on these lint settings. +#![warn(rust_2018_idioms)] +#![allow(clippy::all)] + +#[macro_use] +extern crate cargo_test_macro; + +mod advanced_env; +mod alt_registry; +mod artifact_dep; +mod bad_config; +mod bad_manifest_path; +mod bench; +mod binary_name; +mod build; +mod build_plan; +mod build_script; +mod build_script_env; +mod build_script_extra_link_arg; +mod cache_messages; +mod cargo_add; +mod cargo_alias_config; +mod cargo_command; +mod cargo_config; +mod cargo_env_config; +mod cargo_features; +mod cargo_remove; +mod cargo_targets; +mod cfg; +mod check; +mod check_cfg; +mod clean; +mod collisions; +mod concurrent; +mod config; +mod config_cli; +mod config_include; +mod corrupt_git; +mod credential_process; +mod cross_compile; +mod cross_publish; +mod custom_target; +mod death; +mod dep_info; +mod directory; +mod doc; +mod docscrape; +mod edition; +mod error; +mod features; +mod features2; +mod features_namespaced; +mod fetch; +mod fix; +mod freshness; +mod future_incompat_report; +mod generate_lockfile; +mod git; +mod git_auth; +mod git_gc; +mod glob_targets; +mod help; +mod https; +mod inheritable_workspace_fields; +mod init; +mod install; +mod install_upgrade; +mod jobserver; +mod list_availables; +mod local_registry; +mod locate_project; +mod lockfile_compat; +mod login; +mod logout; +mod lto; +mod member_discovery; +mod member_errors; +mod message_format; +mod messages; +mod metabuild; +mod metadata; +mod minimal_versions; +mod multitarget; +mod net_config; +mod new; +mod offline; +mod old_cargos; +mod out_dir; +mod owner; +mod package; +mod package_features; +mod patch; +mod path; +mod paths; +mod pkgid; +mod plugins; +mod proc_macro; +mod profile_config; +mod profile_custom; +mod profile_overrides; +mod profile_targets; +mod profiles; +mod progress; +mod pub_priv; +mod publish; +mod publish_lockfile; +mod read_manifest; +mod registry; +mod registry_auth; +mod rename_deps; +mod replace; +mod required_features; +mod run; +mod rust_version; +mod rustc; +mod rustc_info_cache; +mod rustdoc; +mod rustdoc_extern_html; +mod rustdocflags; +mod rustflags; +mod search; +mod shell_quoting; +mod source_replacement; +mod ssh; +mod standard_lib; +mod test; +mod timings; +mod tool_paths; +mod tree; +mod tree_graph_features; +mod unit_graph; +mod update; +mod vendor; +mod verify_project; +mod version; +mod warn_on_failure; +mod weak_dep_features; +mod workspaces; +mod yank; + +#[cargo_test] +fn aaa_trigger_cross_compile_disabled_check() { + // This triggers the cross compile disabled check to run ASAP, see #5141 + cargo_test_support::cross_compile::disabled(); +} diff --git a/tests/testsuite/member_discovery.rs b/tests/testsuite/member_discovery.rs new file mode 100644 index 0000000..5377443 --- /dev/null +++ b/tests/testsuite/member_discovery.rs @@ -0,0 +1,44 @@ +//! Tests for workspace member discovery. + +use cargo::core::{Shell, Workspace}; +use cargo::util::config::Config; + +use cargo_test_support::install::cargo_home; +use cargo_test_support::project; +use cargo_test_support::registry; + +/// Tests exclusion of non-directory files from workspace member discovery using glob `*`. +#[cargo_test] +fn bad_file_member_exclusion() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [ "crates/*" ] + "#, + ) + .file("crates/.DS_Store", "PLACEHOLDER") + .file( + "crates/bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#, + ) + .file("crates/bar/src/main.rs", "fn main() {}") + .build(); + + // Prevent this test from accessing the network by setting up .cargo/config. + registry::init(); + let config = Config::new( + Shell::from_write(Box::new(Vec::new())), + cargo_home(), + cargo_home(), + ); + let ws = Workspace::new(&p.root().join("Cargo.toml"), &config).unwrap(); + assert_eq!(ws.members().count(), 1); + assert_eq!(ws.members().next().unwrap().name(), "bar"); +} diff --git a/tests/testsuite/member_errors.rs b/tests/testsuite/member_errors.rs new file mode 100644 index 0000000..c3c340c --- /dev/null +++ b/tests/testsuite/member_errors.rs @@ -0,0 +1,164 @@ +//! Tests for workspace member errors. + +use cargo::core::resolver::ResolveError; +use cargo::core::{compiler::CompileMode, Shell, Workspace}; +use cargo::ops::{self, CompileOptions}; +use cargo::util::{config::Config, errors::ManifestError}; + +use cargo_test_support::install::cargo_home; +use cargo_test_support::project; +use cargo_test_support::registry; + +/// Tests inclusion of a `ManifestError` pointing to a member manifest +/// when that manifest fails to deserialize. +#[cargo_test] +fn toml_deserialize_manifest_error() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + foobar == "0.55" + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + let root_manifest_path = p.root().join("Cargo.toml"); + let member_manifest_path = p.root().join("bar").join("Cargo.toml"); + + let error = Workspace::new(&root_manifest_path, &Config::default().unwrap()).unwrap_err(); + eprintln!("{:?}", error); + + let manifest_err: &ManifestError = error.downcast_ref().expect("Not a ManifestError"); + assert_eq!(manifest_err.manifest_path(), &root_manifest_path); + + let causes: Vec<_> = manifest_err.manifest_causes().collect(); + assert_eq!(causes.len(), 1, "{:?}", causes); + assert_eq!(causes[0].manifest_path(), &member_manifest_path); +} + +/// Tests inclusion of a `ManifestError` pointing to a member manifest +/// when that manifest has an invalid dependency path. +#[cargo_test] +fn member_manifest_path_io_error() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + foobar = { path = "nosuch" } + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + let root_manifest_path = p.root().join("Cargo.toml"); + let member_manifest_path = p.root().join("bar").join("Cargo.toml"); + let missing_manifest_path = p.root().join("bar").join("nosuch").join("Cargo.toml"); + + let error = Workspace::new(&root_manifest_path, &Config::default().unwrap()).unwrap_err(); + eprintln!("{:?}", error); + + let manifest_err: &ManifestError = error.downcast_ref().expect("Not a ManifestError"); + assert_eq!(manifest_err.manifest_path(), &root_manifest_path); + + let causes: Vec<_> = manifest_err.manifest_causes().collect(); + assert_eq!(causes.len(), 2, "{:?}", causes); + assert_eq!(causes[0].manifest_path(), &member_manifest_path); + assert_eq!(causes[1].manifest_path(), &missing_manifest_path); +} + +/// Tests dependency version errors provide which package failed via a `ResolveError`. +#[cargo_test] +fn member_manifest_version_error() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + i-dont-exist = "0.55" + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + // Prevent this test from accessing the network by setting up .cargo/config. + registry::init(); + let config = Config::new( + Shell::from_write(Box::new(Vec::new())), + cargo_home(), + cargo_home(), + ); + let ws = Workspace::new(&p.root().join("Cargo.toml"), &config).unwrap(); + let compile_options = CompileOptions::new(&config, CompileMode::Build).unwrap(); + let member_bar = ws.members().find(|m| &*m.name() == "bar").unwrap(); + + let error = ops::compile(&ws, &compile_options).map(|_| ()).unwrap_err(); + eprintln!("{:?}", error); + + let resolve_err: &ResolveError = error.downcast_ref().expect("Not a ResolveError"); + let package_path = resolve_err.package_path(); + assert_eq!(package_path.len(), 1, "package_path: {:?}", package_path); + assert_eq!(package_path[0], member_bar.package_id()); +} diff --git a/tests/testsuite/message_format.rs b/tests/testsuite/message_format.rs new file mode 100644 index 0000000..e9310b2 --- /dev/null +++ b/tests/testsuite/message_format.rs @@ -0,0 +1,133 @@ +//! Tests for --message-format flag. + +use cargo_test_support::{basic_lib_manifest, basic_manifest, project}; + +#[cargo_test] +fn cannot_specify_two() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + let formats = ["human", "json", "short"]; + + let two_kinds = "error: cannot specify two kinds of `message-format` arguments\n"; + for a in formats.iter() { + for b in formats.iter() { + p.cargo(&format!("build --message-format {},{}", a, b)) + .with_status(101) + .with_stderr(two_kinds) + .run(); + } + } +} + +#[cargo_test] +fn double_json_works() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check --message-format json,json-render-diagnostics") + .run(); + p.cargo("check --message-format json,json-diagnostic-short") + .run(); + p.cargo("check --message-format json,json-diagnostic-rendered-ansi") + .run(); + p.cargo("check --message-format json --message-format json-diagnostic-rendered-ansi") + .run(); + p.cargo("check --message-format json-diagnostic-rendered-ansi") + .run(); + p.cargo("check --message-format json-diagnostic-short,json-diagnostic-rendered-ansi") + .run(); +} + +#[cargo_test] +fn cargo_renders() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + + [dependencies] + bar = { path = 'bar' } + "#, + ) + .file("src/main.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check --message-format json-render-diagnostics") + .with_status(101) + .with_stdout( + "{\"reason\":\"compiler-artifact\",[..]\n\ + {\"reason\":\"build-finished\",\"success\":false}", + ) + .with_stderr_contains( + "\ +[CHECKING] bar [..] +[CHECKING] foo [..] +error[..]`main`[..] +", + ) + .run(); +} + +#[cargo_test] +fn cargo_renders_short() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "") + .build(); + + p.cargo("check --message-format json-render-diagnostics,json-diagnostic-short") + .with_status(101) + .with_stderr_contains( + "\ +[CHECKING] foo [..] +error[..]`main`[..] +", + ) + .with_stderr_does_not_contain("note:") + .run(); +} + +#[cargo_test] +fn cargo_renders_ansi() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/main.rs", "") + .build(); + + p.cargo("check --message-format json-diagnostic-rendered-ansi") + .with_status(101) + .with_stdout_contains("[..]\\u001b[38;5;9merror[..]") + .run(); +} + +#[cargo_test] +fn cargo_renders_doctests() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/lib.rs", + "\ + /// ```rust + /// bar() + /// ``` + pub fn bar() {} + ", + ) + .build(); + + p.cargo("test --doc --message-format short") + .with_status(101) + .with_stdout_contains("src/lib.rs:2:1: error[E0425]:[..]") + .with_stdout_contains("[..]src/lib.rs - bar (line 1)[..]") + .run(); +} diff --git a/tests/testsuite/messages.rs b/tests/testsuite/messages.rs new file mode 100644 index 0000000..0ccff15 --- /dev/null +++ b/tests/testsuite/messages.rs @@ -0,0 +1,144 @@ +//! General tests specifically about diagnostics and other messages. +//! +//! Tests for message caching can be found in `cache_messages`. + +use cargo_test_support::{process, project, Project}; +use cargo_util::ProcessError; + +/// Captures the actual diagnostics displayed by rustc. This is done to avoid +/// relying on the exact message formatting in rustc. +pub fn raw_rustc_output(project: &Project, path: &str, extra: &[&str]) -> String { + let mut proc = process("rustc"); + if cfg!(windows) { + // Sanitize in case the caller wants to do direct string comparison with Cargo's output. + proc.arg(path.replace('/', "\\")); + } else { + proc.arg(path); + } + let rustc_output = match proc + .arg("--crate-type=lib") + .args(extra) + .cwd(project.root()) + .exec_with_output() + { + Ok(output) => output.stderr, + Err(e) => e.downcast::().unwrap().stderr.unwrap(), + }; + // Do a little dance to remove rustc's "warnings emitted" message and the subsequent newline. + let stderr = std::str::from_utf8(&rustc_output).expect("utf8"); + let mut lines = stderr.lines(); + let mut result = String::new(); + while let Some(line) = lines.next() { + if line.contains("warning emitted") + || line.contains("warnings emitted") + || line.contains("aborting due to") + { + // Eat blank line. + match lines.next() { + None | Some("") => continue, + Some(s) => panic!("unexpected str {}", s), + } + } + result.push_str(line); + result.push('\n'); + } + result +} + +#[cargo_test] +fn deduplicate_messages_basic() { + let p = project() + .file( + "src/lib.rs", + r#" + pub fn foo() { + let x = 1; + } + "#, + ) + .build(); + let rustc_message = raw_rustc_output(&p, "src/lib.rs", &[]); + let expected_output = format!( + "{}\ +warning: `foo` (lib) generated 1 warning (run `cargo fix --lib -p foo` to apply 1 suggestion) +warning: `foo` (lib test) generated 1 warning (1 duplicate) +[FINISHED] [..] +[EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[..][EXE]) +", + rustc_message + ); + p.cargo("test --no-run -j1") + .with_stderr(&format!("[COMPILING] foo [..]\n{}", expected_output)) + .run(); + // Run again, to check for caching behavior. + p.cargo("test --no-run -j1") + .with_stderr(expected_output) + .run(); +} + +#[cargo_test] +fn deduplicate_messages_mismatched_warnings() { + // One execution prints 1 warning, the other prints 2 where there is an overlap. + let p = project() + .file( + "src/lib.rs", + r#" + pub fn foo() { + let x = 1; + } + + #[test] + fn t1() { + let MY_VALUE = 1; + assert_eq!(MY_VALUE, 1); + } + "#, + ) + .build(); + let lib_output = raw_rustc_output(&p, "src/lib.rs", &[]); + let mut lib_test_output = raw_rustc_output(&p, "src/lib.rs", &["--test"]); + // Remove the duplicate warning. + let start = lib_test_output.find(&lib_output).expect("same warning"); + lib_test_output.replace_range(start..start + lib_output.len(), ""); + let expected_output = format!( + "\ +{}\ +warning: `foo` (lib) generated 1 warning (run `cargo fix --lib -p foo` to apply 1 suggestion) +{}\ +warning: `foo` (lib test) generated 2 warnings (1 duplicate) +[FINISHED] [..] +[EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[..][EXE]) +", + lib_output, lib_test_output + ); + p.cargo("test --no-run -j1") + .with_stderr(&format!("[COMPILING] foo v0.0.1 [..]\n{}", expected_output)) + .run(); + // Run again, to check for caching behavior. + p.cargo("test --no-run -j1") + .with_stderr(expected_output) + .run(); +} + +#[cargo_test] +fn deduplicate_errors() { + let p = project() + .file( + "src/lib.rs", + r#" + this should not compile + "#, + ) + .build(); + let rustc_message = raw_rustc_output(&p, "src/lib.rs", &[]); + p.cargo("test -j1") + .with_status(101) + .with_stderr(&format!( + "\ +[COMPILING] foo v0.0.1 [..] +{}error: could not compile `foo` due to previous error +", + rustc_message + )) + .run(); +} diff --git a/tests/testsuite/metabuild.rs b/tests/testsuite/metabuild.rs new file mode 100644 index 0000000..022d0bf --- /dev/null +++ b/tests/testsuite/metabuild.rs @@ -0,0 +1,771 @@ +//! Tests for the metabuild feature (declarative build scripts). + +use cargo_test_support::{ + basic_lib_manifest, basic_manifest, is_coarse_mtime, project, registry::Package, rustc_host, + Project, +}; + +use std::str; + +#[cargo_test] +fn metabuild_gated() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + metabuild = ["mb"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + feature `metabuild` is required + + The package requires the Cargo feature called `metabuild`, \ + but that feature is not stabilized in this version of Cargo (1.[..]). + Consider adding `cargo-features = [\"metabuild\"]` to the top of Cargo.toml \ + (above the [package] table) to tell Cargo you are opting in to use this unstable feature. + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#metabuild \ + for more information about the status of this feature. +", + ) + .run(); +} + +fn basic_project() -> Project { + project() + .file( + "Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "foo" + version = "0.0.1" + metabuild = ["mb", "mb-other"] + + [build-dependencies] + mb = {path="mb"} + mb-other = {path="mb-other"} + "#, + ) + .file("src/lib.rs", "") + .file("mb/Cargo.toml", &basic_lib_manifest("mb")) + .file( + "mb/src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb"); }"#, + ) + .file( + "mb-other/Cargo.toml", + r#" + [package] + name = "mb-other" + version = "0.0.1" + "#, + ) + .file( + "mb-other/src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb-other"); }"#, + ) + .build() +} + +#[cargo_test] +fn metabuild_basic() { + let p = basic_project(); + p.cargo("check -vv") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_stdout_contains("[foo 0.0.1] Hello mb") + .with_stdout_contains("[foo 0.0.1] Hello mb-other") + .run(); +} + +#[cargo_test] +fn metabuild_error_both() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "foo" + version = "0.0.1" + metabuild = "mb" + + [build-dependencies] + mb = {path="mb"} + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", r#"fn main() {}"#) + .file("mb/Cargo.toml", &basic_lib_manifest("mb")) + .file( + "mb/src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb"); }"#, + ) + .build(); + + p.cargo("check -vv") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_status(101) + .with_stderr_contains( + "\ +error: failed to parse manifest at [..] + +Caused by: + cannot specify both `metabuild` and `build` +", + ) + .run(); +} + +#[cargo_test] +fn metabuild_missing_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "foo" + version = "0.0.1" + metabuild = "mb" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -vv") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_status(101) + .with_stderr_contains( + "\ +error: failed to parse manifest at [..] + +Caused by: + metabuild package `mb` must be specified in `build-dependencies`", + ) + .run(); +} + +#[cargo_test] +fn metabuild_optional_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "foo" + version = "0.0.1" + metabuild = "mb" + + [build-dependencies] + mb = {path="mb", optional=true} + "#, + ) + .file("src/lib.rs", "") + .file("mb/Cargo.toml", &basic_lib_manifest("mb")) + .file( + "mb/src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb"); }"#, + ) + .build(); + + p.cargo("check -vv") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_stdout_does_not_contain("[foo 0.0.1] Hello mb") + .run(); + + p.cargo("check -vv --features mb") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_stdout_contains("[foo 0.0.1] Hello mb") + .run(); +} + +#[cargo_test] +fn metabuild_lib_name() { + // Test when setting `name` on [lib]. + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "foo" + version = "0.0.1" + metabuild = "mb" + + [build-dependencies] + mb = {path="mb"} + "#, + ) + .file("src/lib.rs", "") + .file( + "mb/Cargo.toml", + r#" + [package] + name = "mb" + version = "0.0.1" + [lib] + name = "other" + "#, + ) + .file( + "mb/src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb"); }"#, + ) + .build(); + + p.cargo("check -vv") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_stdout_contains("[foo 0.0.1] Hello mb") + .run(); +} + +#[cargo_test] +fn metabuild_fresh() { + if is_coarse_mtime() { + // This test doesn't work on coarse mtimes very well. Because the + // metabuild script is created at build time, its mtime is almost + // always equal to the mtime of the output. The second call to `build` + // will then think it needs to be rebuilt when it should be fresh. + return; + } + + // Check that rebuild is fresh. + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "foo" + version = "0.0.1" + metabuild = "mb" + + [build-dependencies] + mb = {path="mb"} + "#, + ) + .file("src/lib.rs", "") + .file("mb/Cargo.toml", &basic_lib_manifest("mb")) + .file( + "mb/src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb"); }"#, + ) + .build(); + + p.cargo("check -vv") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_stdout_contains("[foo 0.0.1] Hello mb") + .run(); + + p.cargo("check -vv") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_stdout_does_not_contain("[foo 0.0.1] Hello mb") + .with_stderr( + "\ +[FRESH] mb [..] +[FRESH] foo [..] +[FINISHED] dev [..] +", + ) + .run(); +} + +#[cargo_test] +fn metabuild_links() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "foo" + version = "0.0.1" + links = "cat" + metabuild = "mb" + + [build-dependencies] + mb = {path="mb"} + "#, + ) + .file("src/lib.rs", "") + .file("mb/Cargo.toml", &basic_lib_manifest("mb")) + .file( + "mb/src/lib.rs", + r#" + pub fn metabuild() { + assert_eq!(std::env::var("CARGO_MANIFEST_LINKS"), + Ok("cat".to_string())); + println!("Hello mb"); + } + "#, + ) + .build(); + + p.cargo("check -vv") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_stdout_contains("[foo 0.0.1] Hello mb") + .run(); +} + +#[cargo_test] +fn metabuild_override() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "foo" + version = "0.0.1" + links = "cat" + metabuild = "mb" + + [build-dependencies] + mb = {path="mb"} + "#, + ) + .file("src/lib.rs", "") + .file("mb/Cargo.toml", &basic_lib_manifest("mb")) + .file( + "mb/src/lib.rs", + r#"pub fn metabuild() { panic!("should not run"); }"#, + ) + .file( + ".cargo/config", + &format!( + r#" + [target.{}.cat] + rustc-link-lib = ["a"] + "#, + rustc_host() + ), + ) + .build(); + + p.cargo("check -vv") + .masquerade_as_nightly_cargo(&["metabuild"]) + .run(); +} + +#[cargo_test] +fn metabuild_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["member1", "member2"] + "#, + ) + .file( + "member1/Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "member1" + version = "0.0.1" + metabuild = ["mb1", "mb2"] + + [build-dependencies] + mb1 = {path="../../mb1"} + mb2 = {path="../../mb2"} + "#, + ) + .file("member1/src/lib.rs", "") + .file( + "member2/Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "member2" + version = "0.0.1" + metabuild = ["mb1"] + + [build-dependencies] + mb1 = {path="../../mb1"} + "#, + ) + .file("member2/src/lib.rs", "") + .build(); + + project() + .at("mb1") + .file("Cargo.toml", &basic_lib_manifest("mb1")) + .file( + "src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb1 {}", std::env::var("CARGO_MANIFEST_DIR").unwrap()); }"#, + ) + .build(); + + project() + .at("mb2") + .file("Cargo.toml", &basic_lib_manifest("mb2")) + .file( + "src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb2 {}", std::env::var("CARGO_MANIFEST_DIR").unwrap()); }"#, + ) + .build(); + + p.cargo("check -vv --workspace") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_stdout_contains("[member1 0.0.1] Hello mb1 [..]member1") + .with_stdout_contains("[member1 0.0.1] Hello mb2 [..]member1") + .with_stdout_contains("[member2 0.0.1] Hello mb1 [..]member2") + .with_stdout_does_not_contain("[member2 0.0.1] Hello mb2 [..]member2") + .run(); +} + +#[cargo_test] +fn metabuild_metadata() { + // The metabuild Target is filtered out of the `metadata` results. + let p = basic_project(); + + let meta = p + .cargo("metadata --format-version=1") + .masquerade_as_nightly_cargo(&["metabuild"]) + .run_json(); + let mb_info: Vec<&str> = meta["packages"] + .as_array() + .unwrap() + .iter() + .find(|p| p["name"].as_str().unwrap() == "foo") + .unwrap()["metabuild"] + .as_array() + .unwrap() + .iter() + .map(|s| s.as_str().unwrap()) + .collect(); + assert_eq!(mb_info, ["mb", "mb-other"]); +} + +#[cargo_test] +fn metabuild_build_plan() { + let p = basic_project(); + + p.cargo("build --build-plan -Zunstable-options") + .masquerade_as_nightly_cargo(&["metabuild", "build-plan"]) + .with_json( + r#" + { + "invocations": [ + { + "package_name": "mb", + "package_version": "0.5.0", + "target_kind": ["lib"], + "compile_mode": "build", + "kind": null, + "deps": [], + "outputs": [ + "[..]/target/debug/deps/libmb-[..].rlib", + "[..]/target/debug/deps/libmb-[..].rmeta" + ], + "links": {}, + "program": "rustc", + "args": "{...}", + "env": "{...}", + "cwd": "[..]" + }, + { + "package_name": "mb-other", + "package_version": "0.0.1", + "target_kind": ["lib"], + "compile_mode": "build", + "kind": null, + "deps": [], + "outputs": [ + "[..]/target/debug/deps/libmb_other-[..].rlib", + "[..]/target/debug/deps/libmb_other-[..].rmeta" + ], + "links": {}, + "program": "rustc", + "args": "{...}", + "env": "{...}", + "cwd": "[..]" + }, + { + "package_name": "foo", + "package_version": "0.0.1", + "target_kind": ["custom-build"], + "compile_mode": "build", + "kind": null, + "deps": [0, 1], + "outputs": "{...}", + "links": "{...}", + "program": "rustc", + "args": "{...}", + "env": "{...}", + "cwd": "[..]" + }, + { + "package_name": "foo", + "package_version": "0.0.1", + "target_kind": ["custom-build"], + "compile_mode": "run-custom-build", + "kind": null, + "deps": [2], + "outputs": [], + "links": {}, + "program": "[..]/foo/target/debug/build/foo-[..]/metabuild-foo", + "args": [], + "env": "{...}", + "cwd": "[..]" + }, + { + "package_name": "foo", + "package_version": "0.0.1", + "target_kind": ["lib"], + "compile_mode": "build", + "kind": null, + "deps": [3], + "outputs": [ + "[..]/foo/target/debug/deps/libfoo-[..].rlib", + "[..]/foo/target/debug/deps/libfoo-[..].rmeta" + ], + "links": "{...}", + "program": "rustc", + "args": "{...}", + "env": "{...}", + "cwd": "[..]" + } + ], + "inputs": [ + "[..]/foo/Cargo.toml", + "[..]/foo/mb/Cargo.toml", + "[..]/foo/mb-other/Cargo.toml" + ] + } + "#, + ) + .run(); + + assert_eq!(p.glob("target/.metabuild/metabuild-foo-*.rs").count(), 1); +} + +#[cargo_test] +fn metabuild_two_versions() { + // Two versions of a metabuild dep with the same name. + let p = project() + .at("ws") + .file( + "Cargo.toml", + r#" + [workspace] + members = ["member1", "member2"] + "#, + ) + .file( + "member1/Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "member1" + version = "0.0.1" + metabuild = ["mb"] + + [build-dependencies] + mb = {path="../../mb1"} + "#, + ) + .file("member1/src/lib.rs", "") + .file( + "member2/Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "member2" + version = "0.0.1" + metabuild = ["mb"] + + [build-dependencies] + mb = {path="../../mb2"} + "#, + ) + .file("member2/src/lib.rs", "") + .build(); + + project().at("mb1") + .file("Cargo.toml", r#" + [package] + name = "mb" + version = "0.0.1" + "#) + .file( + "src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb1 {}", std::env::var("CARGO_MANIFEST_DIR").unwrap()); }"#, + ) + .build(); + + project().at("mb2") + .file("Cargo.toml", r#" + [package] + name = "mb" + version = "0.0.2" + "#) + .file( + "src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb2 {}", std::env::var("CARGO_MANIFEST_DIR").unwrap()); }"#, + ) + .build(); + + p.cargo("check -vv --workspace") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_stdout_contains("[member1 0.0.1] Hello mb1 [..]member1") + .with_stdout_contains("[member2 0.0.1] Hello mb2 [..]member2") + .run(); + + assert_eq!( + p.glob("target/.metabuild/metabuild-member?-*.rs").count(), + 2 + ); +} + +#[cargo_test] +fn metabuild_external_dependency() { + Package::new("mb", "1.0.0") + .file("Cargo.toml", &basic_manifest("mb", "1.0.0")) + .file( + "src/lib.rs", + r#"pub fn metabuild() { println!("Hello mb"); }"#, + ) + .publish(); + Package::new("dep", "1.0.0") + .file( + "Cargo.toml", + r#" + cargo-features = ["metabuild"] + [package] + name = "dep" + version = "1.0.0" + metabuild = ["mb"] + + [build-dependencies] + mb = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build_dep("mb", "1.0.0") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + [dependencies] + dep = "1.0" + "#, + ) + .file("src/lib.rs", "extern crate dep;") + .build(); + + p.cargo("check -vv") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_stdout_contains("[dep 1.0.0] Hello mb") + .run(); + + assert_eq!(p.glob("target/.metabuild/metabuild-dep-*.rs").count(), 1); +} + +#[cargo_test] +fn metabuild_json_artifact() { + let p = basic_project(); + p.cargo("check --message-format=json") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_json_contains_unordered( + r#" + { + "executable": null, + "features": [], + "filenames": "{...}", + "fresh": false, + "package_id": "foo [..]", + "manifest_path": "[..]", + "profile": "{...}", + "reason": "compiler-artifact", + "target": { + "crate_types": [ + "bin" + ], + "doc": false, + "doctest": false, + "edition": "2018", + "kind": [ + "custom-build" + ], + "name": "metabuild-foo", + "src_path": "[..]/foo/target/.metabuild/metabuild-foo-[..].rs", + "test": false + } + } + + { + "cfgs": [], + "env": [], + "linked_libs": [], + "linked_paths": [], + "package_id": "foo [..]", + "out_dir": "[..]", + "reason": "build-script-executed" + } + "#, + ) + .run(); +} + +#[cargo_test] +fn metabuild_failed_build_json() { + let p = basic_project(); + // Modify the metabuild dep so that it fails to compile. + p.change_file("mb/src/lib.rs", ""); + p.cargo("check --message-format=json") + .masquerade_as_nightly_cargo(&["metabuild"]) + .with_status(101) + .with_json_contains_unordered( + r#" + { + "message": { + "children": "{...}", + "code": "{...}", + "level": "error", + "message": "cannot find function `metabuild` in [..] `mb`", + "rendered": "{...}", + "spans": "{...}" + }, + "package_id": "foo [..]", + "manifest_path": "[..]", + "reason": "compiler-message", + "target": { + "crate_types": [ + "bin" + ], + "doc": false, + "doctest": false, + "edition": "2018", + "kind": [ + "custom-build" + ], + "name": "metabuild-foo", + "src_path": null, + "test": false + } + } + "#, + ) + .run(); +} diff --git a/tests/testsuite/metadata.rs b/tests/testsuite/metadata.rs new file mode 100644 index 0000000..547916e --- /dev/null +++ b/tests/testsuite/metadata.rs @@ -0,0 +1,4192 @@ +//! Tests for the `cargo metadata` command. + +use cargo_test_support::install::cargo_home; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, main_file, project, rustc_host}; +use serde_json::json; + +#[cargo_test] +fn cargo_metadata_simple() { + let p = project() + .file("src/foo.rs", "") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .build(); + + p.cargo("metadata") + .with_json( + r#" + { + "packages": [ + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "name": "foo", + "version": "0.5.0", + "id": "foo[..]", + "keywords": [], + "source": null, + "dependencies": [], + "edition": "2015", + "license": null, + "license_file": null, + "links": null, + "description": null, + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "homepage": null, + "documentation": null, + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "test": true, + "edition": "2015", + "name": "foo", + "src_path": "[..]/foo/src/foo.rs" + } + ], + "features": {}, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null + } + ], + "workspace_members": ["foo 0.5.0 (path+file:[..]foo)"], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "foo 0.5.0 (path+file:[..]foo)" + } + ], + "root": "foo 0.5.0 (path+file:[..]foo)" + }, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_warns_on_implicit_version() { + let p = project() + .file("src/foo.rs", "") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .build(); + + p.cargo("metadata").with_stderr("[WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems").run(); + + p.cargo("metadata --format-version 1").with_stderr("").run(); +} + +#[cargo_test] +fn library_with_several_crate_types() { + let p = project() + .file("src/lib.rs", "") + .file( + "Cargo.toml", + r#" +[package] +name = "foo" +version = "0.5.0" + +[lib] +crate-type = ["lib", "staticlib"] + "#, + ) + .build(); + + p.cargo("metadata") + .with_json( + r#" + { + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "name": "foo", + "readme": null, + "repository": null, + "homepage": null, + "documentation": null, + "version": "0.5.0", + "rust_version": null, + "id": "foo[..]", + "keywords": [], + "source": null, + "dependencies": [], + "edition": "2015", + "license": null, + "license_file": null, + "links": null, + "description": null, + "targets": [ + { + "kind": [ + "lib", + "staticlib" + ], + "crate_types": [ + "lib", + "staticlib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "name": "foo", + "src_path": "[..]/foo/src/lib.rs" + } + ], + "features": {}, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null + } + ], + "workspace_members": ["foo 0.5.0 (path+file:[..]foo)"], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "foo 0.5.0 (path+file:[..]foo)" + } + ], + "root": "foo 0.5.0 (path+file:[..]foo)" + }, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); +} + +#[cargo_test] +fn library_with_features() { + let p = project() + .file("src/lib.rs", "") + .file( + "Cargo.toml", + r#" +[package] +name = "foo" +version = "0.5.0" + +[features] +default = ["default_feat"] +default_feat = [] +optional_feat = [] + "#, + ) + .build(); + + p.cargo("metadata") + .with_json( + r#" + { + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "name": "foo", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "version": "0.5.0", + "id": "foo[..]", + "keywords": [], + "source": null, + "dependencies": [], + "edition": "2015", + "license": null, + "license_file": null, + "links": null, + "description": null, + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "name": "foo", + "src_path": "[..]/foo/src/lib.rs" + } + ], + "features": { + "default": [ + "default_feat" + ], + "default_feat": [], + "optional_feat": [] + }, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null + } + ], + "workspace_members": ["foo 0.5.0 (path+file:[..]foo)"], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [ + "default", + "default_feat" + ], + "id": "foo 0.5.0 (path+file:[..]foo)" + } + ], + "root": "foo 0.5.0 (path+file:[..]foo)" + }, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_with_deps_and_version() { + let p = project() + .file("src/foo.rs", "") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + license = "MIT" + description = "foo" + + [[bin]] + name = "foo" + + [dependencies] + bar = "*" + [dev-dependencies] + foobar = "*" + "#, + ) + .build(); + Package::new("baz", "0.0.1").publish(); + Package::new("foobar", "0.0.1").publish(); + Package::new("bar", "0.0.1").dep("baz", "0.0.1").publish(); + + p.cargo("metadata -q --format-version 1") + .with_json( + r#" + { + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [ + { + "features": [], + "kind": null, + "name": "baz", + "optional": false, + "registry": null, + "rename": null, + "req": "^0.0.1", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "target": null, + "uses_default_features": true + } + ], + "description": null, + "edition": "2015", + "features": {}, + "id": "bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null, + "name": "bar", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "bar", + "src_path": "[..]src/lib.rs" + } + ], + "version": "0.0.1" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "edition": "2015", + "features": {}, + "id": "baz 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null, + "name": "baz", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "baz", + "src_path": "[..]src/lib.rs" + } + ], + "version": "0.0.1" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [ + { + "features": [], + "kind": null, + "name": "bar", + "optional": false, + "registry": null, + "rename": null, + "req": "*", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": "dev", + "name": "foobar", + "optional": false, + "registry": null, + "rename": null, + "req": "*", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "target": null, + "uses_default_features": true + } + ], + "description": "foo", + "edition": "2015", + "features": {}, + "id": "foo 0.5.0 (path+file:[..]foo)", + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null, + "name": "foo", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": null, + "targets": [ + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "test": true, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "foo", + "src_path": "[..]src/foo.rs" + } + ], + "version": "0.5.0" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "edition": "2015", + "features": {}, + "id": "foobar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null, + "name": "foobar", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "foobar", + "src_path": "[..]src/lib.rs" + } + ], + "version": "0.0.1" + } + ], + "resolve": { + "nodes": [ + { + "dependencies": [ + "baz 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "dep_kinds": [ + { + "kind": null, + "target": null + } + ], + "name": "baz", + "pkg": "baz 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + } + ], + "features": [], + "id": "bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "baz 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + }, + { + "dependencies": [ + "bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "foobar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "dep_kinds": [ + { + "kind": null, + "target": null + } + ], + "name": "bar", + "pkg": "bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + }, + { + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ], + "name": "foobar", + "pkg": "foobar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + } + ], + "features": [], + "id": "foo 0.5.0 (path+file:[..]foo)" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "foobar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + } + ], + "root": "foo 0.5.0 (path+file:[..]foo)" + }, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_members": [ + "foo 0.5.0 (path+file:[..]foo)" + ], + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); +} + +#[cargo_test] +fn example() { + let p = project() + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .file( + "Cargo.toml", + r#" +[package] +name = "foo" +version = "0.1.0" + +[[example]] +name = "ex" + "#, + ) + .build(); + + p.cargo("metadata") + .with_json( + r#" + { + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "name": "foo", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "version": "0.1.0", + "id": "foo[..]", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "description": null, + "edition": "2015", + "source": null, + "dependencies": [], + "targets": [ + { + "kind": [ "lib" ], + "crate_types": [ "lib" ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "name": "foo", + "src_path": "[..]/foo/src/lib.rs" + }, + { + "kind": [ "example" ], + "crate_types": [ "bin" ], + "doc": false, + "doctest": false, + "test": false, + "edition": "2015", + "name": "ex", + "src_path": "[..]/foo/examples/ex.rs" + } + ], + "features": {}, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null + } + ], + "workspace_members": [ + "foo 0.1.0 (path+file:[..]foo)" + ], + "resolve": { + "root": "foo 0.1.0 (path+file://[..]foo)", + "nodes": [ + { + "id": "foo 0.1.0 (path+file:[..]foo)", + "features": [], + "dependencies": [], + "deps": [] + } + ] + }, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); +} + +#[cargo_test] +fn example_lib() { + let p = project() + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .file( + "Cargo.toml", + r#" +[package] +name = "foo" +version = "0.1.0" + +[[example]] +name = "ex" +crate-type = ["rlib", "dylib"] + "#, + ) + .build(); + + p.cargo("metadata") + .with_json( + r#" + { + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "name": "foo", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "version": "0.1.0", + "id": "foo[..]", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "description": null, + "edition": "2015", + "source": null, + "dependencies": [], + "targets": [ + { + "kind": [ "lib" ], + "crate_types": [ "lib" ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "name": "foo", + "src_path": "[..]/foo/src/lib.rs" + }, + { + "kind": [ "example" ], + "crate_types": [ "rlib", "dylib" ], + "doc": false, + "doctest": false, + "test": false, + "edition": "2015", + "name": "ex", + "src_path": "[..]/foo/examples/ex.rs" + } + ], + "features": {}, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null + } + ], + "workspace_members": [ + "foo 0.1.0 (path+file:[..]foo)" + ], + "resolve": { + "root": "foo 0.1.0 (path+file://[..]foo)", + "nodes": [ + { + "id": "foo 0.1.0 (path+file:[..]foo)", + "features": [], + "dependencies": [], + "deps": [] + } + ] + }, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); +} + +#[cargo_test] +fn workspace_metadata() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + + [workspace.metadata] + tool1 = "hello" + tool2 = [1, 2, 3] + + [workspace.metadata.foo] + bar = 3 + + "#, + ) + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .file("baz/Cargo.toml", &basic_lib_manifest("baz")) + .file("baz/src/lib.rs", "") + .build(); + + p.cargo("metadata") + .with_json( + r#" + { + "packages": [ + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "name": "bar", + "version": "0.5.0", + "id": "bar[..]", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "keywords": [], + "source": null, + "dependencies": [], + "license": null, + "license_file": null, + "links": null, + "description": null, + "edition": "2015", + "targets": [ + { + "kind": [ "lib" ], + "crate_types": [ "lib" ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "name": "bar", + "src_path": "[..]bar/src/lib.rs" + } + ], + "features": {}, + "manifest_path": "[..]bar/Cargo.toml", + "metadata": null, + "publish": null + }, + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "name": "baz", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "version": "0.5.0", + "id": "baz[..]", + "keywords": [], + "source": null, + "dependencies": [], + "license": null, + "license_file": null, + "links": null, + "description": null, + "edition": "2015", + "targets": [ + { + "kind": [ "lib" ], + "crate_types": [ "lib" ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "name": "baz", + "src_path": "[..]baz/src/lib.rs" + } + ], + "features": {}, + "manifest_path": "[..]baz/Cargo.toml", + "metadata": null, + "publish": null + } + ], + "workspace_members": ["bar 0.5.0 (path+file:[..]bar)", "baz 0.5.0 (path+file:[..]baz)"], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "bar 0.5.0 (path+file:[..]bar)" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "baz 0.5.0 (path+file:[..]baz)" + } + ], + "root": null + }, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": { + "tool1": "hello", + "tool2": [1, 2, 3], + "foo": { + "bar": 3 + } + } + }"#, + ) + .run(); +} + +#[cargo_test] +fn workspace_metadata_with_dependencies_no_deps() { + let p = project() + // NOTE that 'artifact' isn't mentioned in the workspace here, yet it shows up as member. + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies] + baz = { path = "../baz/" } + artifact = { path = "../artifact/", artifact = "bin" } + "#, + ) + .file("bar/src/lib.rs", "") + .file("baz/Cargo.toml", &basic_lib_manifest("baz")) + .file("baz/src/lib.rs", "") + .file("artifact/Cargo.toml", &basic_bin_manifest("artifact")) + .file("artifact/src/main.rs", "fn main() {}") + .build(); + + p.cargo("metadata --no-deps -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_json( + r#" + { + "packages": [ + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "name": "bar", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "version": "0.5.0", + "id": "bar[..]", + "keywords": [], + "source": null, + "license": null, + "dependencies": [ + { + "features": [], + "kind": null, + "name": "artifact", + "optional": false, + "path": "[..]/foo/artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true, + "artifact": { + "kinds": [ + "bin" + ], + "lib": false, + "target": null + } + }, + { + "features": [], + "kind": null, + "name": "baz", + "optional": false, + "path": "[..]/foo/baz", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + } + ], + "license_file": null, + "links": null, + "description": null, + "edition": "2015", + "targets": [ + { + "kind": [ "lib" ], + "crate_types": [ "lib" ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "name": "bar", + "src_path": "[..]bar/src/lib.rs" + } + ], + "features": {}, + "manifest_path": "[..]bar/Cargo.toml", + "metadata": null, + "publish": null + }, + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "artifact 0.5.0 (path+file:[..]/foo/artifact)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/artifact/Cargo.toml", + "metadata": null, + "name": "artifact", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "artifact", + "src_path": "[..]/foo/artifact/src/main.rs", + "test": true + } + ], + "version": "0.5.0" + }, + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "name": "baz", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "version": "0.5.0", + "id": "baz[..]", + "keywords": [], + "source": null, + "dependencies": [], + "license": null, + "license_file": null, + "links": null, + "description": null, + "edition": "2015", + "targets": [ + { + "kind": [ "lib" ], + "crate_types": ["lib"], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "name": "baz", + "src_path": "[..]baz/src/lib.rs" + } + ], + "features": {}, + "manifest_path": "[..]baz/Cargo.toml", + "metadata": null, + "publish": null + } + ], + "workspace_members": [ + "bar 0.5.0 (path+file:[..]bar)", + "artifact 0.5.0 (path+file:[..]/foo/artifact)", + "baz 0.5.0 (path+file:[..]baz)" + ], + "resolve": null, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); +} + +#[cargo_test] +fn workspace_metadata_with_dependencies_and_resolve() { + let alt_target = "wasm32-unknown-unknown"; + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "artifact", "non-artifact", "bin-only-artifact"] + "#, + ) + .file( + "bar/Cargo.toml", + &r#" + [package] + + name = "bar" + version = "0.5.0" + authors = [] + + [build-dependencies] + artifact = { path = "../artifact/", artifact = "bin", target = "target" } + bin-only-artifact = { path = "../bin-only-artifact/", artifact = "bin", target = "$ALT_TARGET" } + non-artifact = { path = "../non-artifact" } + + [dependencies] + artifact = { path = "../artifact/", artifact = ["cdylib", "staticlib", "bin:baz-name"], lib = true, target = "$ALT_TARGET" } + bin-only-artifact = { path = "../bin-only-artifact/", artifact = "bin:a-name" } + non-artifact = { path = "../non-artifact" } + + [dev-dependencies] + artifact = { path = "../artifact/" } + non-artifact = { path = "../non-artifact" } + bin-only-artifact = { path = "../bin-only-artifact/", artifact = "bin:b-name" } + "#.replace("$ALT_TARGET", alt_target), + ) + .file("bar/src/lib.rs", "") + .file("bar/build.rs", "fn main() {}") + .file( + "artifact/Cargo.toml", + r#" + [package] + name = "artifact" + version = "0.5.0" + authors = [] + + [lib] + crate-type = ["staticlib", "cdylib", "rlib"] + + [[bin]] + name = "bar-name" + + [[bin]] + name = "baz-name" + "#, + ) + .file("artifact/src/main.rs", "fn main() {}") + .file("artifact/src/lib.rs", "") + .file( + "bin-only-artifact/Cargo.toml", + r#" + [package] + name = "bin-only-artifact" + version = "0.5.0" + authors = [] + + [[bin]] + name = "a-name" + + [[bin]] + name = "b-name" + "#, + ) + .file("bin-only-artifact/src/main.rs", "fn main() {}") + .file("non-artifact/Cargo.toml", + r#" + [package] + + name = "non-artifact" + version = "0.5.0" + authors = [] + "#, + ) + .file("non-artifact/src/lib.rs", "") + .build(); + + p.cargo("metadata -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_json( + r#" + { + "metadata": null, + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "artifact 0.5.0 (path+file://[..]/foo/artifact)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/artifact/Cargo.toml", + "metadata": null, + "name": "artifact", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "staticlib", + "cdylib", + "rlib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "staticlib", + "cdylib", + "rlib" + ], + "name": "artifact", + "src_path": "[..]/foo/artifact/src/lib.rs", + "test": true + }, + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "bar-name", + "src_path": "[..]/foo/artifact/src/main.rs", + "test": true + }, + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "baz-name", + "src_path": "[..]/foo/artifact/src/main.rs", + "test": true + } + ], + "version": "0.5.0" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [ + { + "artifact": { + "kinds": [ + "cdylib", + "staticlib", + "bin:baz-name" + ], + "lib": true, + "target": "wasm32-unknown-unknown" + }, + "features": [], + "kind": null, + "name": "artifact", + "optional": false, + "path": "[..]/foo/artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "artifact": { + "kinds": [ + "bin:a-name" + ], + "lib": false, + "target": null + }, + "features": [], + "kind": null, + "name": "bin-only-artifact", + "optional": false, + "path": "[..]/foo/bin-only-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": null, + "name": "non-artifact", + "optional": false, + "path": "[..]/foo/non-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": "dev", + "name": "artifact", + "optional": false, + "path": "[..]/foo/artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "artifact": { + "kinds": [ + "bin:b-name" + ], + "lib": false, + "target": null + }, + "features": [], + "kind": "dev", + "name": "bin-only-artifact", + "optional": false, + "path": "[..]/foo/bin-only-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": "dev", + "name": "non-artifact", + "optional": false, + "path": "[..]/foo/non-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "artifact": { + "kinds": [ + "bin" + ], + "lib": false, + "target": "target" + }, + "features": [], + "kind": "build", + "name": "artifact", + "optional": false, + "path": "[..]/foo/artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "artifact": { + "kinds": [ + "bin" + ], + "lib": false, + "target": "wasm32-unknown-unknown" + }, + "features": [], + "kind": "build", + "name": "bin-only-artifact", + "optional": false, + "path": "[..]/foo/bin-only-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": "build", + "name": "non-artifact", + "optional": false, + "path": "[..]/foo/non-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + } + ], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "bar 0.5.0 (path+file://[..]/foo/bar)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/bar/Cargo.toml", + "metadata": null, + "name": "bar", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "bar", + "src_path": "[..]/foo/bar/src/lib.rs", + "test": true + }, + { + "crate_types": [ + "bin" + ], + "doc": false, + "doctest": false, + "edition": "2015", + "kind": [ + "custom-build" + ], + "name": "build-script-build", + "src_path": "[..]/foo/bar/build.rs", + "test": false + } + ], + "version": "0.5.0" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "bin-only-artifact 0.5.0 (path+file://[..]/foo/bin-only-artifact)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/bin-only-artifact/Cargo.toml", + "metadata": null, + "name": "bin-only-artifact", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "a-name", + "src_path": "[..]/foo/bin-only-artifact/src/main.rs", + "test": true + }, + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "b-name", + "src_path": "[..]/foo/bin-only-artifact/src/main.rs", + "test": true + } + ], + "version": "0.5.0" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "non-artifact 0.5.0 (path+file://[..]/foo/non-artifact)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/non-artifact/Cargo.toml", + "metadata": null, + "name": "non-artifact", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "non-artifact", + "src_path": "[..]/foo/non-artifact/src/lib.rs", + "test": true + } + ], + "version": "0.5.0" + } + ], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "artifact 0.5.0 (path+file://[..]/foo/artifact)" + }, + { + "dependencies": [ + "artifact 0.5.0 (path+file://[..]/foo/artifact)", + "bin-only-artifact 0.5.0 (path+file://[..]/foo/bin-only-artifact)", + "non-artifact 0.5.0 (path+file://[..]/foo/non-artifact)" + ], + "deps": [ + { + "dep_kinds": [ + { + "extern_name": "artifact", + "kind": null, + "target": null + }, + { + "artifact": "cdylib", + "compile_target": "wasm32-unknown-unknown", + "extern_name": "artifact", + "kind": null, + "target": null + }, + { + "artifact": "staticlib", + "compile_target": "wasm32-unknown-unknown", + "extern_name": "artifact", + "kind": null, + "target": null + }, + { + "artifact": "bin", + "bin_name": "baz-name", + "compile_target": "wasm32-unknown-unknown", + "extern_name": "baz_name", + "kind": null, + "target": null + }, + { + "kind": "dev", + "target": null + }, + { + "artifact": "bin", + "bin_name": "bar-name", + "compile_target": "", + "extern_name": "bar_name", + "kind": "build", + "target": null + }, + { + "artifact": "bin", + "bin_name": "baz-name", + "compile_target": "", + "extern_name": "baz_name", + "kind": "build", + "target": null + } + ], + "name": "artifact", + "pkg": "artifact 0.5.0 (path+file://[..]/foo/artifact)" + }, + { + "dep_kinds": [ + { + "artifact": "bin", + "bin_name": "a-name", + "extern_name": "a_name", + "kind": null, + "target": null + }, + { + "artifact": "bin", + "bin_name": "b-name", + "extern_name": "b_name", + "kind": "dev", + "target": null + }, + { + "artifact": "bin", + "bin_name": "a-name", + "compile_target": "wasm32-unknown-unknown", + "extern_name": "a_name", + "kind": "build", + "target": null + }, + { + "artifact": "bin", + "bin_name": "b-name", + "compile_target": "wasm32-unknown-unknown", + "extern_name": "b_name", + "kind": "build", + "target": null + } + ], + "name": "", + "pkg": "bin-only-artifact 0.5.0 (path+file://[..]/foo/bin-only-artifact)" + }, + { + "dep_kinds": [ + { + "kind": null, + "target": null + }, + { + "kind": "dev", + "target": null + }, + { + "kind": "build", + "target": null + } + ], + "name": "non_artifact", + "pkg": "non-artifact 0.5.0 (path+file://[..]/foo/non-artifact)" + } + ], + "features": [], + "id": "bar 0.5.0 (path+file://[..]/foo/bar)" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "bin-only-artifact 0.5.0 (path+file://[..]/foo/bin-only-artifact)" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "non-artifact 0.5.0 (path+file://[..]/foo/non-artifact)" + } + ], + "root": null + }, + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_members": [ + "bar 0.5.0 (path+file://[..]/foo/bar)", + "artifact 0.5.0 (path+file://[..]/foo/artifact)", + "bin-only-artifact 0.5.0 (path+file://[..]/foo/bin-only-artifact)", + "non-artifact 0.5.0 (path+file://[..]/foo/non-artifact)" + ], + "workspace_root": "[..]/foo" + } + "#, + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_with_invalid_manifest() { + let p = project().file("Cargo.toml", "").build(); + + p.cargo("metadata --format-version 1") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + virtual manifests must be configured with [workspace]", + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_with_invalid_authors_field() { + let p = project() + .file("src/foo.rs", "") + .file( + "Cargo.toml", + r#" + [package] + authors = "" + "#, + ) + .build(); + + p.cargo("metadata") + .with_status(101) + .with_stderr( + r#"[ERROR] failed to parse manifest at `[..]` + +Caused by: + invalid type: string "", expected a vector of strings or workspace + in `package.authors`"#, + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_with_invalid_version_field() { + let p = project() + .file("src/foo.rs", "") + .file( + "Cargo.toml", + r#" + [package] + version = 1 + "#, + ) + .build(); + + p.cargo("metadata") + .with_status(101) + .with_stderr( + r#"[ERROR] failed to parse manifest at `[..]` + +Caused by: + invalid type: integer `1`, expected SemVer version + in `package.version`"#, + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_with_invalid_publish_field() { + let p = project() + .file("src/foo.rs", "") + .file( + "Cargo.toml", + r#" + [package] + publish = "foo" + "#, + ) + .build(); + + p.cargo("metadata") + .with_status(101) + .with_stderr( + r#"[ERROR] failed to parse manifest at `[..]` + +Caused by: + invalid type: string "foo", expected a boolean, a vector of strings, or workspace + in `package.publish`"#, + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_with_invalid_artifact_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies] + artifact = { path = "artifact", artifact = "bin:notfound" } + "#, + ) + .file("src/lib.rs", "") + .file("artifact/Cargo.toml", &basic_bin_manifest("artifact")) + .file("artifact/src/main.rs", "fn main() {}") + .build(); + + p.cargo("metadata -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr( + "\ +[WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems +[ERROR] dependency `artifact` in package `foo` requires a `bin:notfound` artifact to be present.", + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_with_invalid_duplicate_renamed_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies] + bar = { path = "bar" } + baz = { path = "bar", package = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("metadata") + .with_status(101) + .with_stderr( + "\ +[WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems +[ERROR] the crate `foo v0.5.0 ([..])` depends on crate `bar v0.5.0 ([..])` multiple times with different names", + ) + .run(); +} + +const MANIFEST_OUTPUT: &str = r#" +{ + "packages": [{ + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "name":"foo", + "version":"0.5.0", + "id":"foo[..]0.5.0[..](path+file://[..]/foo)", + "source":null, + "dependencies":[], + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "description": null, + "edition": "2015", + "targets":[{ + "kind":["bin"], + "crate_types":["bin"], + "doc": true, + "doctest": false, + "test": true, + "edition": "2015", + "name":"foo", + "src_path":"[..]/foo/src/foo.rs" + }], + "features":{}, + "manifest_path":"[..]Cargo.toml", + "metadata": null, + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null + }], + "workspace_members": [ "foo 0.5.0 (path+file:[..]foo)" ], + "resolve": null, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null +}"#; + +#[cargo_test] +fn cargo_metadata_no_deps_path_to_cargo_toml_relative() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("metadata --no-deps --manifest-path foo/Cargo.toml") + .cwd(p.root().parent().unwrap()) + .with_json(MANIFEST_OUTPUT) + .run(); +} + +#[cargo_test] +fn cargo_metadata_no_deps_path_to_cargo_toml_absolute() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("metadata --no-deps --manifest-path") + .arg(p.root().join("Cargo.toml")) + .cwd(p.root().parent().unwrap()) + .with_json(MANIFEST_OUTPUT) + .run(); +} + +#[cargo_test] +fn cargo_metadata_no_deps_path_to_cargo_toml_parent_relative() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("metadata --no-deps --manifest-path foo") + .cwd(p.root().parent().unwrap()) + .with_status(101) + .with_stderr( + "[ERROR] the manifest-path must be \ + a path to a Cargo.toml file", + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_no_deps_path_to_cargo_toml_parent_absolute() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("metadata --no-deps --manifest-path") + .arg(p.root()) + .cwd(p.root().parent().unwrap()) + .with_status(101) + .with_stderr( + "[ERROR] the manifest-path must be \ + a path to a Cargo.toml file", + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_no_deps_cwd() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("metadata --no-deps") + .with_json(MANIFEST_OUTPUT) + .run(); +} + +#[cargo_test] +fn cargo_metadata_bad_version() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("metadata --no-deps --format-version 2") + .with_status(1) + .with_stderr_contains( + "\ +error: invalid value '2' for '--format-version ' + [possible values: 1] +", + ) + .run(); +} + +#[cargo_test] +fn multiple_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [features] + a = [] + b = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("metadata --features").arg("a b").run(); +} + +#[cargo_test] +fn package_metadata() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = ["wycats@example.com"] + categories = ["database"] + keywords = ["database"] + readme = "README.md" + repository = "https://github.com/rust-lang/cargo" + homepage = "https://rust-lang.org" + documentation = "https://doc.rust-lang.org/stable/std/" + + [package.metadata.bar] + baz = "quux" + "#, + ) + .file("README.md", "") + .file("src/lib.rs", "") + .build(); + + p.cargo("metadata --no-deps") + .with_json( + r#" + { + "packages": [ + { + "authors": ["wycats@example.com"], + "categories": ["database"], + "default_run": null, + "name": "foo", + "readme": "README.md", + "repository": "https://github.com/rust-lang/cargo", + "rust_version": null, + "homepage": "https://rust-lang.org", + "documentation": "https://doc.rust-lang.org/stable/std/", + "version": "0.1.0", + "id": "foo[..]", + "keywords": ["database"], + "source": null, + "dependencies": [], + "edition": "2015", + "license": null, + "license_file": null, + "links": null, + "description": null, + "targets": [ + { + "kind": [ "lib" ], + "crate_types": [ "lib" ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "name": "foo", + "src_path": "[..]foo/src/lib.rs" + } + ], + "features": {}, + "manifest_path": "[..]foo/Cargo.toml", + "metadata": { + "bar": { + "baz": "quux" + } + }, + "publish": null + } + ], + "workspace_members": ["foo[..]"], + "resolve": null, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); +} + +#[cargo_test] +fn package_publish() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = ["wycats@example.com"] + categories = ["database"] + keywords = ["database"] + readme = "README.md" + repository = "https://github.com/rust-lang/cargo" + publish = ["my-registry"] + "#, + ) + .file("README.md", "") + .file("src/lib.rs", "") + .build(); + + p.cargo("metadata --no-deps") + .with_json( + r#" + { + "packages": [ + { + "authors": ["wycats@example.com"], + "categories": ["database"], + "default_run": null, + "name": "foo", + "readme": "README.md", + "repository": "https://github.com/rust-lang/cargo", + "rust_version": null, + "homepage": null, + "documentation": null, + "version": "0.1.0", + "id": "foo[..]", + "keywords": ["database"], + "source": null, + "dependencies": [], + "edition": "2015", + "license": null, + "license_file": null, + "links": null, + "description": null, + "targets": [ + { + "kind": [ "lib" ], + "crate_types": [ "lib" ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "name": "foo", + "src_path": "[..]foo/src/lib.rs" + } + ], + "features": {}, + "manifest_path": "[..]foo/Cargo.toml", + "metadata": null, + "publish": ["my-registry"] + } + ], + "workspace_members": ["foo[..]"], + "resolve": null, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .run(); +} + +#[cargo_test] +fn cargo_metadata_path_to_cargo_toml_project() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("package --manifest-path") + .arg(p.root().join("bar/Cargo.toml")) + .cwd(p.root().parent().unwrap()) + .run(); + + p.cargo("metadata --manifest-path") + .arg(p.root().join("target/package/bar-0.5.0/Cargo.toml")) + .with_json( + r#" + { + "packages": [ + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "edition": "2015", + "features": {}, + "id": "bar 0.5.0 ([..])", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null, + "name": "bar", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "bar", + "src_path": "[..]src/lib.rs" + } + ], + "version": "0.5.0" + } + ], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "bar 0.5.0 ([..])" + } + ], + "root": "bar 0.5.0 (path+file:[..])" + }, + "target_directory": "[..]", + "version": 1, + "workspace_members": [ + "bar 0.5.0 (path+file:[..])" + ], + "workspace_root": "[..]", + "metadata": null + } + "#, + ) + .run(); +} + +#[cargo_test] +fn package_edition_2018() { + let p = project() + .file("src/lib.rs", "") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = ["wycats@example.com"] + edition = "2018" + "#, + ) + .build(); + p.cargo("metadata") + .with_json( + r#" + { + "packages": [ + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "edition": "2018", + "features": {}, + "id": "foo 0.1.0 (path+file:[..])", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null, + "name": "foo", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2018", + "kind": [ + "lib" + ], + "name": "foo", + "src_path": "[..]src/lib.rs" + } + ], + "version": "0.1.0" + } + ], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "foo 0.1.0 (path+file:[..])" + } + ], + "root": "foo 0.1.0 (path+file:[..])" + }, + "target_directory": "[..]", + "version": 1, + "workspace_members": [ + "foo 0.1.0 (path+file:[..])" + ], + "workspace_root": "[..]", + "metadata": null + } + "#, + ) + .run(); +} + +#[cargo_test] +fn package_default_run() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#) + .file("src/bin/b.rs", r#"fn main() { println!("hello B"); }"#) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = ["wycats@example.com"] + edition = "2018" + default-run = "a" + "#, + ) + .build(); + let json = p.cargo("metadata").run_json(); + assert_eq!(json["packages"][0]["default_run"], json!("a")); +} + +#[cargo_test] +fn package_rust_version() { + let p = project() + .file("src/lib.rs", "") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = ["wycats@example.com"] + edition = "2018" + rust-version = "1.56" + "#, + ) + .build(); + let json = p.cargo("metadata").run_json(); + assert_eq!(json["packages"][0]["rust_version"], json!("1.56")); +} + +#[cargo_test] +fn target_edition_2018() { + let p = project() + .file("src/lib.rs", "") + .file("src/main.rs", "") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = ["wycats@example.com"] + edition = "2015" + + [lib] + edition = "2018" + "#, + ) + .build(); + p.cargo("metadata") + .with_json( + r#" + { + "packages": [ + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "edition": "2015", + "features": {}, + "id": "foo 0.1.0 (path+file:[..])", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]Cargo.toml", + "metadata": null, + "publish": null, + "name": "foo", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2018", + "kind": [ + "lib" + ], + "name": "foo", + "src_path": "[..]src/lib.rs" + }, + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "test": true, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "foo", + "src_path": "[..]src/main.rs" + } + ], + "version": "0.1.0" + } + ], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "foo 0.1.0 (path+file:[..])" + } + ], + "root": "foo 0.1.0 (path+file:[..])" + }, + "target_directory": "[..]", + "version": 1, + "workspace_members": [ + "foo 0.1.0 (path+file:[..])" + ], + "workspace_root": "[..]", + "metadata": null + } + "#, + ) + .run(); +} + +#[cargo_test] +fn rename_dependency() { + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.2.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { version = "0.1.0" } + baz = { version = "0.2.0", package = "bar" } + "#, + ) + .file("src/lib.rs", "extern crate bar; extern crate baz;") + .build(); + + p.cargo("metadata") + .with_json( + r#" +{ + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "edition": "2015", + "features": {}, + "id": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]", + "metadata": null, + "publish": null, + "name": "bar", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "bar", + "src_path": "[..]" + } + ], + "version": "0.1.0" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "edition": "2015", + "features": {}, + "id": "bar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]", + "metadata": null, + "publish": null, + "name": "bar", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "bar", + "src_path": "[..]" + } + ], + "version": "0.2.0" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [ + { + "features": [], + "kind": null, + "name": "bar", + "optional": false, + "rename": null, + "registry": null, + "req": "^0.1.0", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": null, + "name": "bar", + "optional": false, + "rename": "baz", + "registry": null, + "req": "^0.2.0", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "target": null, + "uses_default_features": true + } + ], + "description": null, + "edition": "2015", + "features": {}, + "id": "foo 0.0.1[..]", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]", + "metadata": null, + "publish": null, + "name": "foo", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "foo", + "src_path": "[..]" + } + ], + "version": "0.0.1" + } + ], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "bar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" + }, + { + "dependencies": [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "dep_kinds": [ + { + "kind": null, + "target": null + } + ], + "name": "bar", + "pkg": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" + }, + { + "dep_kinds": [ + { + "kind": null, + "target": null + } + ], + "name": "baz", + "pkg": "bar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" + } + ], + "features": [], + "id": "foo 0.0.1[..]" + } + ], + "root": "foo 0.0.1[..]" + }, + "target_directory": "[..]", + "version": 1, + "workspace_members": [ + "foo 0.0.1[..]" + ], + "workspace_root": "[..]", + "metadata": null +}"#, + ) + .run(); +} + +#[cargo_test] +fn metadata_links() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + links = "a" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + p.cargo("metadata") + .with_json( + r#" + { + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "edition": "2015", + "features": {}, + "id": "foo 0.5.0 [..]", + "keywords": [], + "license": null, + "license_file": null, + "links": "a", + "manifest_path": "[..]/foo/Cargo.toml", + "metadata": null, + "publish": null, + "name": "foo", + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "foo", + "src_path": "[..]/foo/src/lib.rs" + }, + { + "crate_types": [ + "bin" + ], + "doc": false, + "doctest": false, + "test": false, + "edition": "2015", + "kind": [ + "custom-build" + ], + "name": "build-script-build", + "src_path": "[..]/foo/build.rs" + } + ], + "version": "0.5.0" + } + ], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "foo 0.5.0 [..]" + } + ], + "root": "foo 0.5.0 [..]" + }, + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_members": [ + "foo 0.5.0 [..]" + ], + "workspace_root": "[..]/foo", + "metadata": null + } + "#, + ) + .run() +} + +#[cargo_test] +fn deps_with_bin_only() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + bdep = { path = "bdep" } + "#, + ) + .file("src/lib.rs", "") + .file("bdep/Cargo.toml", &basic_bin_manifest("bdep")) + .file("bdep/src/main.rs", "fn main() {}") + .build(); + + p.cargo("metadata") + .with_json( + r#" + { + "packages": [ + { + "name": "foo", + "version": "0.1.0", + "id": "foo 0.1.0 ([..])", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": [ + { + "name": "bdep", + "source": null, + "req": "*", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "path": "[..]/foo/bdep", + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "foo", + "src_path": "[..]/foo/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "[..]/foo/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + } + ], + "workspace_members": [ + "foo 0.1.0 ([..])" + ], + "resolve": { + "nodes": [ + { + "id": "foo 0.1.0 ([..])", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "foo 0.1.0 ([..])" + }, + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_root": "[..]foo", + "metadata": null + } + "#, + ) + .run(); +} + +#[cargo_test] +fn filter_platform() { + // Testing the --filter-platform flag. + Package::new("normal-dep", "0.0.1").publish(); + Package::new("host-dep", "0.0.1").publish(); + Package::new("alt-dep", "0.0.1").publish(); + Package::new("cfg-dep", "0.0.1").publish(); + // Just needs to be a valid target that is different from host. + // Presumably nobody runs these tests on wasm. 🙃 + let alt_target = "wasm32-unknown-unknown"; + let host_target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + normal-dep = "0.0.1" + + [target.{}.dependencies] + host-dep = "0.0.1" + + [target.{}.dependencies] + alt-dep = "0.0.1" + + [target.'cfg(foobar)'.dependencies] + cfg-dep = "0.0.1" + "#, + host_target, alt_target + ), + ) + .file("src/lib.rs", "") + .build(); + + let alt_dep = r#" + { + "name": "alt-dep", + "version": "0.0.1", + "id": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": null, + "license_file": null, + "description": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "alt-dep", + "src_path": "[..]/alt-dep-0.0.1/src/lib.rs", + "edition": "2015", + "test": true, + "doc": true, + "doctest": true + } + ], + "features": {}, + "manifest_path": "[..]/alt-dep-0.0.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + } + "#; + + let cfg_dep = r#" + { + "name": "cfg-dep", + "version": "0.0.1", + "id": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": null, + "license_file": null, + "description": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cfg-dep", + "src_path": "[..]/cfg-dep-0.0.1/src/lib.rs", + "edition": "2015", + "test": true, + "doc": true, + "doctest": true + } + ], + "features": {}, + "manifest_path": "[..]/cfg-dep-0.0.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + } + "#; + + let host_dep = r#" + { + "name": "host-dep", + "version": "0.0.1", + "id": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": null, + "license_file": null, + "description": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "host-dep", + "src_path": "[..]/host-dep-0.0.1/src/lib.rs", + "edition": "2015", + "test": true, + "doc": true, + "doctest": true + } + ], + "features": {}, + "manifest_path": "[..]/host-dep-0.0.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + } + "#; + + let normal_dep = r#" + { + "name": "normal-dep", + "version": "0.0.1", + "id": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": null, + "license_file": null, + "description": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "normal-dep", + "src_path": "[..]/normal-dep-0.0.1/src/lib.rs", + "edition": "2015", + "test": true, + "doc": true, + "doctest": true + } + ], + "features": {}, + "manifest_path": "[..]/normal-dep-0.0.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + } + "#; + + // The dependencies are stored in sorted order by target and then by name. + // Since the testsuite may run on different targets, this needs to be + // sorted before it can be compared. + let mut foo_deps = serde_json::json!([ + { + "name": "normal-dep", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "cfg-dep", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(foobar)", + "registry": null + }, + { + "name": "alt-dep", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": alt_target, + "registry": null + }, + { + "name": "host-dep", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": host_target, + "registry": null + } + ]); + foo_deps.as_array_mut().unwrap().sort_by(|a, b| { + // This really should be `rename`, but not needed here. + // Also, sorting on `name` isn't really necessary since this test + // only has one package per target, but leaving it here to be safe. + let a = (a["target"].as_str(), a["name"].as_str()); + let b = (b["target"].as_str(), b["name"].as_str()); + a.cmp(&b) + }); + + let foo = r#" + { + "name": "foo", + "version": "0.1.0", + "id": "foo 0.1.0 (path+file:[..]foo)", + "license": null, + "license_file": null, + "description": null, + "source": null, + "dependencies": + $FOO_DEPS, + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "foo", + "src_path": "[..]/foo/src/lib.rs", + "edition": "2015", + "test": true, + "doc": true, + "doctest": true + } + ], + "features": {}, + "manifest_path": "[..]/foo/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [], + "default_run": null, + "keywords": [], + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null + } + "# + .replace("$ALT_TRIPLE", alt_target) + .replace("$HOST_TRIPLE", host_target) + .replace("$FOO_DEPS", &foo_deps.to_string()); + + // We're going to be checking that we don't download excessively, + // so we need to ensure that downloads will happen. + let clear = || { + cargo_home().join("registry/cache").rm_rf(); + cargo_home().join("registry/src").rm_rf(); + p.build_dir().rm_rf(); + }; + + // Normal metadata, no filtering, returns *everything*. + p.cargo("metadata") + .with_stderr_unordered( + "\ +[UPDATING] [..] +[WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems +[DOWNLOADING] crates ... +[DOWNLOADED] normal-dep v0.0.1 [..] +[DOWNLOADED] host-dep v0.0.1 [..] +[DOWNLOADED] alt-dep v0.0.1 [..] +[DOWNLOADED] cfg-dep v0.0.1 [..] +", + ) + .with_json( + &r#" +{ + "packages": [ + $ALT_DEP, + $CFG_DEP, + $FOO, + $HOST_DEP, + $NORMAL_DEP + ], + "workspace_members": [ + "foo 0.1.0 (path+file:[..]foo)" + ], + "resolve": { + "nodes": [ + { + "id": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "foo 0.1.0 (path+file:[..]foo)", + "dependencies": [ + "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "alt_dep", + "pkg": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "$ALT_TRIPLE" + } + ] + }, + { + "name": "cfg_dep", + "pkg": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(foobar)" + } + ] + }, + { + "name": "host_dep", + "pkg": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "$HOST_TRIPLE" + } + ] + }, + { + "name": "normal_dep", + "pkg": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "foo 0.1.0 (path+file:[..]foo)" + }, + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null +} +"# + .replace("$ALT_TRIPLE", alt_target) + .replace("$HOST_TRIPLE", host_target) + .replace("$ALT_DEP", alt_dep) + .replace("$CFG_DEP", cfg_dep) + .replace("$HOST_DEP", host_dep) + .replace("$NORMAL_DEP", normal_dep) + .replace("$FOO", &foo), + ) + .run(); + clear(); + + // Filter on alternate, removes cfg and host. + p.cargo("metadata --filter-platform") + .arg(alt_target) + .with_stderr_unordered( + "\ +[WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems +[DOWNLOADING] crates ... +[DOWNLOADED] normal-dep v0.0.1 [..] +[DOWNLOADED] host-dep v0.0.1 [..] +[DOWNLOADED] alt-dep v0.0.1 [..] +", + ) + .with_json( + &r#" +{ + "packages": [ + $ALT_DEP, + $FOO, + $NORMAL_DEP + ], + "workspace_members": "{...}", + "resolve": { + "nodes": [ + { + "id": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "foo 0.1.0 (path+file:[..]foo)", + "dependencies": [ + "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "alt_dep", + "pkg": "alt-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "$ALT_TRIPLE" + } + ] + }, + { + "name": "normal_dep", + "pkg": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "foo 0.1.0 (path+file:[..]foo)" + }, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]foo", + "metadata": null +} +"# + .replace("$ALT_TRIPLE", alt_target) + .replace("$ALT_DEP", alt_dep) + .replace("$NORMAL_DEP", normal_dep) + .replace("$FOO", &foo), + ) + .run(); + clear(); + + // Filter on host, removes alt and cfg. + p.cargo("metadata --filter-platform") + .arg(&host_target) + .with_stderr_unordered( + "\ +[WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems +[DOWNLOADING] crates ... +[DOWNLOADED] normal-dep v0.0.1 [..] +[DOWNLOADED] host-dep v0.0.1 [..] +", + ) + .with_json( + &r#" +{ + "packages": [ + $FOO, + $HOST_DEP, + $NORMAL_DEP + ], + "workspace_members": "{...}", + "resolve": { + "nodes": [ + { + "id": "foo 0.1.0 (path+file:[..]foo)", + "dependencies": [ + "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "host_dep", + "pkg": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "$HOST_TRIPLE" + } + ] + }, + { + "name": "normal_dep", + "pkg": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "foo 0.1.0 (path+file:[..]foo)" + }, + "target_directory": "[..]foo/target", + "version": 1, + "workspace_root": "[..]foo", + "metadata": null +} +"# + .replace("$HOST_TRIPLE", host_target) + .replace("$HOST_DEP", host_dep) + .replace("$NORMAL_DEP", normal_dep) + .replace("$FOO", &foo), + ) + .run(); + clear(); + + // Filter host with cfg, removes alt only + p.cargo("metadata --filter-platform") + .arg(&host_target) + .env("RUSTFLAGS", "--cfg=foobar") + .with_stderr_unordered( + "\ +[WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems +[DOWNLOADING] crates ... +[DOWNLOADED] normal-dep v0.0.1 [..] +[DOWNLOADED] host-dep v0.0.1 [..] +[DOWNLOADED] cfg-dep v0.0.1 [..] +", + ) + .with_json( + &r#" +{ + "packages": [ + $CFG_DEP, + $FOO, + $HOST_DEP, + $NORMAL_DEP + ], + "workspace_members": "{...}", + "resolve": { + "nodes": [ + { + "id": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "foo 0.1.0 (path+file:[..]/foo)", + "dependencies": [ + "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_dep", + "pkg": "cfg-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(foobar)" + } + ] + }, + { + "name": "host_dep", + "pkg": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "$HOST_TRIPLE" + } + ] + }, + { + "name": "normal_dep", + "pkg": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "host-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "normal-dep 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "foo 0.1.0 (path+file:[..]/foo)" + }, + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null +} +"# + .replace("$HOST_TRIPLE", host_target) + .replace("$CFG_DEP", cfg_dep) + .replace("$HOST_DEP", host_dep) + .replace("$NORMAL_DEP", normal_dep) + .replace("$FOO", &foo), + ) + .run(); +} + +#[cargo_test] +fn dep_kinds() { + Package::new("bar", "0.1.0").publish(); + Package::new("winapi", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + + [dev-dependencies] + bar = "0.1" + + [build-dependencies] + bar = "0.1" + + [target.'cfg(windows)'.dependencies] + winapi = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("metadata") + .with_json( + r#" + { + "packages": "{...}", + "workspace_members": "{...}", + "target_directory": "{...}", + "version": 1, + "workspace_root": "{...}", + "metadata": null, + "resolve": { + "nodes": [ + { + "id": "bar 0.1.0 [..]", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "foo 0.1.0 [..]", + "dependencies": [ + "bar 0.1.0 [..]", + "winapi 0.1.0 [..]" + ], + "deps": [ + { + "name": "bar", + "pkg": "bar 0.1.0 [..]", + "dep_kinds": [ + { + "kind": null, + "target": null + }, + { + "kind": "dev", + "target": null + }, + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "winapi", + "pkg": "winapi 0.1.0 [..]", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "winapi 0.1.0 [..]", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "foo 0.1.0 [..]" + } + } + "#, + ) + .run(); +} + +#[cargo_test] +fn dep_kinds_workspace() { + // Check for bug with duplicate dep kinds in a workspace. + // If different members select different features for the same package, + // they show up multiple times in the resolver `deps`. + // + // Here: + // foo -> dep + // bar -> foo[feat1] -> dep + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + feat1 = [] + + [dependencies] + dep = { path="dep" } + + [workspace] + members = ["bar"] + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + foo = { path="..", features=["feat1"] } + "#, + ) + .file("bar/src/lib.rs", "") + .file("dep/Cargo.toml", &basic_lib_manifest("dep")) + .file("dep/src/lib.rs", "") + .build(); + + p.cargo("metadata") + .with_json( + r#" + { + "packages": "{...}", + "workspace_members": "{...}", + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null, + "resolve": { + "nodes": [ + { + "id": "bar 0.1.0 (path+file://[..]/foo/bar)", + "dependencies": [ + "foo 0.1.0 (path+file://[..]/foo)" + ], + "deps": [ + { + "name": "foo", + "pkg": "foo 0.1.0 (path+file://[..]/foo)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "dep 0.5.0 (path+file://[..]/foo/dep)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "foo 0.1.0 (path+file://[..]/foo)", + "dependencies": [ + "dep 0.5.0 (path+file://[..]/foo/dep)" + ], + "deps": [ + { + "name": "dep", + "pkg": "dep 0.5.0 (path+file://[..]/foo/dep)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "feat1" + ] + } + ], + "root": "foo 0.1.0 (path+file://[..]/foo)" + } + } + "#, + ) + .run(); +} + +// Creating non-utf8 path is an OS-specific pain, so let's run this only on +// linux, where arbitrary bytes work. +#[cfg(target_os = "linux")] +#[cargo_test] +fn cargo_metadata_non_utf8() { + use std::ffi::OsString; + use std::os::unix::ffi::OsStringExt; + use std::path::PathBuf; + + let base = PathBuf::from(OsString::from_vec(vec![255])); + + let p = project() + .no_manifest() + .file(base.join("./src/lib.rs"), "") + .file(base.join("./Cargo.toml"), &basic_lib_manifest("foo")) + .build(); + + p.cargo("metadata") + .cwd(p.root().join(base)) + .arg("--format-version") + .arg("1") + .with_stderr("error: path contains invalid UTF-8 characters") + .with_status(101) + .run(); +} + +// TODO: Consider using this test instead of the version without the 'artifact' suffix or merge them because they should be pretty much the same. +#[cargo_test] +fn workspace_metadata_with_dependencies_no_deps_artifact() { + let p = project() + // NOTE that 'artifact' isn't mentioned in the workspace here, yet it shows up as member. + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies] + baz = { path = "../baz/" } + baz-renamed = { path = "../baz/" } + artifact = { path = "../artifact/", artifact = "bin" } + "#, + ) + .file("bar/src/lib.rs", "") + .file("baz/Cargo.toml", &basic_lib_manifest("baz")) + .file("baz/src/lib.rs", "") + .file("artifact/Cargo.toml", &basic_bin_manifest("artifact")) + .file("artifact/src/main.rs", "fn main() {}") + .build(); + + p.cargo("metadata --no-deps -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_json( + r#" + { + "metadata": null, + "packages": [ + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "dependencies": [ + { + "artifact": { + "kinds": [ + "bin" + ], + "lib": false, + "target": null + }, + "features": [], + "kind": null, + "name": "artifact", + "optional": false, + "path": "[..]/foo/artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": null, + "name": "baz", + "optional": false, + "path": "[..]/foo/baz", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": null, + "name": "baz-renamed", + "optional": false, + "path": "[..]/foo/baz", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + } + ], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "bar 0.5.0 (path+file://[..]/foo/bar)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/bar/Cargo.toml", + "metadata": null, + "name": "bar", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "bar", + "src_path": "[..]/foo/bar/src/lib.rs", + "test": true + } + ], + "version": "0.5.0" + }, + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "artifact 0.5.0 (path+file://[..]/foo/artifact)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/artifact/Cargo.toml", + "metadata": null, + "name": "artifact", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "artifact", + "src_path": "[..]/foo/artifact/src/main.rs", + "test": true + } + ], + "version": "0.5.0" + }, + { + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "baz 0.5.0 (path+file://[..]/foo/baz)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/baz/Cargo.toml", + "metadata": null, + "name": "baz", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "baz", + "src_path": "[..]/foo/baz/src/lib.rs", + "test": true + } + ], + "version": "0.5.0" + } + ], + "resolve": null, + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_members": [ + "bar 0.5.0 (path+file://[..]/foo/bar)", + "artifact 0.5.0 (path+file://[..]/foo/artifact)", + "baz 0.5.0 (path+file://[..]/foo/baz)" + ], + "workspace_root": "[..]/foo" + } +"#, + ) + .run(); +} diff --git a/tests/testsuite/minimal_versions.rs b/tests/testsuite/minimal_versions.rs new file mode 100644 index 0000000..f814dcb --- /dev/null +++ b/tests/testsuite/minimal_versions.rs @@ -0,0 +1,38 @@ +//! Tests for minimal-version resolution. +//! +//! Note: Some tests are located in the resolver-tests package. + +use cargo_test_support::project; +use cargo_test_support::registry::Package; + +// Ensure that the "-Z minimal-versions" CLI option works and the minimal +// version of a dependency ends up in the lock file. +#[cargo_test] +fn minimal_version_cli() { + Package::new("dep", "1.0.0").publish(); + Package::new("dep", "1.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [dependencies] + dep = "1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("generate-lockfile -Zminimal-versions") + .masquerade_as_nightly_cargo(&["minimal-versions"]) + .run(); + + let lock = p.read_lockfile(); + + assert!(!lock.contains("1.1.0")); +} diff --git a/tests/testsuite/mock-std/Cargo.toml b/tests/testsuite/mock-std/Cargo.toml new file mode 100644 index 0000000..a69aa4b --- /dev/null +++ b/tests/testsuite/mock-std/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] +members = [ + "library/alloc", + "library/core", + "library/proc_macro", + "library/std", + "library/test", +] diff --git a/tests/testsuite/mock-std/library/alloc/Cargo.toml b/tests/testsuite/mock-std/library/alloc/Cargo.toml new file mode 100644 index 0000000..dc965ab --- /dev/null +++ b/tests/testsuite/mock-std/library/alloc/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "alloc" +version = "0.1.0" +authors = ["Alex Crichton "] +edition = "2018" + +[dependencies] +registry-dep-using-core = { version = "*", features = ['mockbuild'] } diff --git a/tests/testsuite/mock-std/library/alloc/src/lib.rs b/tests/testsuite/mock-std/library/alloc/src/lib.rs new file mode 100644 index 0000000..823716e --- /dev/null +++ b/tests/testsuite/mock-std/library/alloc/src/lib.rs @@ -0,0 +1,11 @@ +#![feature(staged_api)] +#![stable(since = "1.0.0", feature = "dummy")] + +extern crate alloc; + +#[stable(since = "1.0.0", feature = "dummy")] +pub use alloc::*; + +#[stable(since = "1.0.0", feature = "dummy")] +pub fn custom_api() { +} diff --git a/tests/testsuite/mock-std/library/compiler_builtins/Cargo.toml b/tests/testsuite/mock-std/library/compiler_builtins/Cargo.toml new file mode 100644 index 0000000..d1df281 --- /dev/null +++ b/tests/testsuite/mock-std/library/compiler_builtins/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "compiler_builtins" +version = "0.1.0" +authors = ["Alex Crichton "] +edition = "2018" diff --git a/tests/testsuite/mock-std/library/compiler_builtins/src/lib.rs b/tests/testsuite/mock-std/library/compiler_builtins/src/lib.rs new file mode 100644 index 0000000..65e2cc3 --- /dev/null +++ b/tests/testsuite/mock-std/library/compiler_builtins/src/lib.rs @@ -0,0 +1 @@ +// intentionally blank diff --git a/tests/testsuite/mock-std/library/core/Cargo.toml b/tests/testsuite/mock-std/library/core/Cargo.toml new file mode 100644 index 0000000..3f7de53 --- /dev/null +++ b/tests/testsuite/mock-std/library/core/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "core" +version = "0.1.0" +authors = ["Alex Crichton "] +edition = "2018" diff --git a/tests/testsuite/mock-std/library/core/src/lib.rs b/tests/testsuite/mock-std/library/core/src/lib.rs new file mode 100644 index 0000000..b90ed09 --- /dev/null +++ b/tests/testsuite/mock-std/library/core/src/lib.rs @@ -0,0 +1,9 @@ +#![feature(staged_api)] +#![stable(since = "1.0.0", feature = "dummy")] + +#[stable(since = "1.0.0", feature = "dummy")] +pub use core::*; + +#[stable(since = "1.0.0", feature = "dummy")] +pub fn custom_api() { +} diff --git a/tests/testsuite/mock-std/library/panic_unwind/Cargo.toml b/tests/testsuite/mock-std/library/panic_unwind/Cargo.toml new file mode 100644 index 0000000..e7beb92 --- /dev/null +++ b/tests/testsuite/mock-std/library/panic_unwind/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "panic_unwind" +version = "0.1.0" +authors = ["Alex Crichton "] +edition = "2018" diff --git a/tests/testsuite/mock-std/library/panic_unwind/src/lib.rs b/tests/testsuite/mock-std/library/panic_unwind/src/lib.rs new file mode 100644 index 0000000..6af65d8 --- /dev/null +++ b/tests/testsuite/mock-std/library/panic_unwind/src/lib.rs @@ -0,0 +1,5 @@ +#![feature(panic_unwind, panic_runtime)] +#![panic_runtime] +#![no_std] + +extern crate panic_unwind; diff --git a/tests/testsuite/mock-std/library/proc_macro/Cargo.toml b/tests/testsuite/mock-std/library/proc_macro/Cargo.toml new file mode 100644 index 0000000..939a113 --- /dev/null +++ b/tests/testsuite/mock-std/library/proc_macro/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "proc_macro" +version = "0.1.0" +authors = ["Alex Crichton "] +edition = "2018" diff --git a/tests/testsuite/mock-std/library/proc_macro/src/lib.rs b/tests/testsuite/mock-std/library/proc_macro/src/lib.rs new file mode 100644 index 0000000..82a7684 --- /dev/null +++ b/tests/testsuite/mock-std/library/proc_macro/src/lib.rs @@ -0,0 +1,11 @@ +#![feature(staged_api)] +#![stable(since = "1.0.0", feature = "dummy")] + +extern crate proc_macro; + +#[stable(since = "1.0.0", feature = "dummy")] +pub use proc_macro::*; + +#[stable(since = "1.0.0", feature = "dummy")] +pub fn custom_api() { +} diff --git a/tests/testsuite/mock-std/library/rustc-std-workspace-alloc/Cargo.toml b/tests/testsuite/mock-std/library/rustc-std-workspace-alloc/Cargo.toml new file mode 100644 index 0000000..6b86f22 --- /dev/null +++ b/tests/testsuite/mock-std/library/rustc-std-workspace-alloc/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rustc-std-workspace-alloc" +version = "1.9.0" +authors = ["Alex Crichton "] +edition = "2018" + +[lib] +path = "lib.rs" + +[dependencies] +alloc = { path = "../alloc" } diff --git a/tests/testsuite/mock-std/library/rustc-std-workspace-alloc/lib.rs b/tests/testsuite/mock-std/library/rustc-std-workspace-alloc/lib.rs new file mode 100644 index 0000000..2bbfa1a --- /dev/null +++ b/tests/testsuite/mock-std/library/rustc-std-workspace-alloc/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +pub use alloc::*; diff --git a/tests/testsuite/mock-std/library/rustc-std-workspace-core/Cargo.toml b/tests/testsuite/mock-std/library/rustc-std-workspace-core/Cargo.toml new file mode 100644 index 0000000..8d19216 --- /dev/null +++ b/tests/testsuite/mock-std/library/rustc-std-workspace-core/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rustc-std-workspace-core" +version = "1.9.0" +authors = ["Alex Crichton "] +edition = "2018" + +[lib] +path = "lib.rs" + +[dependencies] +core = { path = "../core" } diff --git a/tests/testsuite/mock-std/library/rustc-std-workspace-core/lib.rs b/tests/testsuite/mock-std/library/rustc-std-workspace-core/lib.rs new file mode 100644 index 0000000..8162517 --- /dev/null +++ b/tests/testsuite/mock-std/library/rustc-std-workspace-core/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +pub use core::*; diff --git a/tests/testsuite/mock-std/library/rustc-std-workspace-std/Cargo.toml b/tests/testsuite/mock-std/library/rustc-std-workspace-std/Cargo.toml new file mode 100644 index 0000000..91572b8 --- /dev/null +++ b/tests/testsuite/mock-std/library/rustc-std-workspace-std/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rustc-std-workspace-std" +version = "1.9.0" +authors = ["Alex Crichton "] +edition = "2018" + +[lib] +path = "lib.rs" + +[dependencies] +std = { path = "../std" } diff --git a/tests/testsuite/mock-std/library/rustc-std-workspace-std/lib.rs b/tests/testsuite/mock-std/library/rustc-std-workspace-std/lib.rs new file mode 100644 index 0000000..f40d09c --- /dev/null +++ b/tests/testsuite/mock-std/library/rustc-std-workspace-std/lib.rs @@ -0,0 +1 @@ +pub use std::*; diff --git a/tests/testsuite/mock-std/library/std/Cargo.toml b/tests/testsuite/mock-std/library/std/Cargo.toml new file mode 100644 index 0000000..d2cfdea --- /dev/null +++ b/tests/testsuite/mock-std/library/std/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "std" +version = "0.1.0" +authors = ["Alex Crichton "] +edition = "2018" + +[dependencies] +registry-dep-using-alloc = { version = "*", features = ['mockbuild'] } + +[features] +feature1 = [] diff --git a/tests/testsuite/mock-std/library/std/src/lib.rs b/tests/testsuite/mock-std/library/std/src/lib.rs new file mode 100644 index 0000000..146d4c4 --- /dev/null +++ b/tests/testsuite/mock-std/library/std/src/lib.rs @@ -0,0 +1,12 @@ +#![feature(staged_api)] +#![stable(since = "1.0.0", feature = "dummy")] + +#[stable(since = "1.0.0", feature = "dummy")] +pub use std::*; + +#[stable(since = "1.0.0", feature = "dummy")] +pub fn custom_api() {} + +#[cfg(feature = "feature1")] +#[stable(since = "1.0.0", feature = "dummy")] +pub fn conditional_function() {} diff --git a/tests/testsuite/mock-std/library/test/Cargo.toml b/tests/testsuite/mock-std/library/test/Cargo.toml new file mode 100644 index 0000000..299db7b --- /dev/null +++ b/tests/testsuite/mock-std/library/test/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "test" +version = "0.1.0" +authors = ["Alex Crichton "] +edition = "2018" + +[dependencies] +proc_macro = { path = "../proc_macro" } +std = { path = "../std" } +panic_unwind = { path = "../panic_unwind" } +compiler_builtins = { path = "../compiler_builtins" } +registry-dep-using-std = { version = "*", features = ['mockbuild'] } + +[features] +panic-unwind = [] +backtrace = [] +feature1 = ["std/feature1"] +default = [] diff --git a/tests/testsuite/mock-std/library/test/src/lib.rs b/tests/testsuite/mock-std/library/test/src/lib.rs new file mode 100644 index 0000000..a112855 --- /dev/null +++ b/tests/testsuite/mock-std/library/test/src/lib.rs @@ -0,0 +1,10 @@ +#![feature(staged_api)] +#![feature(test)] +#![unstable(feature = "test", issue = "none")] + +extern crate test; + +pub use test::*; + +pub fn custom_api() { +} diff --git a/tests/testsuite/multitarget.rs b/tests/testsuite/multitarget.rs new file mode 100644 index 0000000..5f3543f --- /dev/null +++ b/tests/testsuite/multitarget.rs @@ -0,0 +1,231 @@ +//! Tests for multiple `--target` flags to subcommands + +use cargo_test_support::{basic_manifest, cross_compile, project, rustc_host}; + +#[cargo_test] +fn simple_build() { + if cross_compile::disabled() { + return; + } + let t1 = cross_compile::alternate(); + let t2 = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build") + .arg("--target") + .arg(&t1) + .arg("--target") + .arg(&t2) + .run(); + + assert!(p.target_bin(t1, "foo").is_file()); + assert!(p.target_bin(t2, "foo").is_file()); +} + +#[cargo_test] +fn simple_build_with_config() { + if cross_compile::disabled() { + return; + } + let t1 = cross_compile::alternate(); + let t2 = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config.toml", + &format!( + r#" + [build] + target = ["{t1}", "{t2}"] + "# + ), + ) + .build(); + + p.cargo("build").run(); + + assert!(p.target_bin(t1, "foo").is_file()); + assert!(p.target_bin(t2, "foo").is_file()); +} + +#[cargo_test] +fn simple_test() { + if !cross_compile::can_run_on_host() { + return; + } + let t1 = cross_compile::alternate(); + let t2 = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/lib.rs", "fn main() {}") + .build(); + + p.cargo("test") + .arg("--target") + .arg(&t1) + .arg("--target") + .arg(&t2) + .with_stderr_contains(&format!("[RUNNING] [..]{}[..]", t1)) + .with_stderr_contains(&format!("[RUNNING] [..]{}[..]", t2)) + .run(); +} + +#[cargo_test] +fn simple_run() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("run --target a --target b") + .with_stderr("[ERROR] only one `--target` argument is supported") + .with_status(101) + .run(); +} + +#[cargo_test] +fn simple_doc() { + if cross_compile::disabled() { + return; + } + let t1 = cross_compile::alternate(); + let t2 = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/lib.rs", "//! empty lib") + .build(); + + p.cargo("doc") + .arg("--target") + .arg(&t1) + .arg("--target") + .arg(&t2) + .run(); + + assert!(p.build_dir().join(&t1).join("doc/foo/index.html").is_file()); + assert!(p.build_dir().join(&t2).join("doc/foo/index.html").is_file()); +} + +#[cargo_test] +fn simple_check() { + if cross_compile::disabled() { + return; + } + let t1 = cross_compile::alternate(); + let t2 = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .arg("--target") + .arg(&t1) + .arg("--target") + .arg(&t2) + .run(); +} + +#[cargo_test] +fn same_value_twice() { + if cross_compile::disabled() { + return; + } + let t = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build") + .arg("--target") + .arg(&t) + .arg("--target") + .arg(&t) + .run(); + + assert!(p.target_bin(t, "foo").is_file()); +} + +#[cargo_test] +fn same_value_twice_with_config() { + if cross_compile::disabled() { + return; + } + let t = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config.toml", + &format!( + r#" + [build] + target = ["{t}", "{t}"] + "# + ), + ) + .build(); + + p.cargo("build").run(); + + assert!(p.target_bin(t, "foo").is_file()); +} + +#[cargo_test] +fn works_with_config_in_both_string_or_list() { + if cross_compile::disabled() { + return; + } + let t = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config.toml", + &format!( + r#" + [build] + target = "{t}" + "# + ), + ) + .build(); + + p.cargo("build").run(); + + assert!(p.target_bin(t, "foo").is_file()); + + p.cargo("clean").run(); + + p.change_file( + ".cargo/config.toml", + &format!( + r#" + [build] + target = ["{t}"] + "# + ), + ); + + p.cargo("build").run(); + + assert!(p.target_bin(t, "foo").is_file()); +} + +#[cargo_test] +fn works_with_env() { + let t = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build").env("CARGO_BUILD_TARGET", t).run(); + + assert!(p.target_bin(t, "foo").is_file()); +} diff --git a/tests/testsuite/net_config.rs b/tests/testsuite/net_config.rs new file mode 100644 index 0000000..569ec55 --- /dev/null +++ b/tests/testsuite/net_config.rs @@ -0,0 +1,74 @@ +//! Tests for network configuration. + +use cargo_test_support::project; + +#[cargo_test] +fn net_retry_loads_from_config() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + git = "http://127.0.0.1:11/foo/bar" + "#, + ) + .file("src/main.rs", "") + .file( + ".cargo/config", + r#" + [net] + retry=1 + [http] + timeout=1 + "#, + ) + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr_contains( + "[WARNING] spurious network error \ + (1 tries remaining): [..]", + ) + .run(); +} + +#[cargo_test] +fn net_retry_git_outputs_warning() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + git = "http://127.0.0.1:11/foo/bar" + "#, + ) + .file( + ".cargo/config", + r#" + [http] + timeout=1 + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check -v -j 1") + .with_status(101) + .with_stderr_contains( + "[WARNING] spurious network error \ + (2 tries remaining): [..]", + ) + .with_stderr_contains("[WARNING] spurious network error (1 tries remaining): [..]") + .run(); +} diff --git a/tests/testsuite/new.rs b/tests/testsuite/new.rs new file mode 100644 index 0000000..1564991 --- /dev/null +++ b/tests/testsuite/new.rs @@ -0,0 +1,543 @@ +//! Tests for the `cargo new` command. + +use cargo_test_support::cargo_process; +use cargo_test_support::paths; +use std::env; +use std::fs::{self, File}; + +fn create_default_gitconfig() { + // This helps on Windows where libgit2 is very aggressive in attempting to + // find a git config file. + let gitconfig = paths::home().join(".gitconfig"); + File::create(gitconfig).unwrap(); + + // If we're running this under a user account that has a different default branch set up + // then tests that assume the default branch is master will fail. We set the default branch + // to master explicitly so that tests that rely on this behavior still pass. + fs::write( + paths::home().join(".gitconfig"), + r#" + [init] + defaultBranch = master + "#, + ) + .unwrap(); +} + +#[cargo_test] +fn simple_lib() { + cargo_process("new --lib foo --vcs none --edition 2015") + .with_stderr("[CREATED] library `foo` package") + .run(); + + assert!(paths::root().join("foo").is_dir()); + assert!(paths::root().join("foo/Cargo.toml").is_file()); + assert!(paths::root().join("foo/src/lib.rs").is_file()); + assert!(!paths::root().join("foo/.gitignore").is_file()); + + let lib = paths::root().join("foo/src/lib.rs"); + let contents = fs::read_to_string(&lib).unwrap(); + assert_eq!( + contents, + r#"pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} +"# + ); + + cargo_process("build").cwd(&paths::root().join("foo")).run(); +} + +#[cargo_test] +fn simple_bin() { + cargo_process("new --bin foo --edition 2015") + .with_stderr("[CREATED] binary (application) `foo` package") + .run(); + + assert!(paths::root().join("foo").is_dir()); + assert!(paths::root().join("foo/Cargo.toml").is_file()); + assert!(paths::root().join("foo/src/main.rs").is_file()); + + cargo_process("build").cwd(&paths::root().join("foo")).run(); + assert!(paths::root() + .join(&format!("foo/target/debug/foo{}", env::consts::EXE_SUFFIX)) + .is_file()); +} + +#[cargo_test] +fn both_lib_and_bin() { + cargo_process("new --lib --bin foo") + .with_status(101) + .with_stderr("[ERROR] can't specify both lib and binary outputs") + .run(); +} + +#[cargo_test] +fn simple_git() { + cargo_process("new --lib foo --edition 2015").run(); + + assert!(paths::root().is_dir()); + assert!(paths::root().join("foo/Cargo.toml").is_file()); + assert!(paths::root().join("foo/src/lib.rs").is_file()); + assert!(paths::root().join("foo/.git").is_dir()); + assert!(paths::root().join("foo/.gitignore").is_file()); + + let fp = paths::root().join("foo/.gitignore"); + let contents = fs::read_to_string(&fp).unwrap(); + assert_eq!(contents, "/target\n/Cargo.lock\n",); + + cargo_process("build").cwd(&paths::root().join("foo")).run(); +} + +#[cargo_test] +fn no_argument() { + cargo_process("new") + .with_status(1) + .with_stderr_contains( + "\ +error: the following required arguments were not provided: + +", + ) + .run(); +} + +#[cargo_test] +fn existing() { + let dst = paths::root().join("foo"); + fs::create_dir(&dst).unwrap(); + cargo_process("new foo") + .with_status(101) + .with_stderr( + "[ERROR] destination `[CWD]/foo` already exists\n\n\ + Use `cargo init` to initialize the directory", + ) + .run(); +} + +#[cargo_test] +fn invalid_characters() { + cargo_process("new foo.rs") + .with_status(101) + .with_stderr( + "\ +[ERROR] invalid character `.` in package name: `foo.rs`, [..] +If you need a package name to not match the directory name, consider using --name flag. +If you need a binary with the name \"foo.rs\", use a valid package name, \ +and set the binary name to be different from the package. \ +This can be done by setting the binary filename to `src/bin/foo.rs.rs` \ +or change the name in Cargo.toml with: + + [[bin]] + name = \"foo.rs\" + path = \"src/main.rs\" + +", + ) + .run(); +} + +#[cargo_test] +fn reserved_name() { + cargo_process("new test") + .with_status(101) + .with_stderr( + "\ +[ERROR] the name `test` cannot be used as a package name, it conflicts [..] +If you need a package name to not match the directory name, consider using --name flag. +If you need a binary with the name \"test\", use a valid package name, \ +and set the binary name to be different from the package. \ +This can be done by setting the binary filename to `src/bin/test.rs` \ +or change the name in Cargo.toml with: + + [[bin]] + name = \"test\" + path = \"src/main.rs\" + +", + ) + .run(); +} + +#[cargo_test] +fn reserved_binary_name() { + cargo_process("new --bin incremental") + .with_status(101) + .with_stderr( + "\ +[ERROR] the name `incremental` cannot be used as a package name, it conflicts [..] +If you need a package name to not match the directory name, consider using --name flag. +", + ) + .run(); + + cargo_process("new --lib incremental") + .with_stderr( + "\ +[WARNING] the name `incremental` will not support binary executables with that name, \ +it conflicts with cargo's build directory names +[CREATED] library `incremental` package +", + ) + .run(); +} + +#[cargo_test] +fn keyword_name() { + cargo_process("new pub") + .with_status(101) + .with_stderr( + "\ +[ERROR] the name `pub` cannot be used as a package name, it is a Rust keyword +If you need a package name to not match the directory name, consider using --name flag. +If you need a binary with the name \"pub\", use a valid package name, \ +and set the binary name to be different from the package. \ +This can be done by setting the binary filename to `src/bin/pub.rs` \ +or change the name in Cargo.toml with: + + [[bin]] + name = \"pub\" + path = \"src/main.rs\" + +", + ) + .run(); +} + +#[cargo_test] +fn std_name() { + cargo_process("new core") + .with_stderr( + "\ +[WARNING] the name `core` is part of Rust's standard library +It is recommended to use a different name to avoid problems. +If you need a package name to not match the directory name, consider using --name flag. +If you need a binary with the name \"core\", use a valid package name, \ +and set the binary name to be different from the package. \ +This can be done by setting the binary filename to `src/bin/core.rs` \ +or change the name in Cargo.toml with: + + [[bin]] + name = \"core\" + path = \"src/main.rs\" + +[CREATED] binary (application) `core` package +", + ) + .run(); +} + +#[cargo_test] +fn git_prefers_command_line() { + let root = paths::root(); + fs::create_dir(&root.join(".cargo")).unwrap(); + fs::write( + &root.join(".cargo/config"), + r#" + [cargo-new] + vcs = "none" + name = "foo" + email = "bar" + "#, + ) + .unwrap(); + + cargo_process("new foo --vcs git").run(); + assert!(paths::root().join("foo/.gitignore").exists()); + assert!(!fs::read_to_string(paths::root().join("foo/Cargo.toml")) + .unwrap() + .contains("authors =")); +} + +#[cargo_test] +fn subpackage_no_git() { + cargo_process("new foo").run(); + + assert!(paths::root().join("foo/.git").is_dir()); + assert!(paths::root().join("foo/.gitignore").is_file()); + + let subpackage = paths::root().join("foo").join("components"); + fs::create_dir(&subpackage).unwrap(); + cargo_process("new foo/components/subcomponent").run(); + + assert!(!paths::root() + .join("foo/components/subcomponent/.git") + .is_file()); + assert!(!paths::root() + .join("foo/components/subcomponent/.gitignore") + .is_file()); +} + +#[cargo_test] +fn subpackage_git_with_gitignore() { + cargo_process("new foo").run(); + + assert!(paths::root().join("foo/.git").is_dir()); + assert!(paths::root().join("foo/.gitignore").is_file()); + + let gitignore = paths::root().join("foo/.gitignore"); + fs::write(gitignore, b"components").unwrap(); + + let subpackage = paths::root().join("foo/components"); + fs::create_dir(&subpackage).unwrap(); + cargo_process("new foo/components/subcomponent").run(); + + assert!(paths::root() + .join("foo/components/subcomponent/.git") + .is_dir()); + assert!(paths::root() + .join("foo/components/subcomponent/.gitignore") + .is_file()); +} + +#[cargo_test] +fn subpackage_git_with_vcs_arg() { + cargo_process("new foo").run(); + + let subpackage = paths::root().join("foo").join("components"); + fs::create_dir(&subpackage).unwrap(); + cargo_process("new foo/components/subcomponent --vcs git").run(); + + assert!(paths::root() + .join("foo/components/subcomponent/.git") + .is_dir()); + assert!(paths::root() + .join("foo/components/subcomponent/.gitignore") + .is_file()); +} + +#[cargo_test] +fn unknown_flags() { + cargo_process("new foo --flag") + .with_status(1) + .with_stderr_contains("error: unexpected argument '--flag' found") + .run(); +} + +#[cargo_test] +fn explicit_invalid_name_not_suggested() { + cargo_process("new --name 10-invalid a") + .with_status(101) + .with_stderr( + "\ +[ERROR] the name `10-invalid` cannot be used as a package name, \ +the name cannot start with a digit\n\ +If you need a binary with the name \"10-invalid\", use a valid package name, \ +and set the binary name to be different from the package. \ +This can be done by setting the binary filename to `src/bin/10-invalid.rs` \ +or change the name in Cargo.toml with: + + [[bin]] + name = \"10-invalid\" + path = \"src/main.rs\" + +", + ) + .run(); +} + +#[cargo_test] +fn explicit_project_name() { + cargo_process("new --lib foo --name bar") + .with_stderr("[CREATED] library `bar` package") + .run(); +} + +#[cargo_test] +fn new_with_edition_2015() { + cargo_process("new --edition 2015 foo").run(); + let manifest = fs::read_to_string(paths::root().join("foo/Cargo.toml")).unwrap(); + assert!(manifest.contains("edition = \"2015\"")); +} + +#[cargo_test] +fn new_with_edition_2018() { + cargo_process("new --edition 2018 foo").run(); + let manifest = fs::read_to_string(paths::root().join("foo/Cargo.toml")).unwrap(); + assert!(manifest.contains("edition = \"2018\"")); +} + +#[cargo_test] +fn new_default_edition() { + cargo_process("new foo").run(); + let manifest = fs::read_to_string(paths::root().join("foo/Cargo.toml")).unwrap(); + assert!(manifest.contains("edition = \"2021\"")); +} + +#[cargo_test] +fn new_with_bad_edition() { + cargo_process("new --edition something_else foo") + .with_stderr_contains("error: invalid value 'something_else' for '--edition '") + .with_status(1) + .run(); +} + +#[cargo_test] +fn new_with_reference_link() { + cargo_process("new foo").run(); + + let contents = fs::read_to_string(paths::root().join("foo/Cargo.toml")).unwrap(); + assert!(contents.contains("# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html")) +} + +#[cargo_test] +fn lockfile_constant_during_new() { + cargo_process("new foo").run(); + + cargo_process("build").cwd(&paths::root().join("foo")).run(); + let before = fs::read_to_string(paths::root().join("foo/Cargo.lock")).unwrap(); + cargo_process("build").cwd(&paths::root().join("foo")).run(); + let after = fs::read_to_string(paths::root().join("foo/Cargo.lock")).unwrap(); + assert_eq!(before, after); +} + +#[cargo_test] +fn restricted_windows_name() { + if cfg!(windows) { + cargo_process("new nul") + .with_status(101) + .with_stderr( + "\ +[ERROR] cannot use name `nul`, it is a reserved Windows filename +If you need a package name to not match the directory name, consider using --name flag. +", + ) + .run(); + } else { + cargo_process("new nul") + .with_stderr( + "\ +[WARNING] the name `nul` is a reserved Windows filename +This package will not work on Windows platforms. +[CREATED] binary (application) `nul` package +", + ) + .run(); + } +} + +#[cargo_test] +fn non_ascii_name() { + cargo_process("new Привет") + .with_stderr( + "\ +[WARNING] the name `Привет` contains non-ASCII characters +Non-ASCII crate names are not supported by Rust. +[CREATED] binary (application) `Привет` package +", + ) + .run(); +} + +#[cargo_test] +fn non_ascii_name_invalid() { + // These are alphanumeric characters, but not Unicode XID. + cargo_process("new ⒶⒷⒸ") + .with_status(101) + .with_stderr( + "\ +[ERROR] invalid character `Ⓐ` in package name: `ⒶⒷⒸ`, \ +the first character must be a Unicode XID start character (most letters or `_`) +If you need a package name to not match the directory name, consider using --name flag. +If you need a binary with the name \"ⒶⒷⒸ\", use a valid package name, \ +and set the binary name to be different from the package. \ +This can be done by setting the binary filename to `src/bin/ⒶⒷⒸ.rs` \ +or change the name in Cargo.toml with: + + [[bin]] + name = \"ⒶⒷⒸ\" + path = \"src/main.rs\" + +", + ) + .run(); + + cargo_process("new a¼") + .with_status(101) + .with_stderr( + "\ +[ERROR] invalid character `¼` in package name: `a¼`, \ +characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) +If you need a package name to not match the directory name, consider using --name flag. +If you need a binary with the name \"a¼\", use a valid package name, \ +and set the binary name to be different from the package. \ +This can be done by setting the binary filename to `src/bin/a¼.rs` \ +or change the name in Cargo.toml with: + + [[bin]] + name = \"a¼\" + path = \"src/main.rs\" + +", + ) + .run(); +} + +#[cargo_test] +fn git_default_branch() { + // Check for init.defaultBranch support. + create_default_gitconfig(); + + cargo_process("new foo").run(); + let repo = git2::Repository::open(paths::root().join("foo")).unwrap(); + let head = repo.find_reference("HEAD").unwrap(); + assert_eq!(head.symbolic_target().unwrap(), "refs/heads/master"); + + fs::write( + paths::home().join(".gitconfig"), + r#" + [init] + defaultBranch = hello + "#, + ) + .unwrap(); + cargo_process("new bar").run(); + let repo = git2::Repository::open(paths::root().join("bar")).unwrap(); + let head = repo.find_reference("HEAD").unwrap(); + assert_eq!(head.symbolic_target().unwrap(), "refs/heads/hello"); +} + +#[cargo_test] +fn non_utf8_str_in_ignore_file() { + let gitignore = paths::home().join(".gitignore"); + File::create(gitignore).unwrap(); + + fs::write(paths::home().join(".gitignore"), &[0xFF, 0xFE]).unwrap(); + + cargo_process(&format!("init {} --vcs git", paths::home().display())) + .with_status(101) + .with_stderr( + "\ +error: Failed to create package `home` at `[..]` + +Caused by: + Character at line 0 is invalid. Cargo only supports UTF-8. +", + ) + .run(); +} + +#[cfg(unix)] +#[cargo_test] +fn path_with_invalid_character() { + cargo_process("new --name testing test:ing") + .with_stderr( + "\ +[WARNING] the path `[CWD]/test:ing` contains invalid PATH characters (usually `:`, `;`, or `\"`) +It is recommended to use a different name to avoid problems. +[CREATED] binary (application) `testing` package +", + ) + .run(); +} diff --git a/tests/testsuite/offline.rs b/tests/testsuite/offline.rs new file mode 100644 index 0000000..cd4bc10 --- /dev/null +++ b/tests/testsuite/offline.rs @@ -0,0 +1,715 @@ +//! Tests for --offline flag. + +use cargo_test_support::{basic_manifest, git, main_file, path2url, project, registry::Package}; +use std::fs; + +#[cargo_test] +fn offline_unused_target_dep() { + // --offline with a target dependency that is not used and not downloaded. + Package::new("unused_dep", "1.0.0").publish(); + Package::new("used_dep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + used_dep = "1.0" + [target.'cfg(unused)'.dependencies] + unused_dep = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + // Do a build that downloads only what is necessary. + p.cargo("check") + .with_stderr_contains("[DOWNLOADED] used_dep [..]") + .with_stderr_does_not_contain("[DOWNLOADED] unused_dep [..]") + .run(); + p.cargo("clean").run(); + // Build offline, make sure it works. + p.cargo("check --offline").run(); +} + +#[cargo_test] +fn offline_missing_optional() { + Package::new("opt_dep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + opt_dep = { version = "1.0", optional = true } + "#, + ) + .file("src/lib.rs", "") + .build(); + // Do a build that downloads only what is necessary. + p.cargo("check") + .with_stderr_does_not_contain("[DOWNLOADED] opt_dep [..]") + .run(); + p.cargo("clean").run(); + // Build offline, make sure it works. + p.cargo("check --offline").run(); + p.cargo("check --offline --features=opt_dep") + .with_stderr( + "\ +[ERROR] failed to download `opt_dep v1.0.0` + +Caused by: + attempting to make an HTTP request, but --offline was specified +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn cargo_compile_path_with_offline() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check --offline").run(); +} + +#[cargo_test] +fn cargo_compile_with_downloaded_dependency_with_offline() { + Package::new("present_dep", "1.2.3") + .file("Cargo.toml", &basic_manifest("present_dep", "1.2.3")) + .file("src/lib.rs", "") + .publish(); + + // make package downloaded + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "1.2.3" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check").run(); + + let p2 = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + present_dep = "1.2.3" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p2.cargo("check --offline") + .with_stderr( + "\ +[CHECKING] present_dep v1.2.3 +[CHECKING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_offline_not_try_update() { + // When --offline needs to download the registry, provide a reasonable + // error hint to run without --offline. + let p = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + not_cached_dep = "1.2.5" + "#, + ) + .file("src/lib.rs", "") + .build(); + + let msg = "\ +[ERROR] no matching package named `not_cached_dep` found +location searched: registry `crates-io` +required by package `bar v0.1.0 ([..]/bar)` +As a reminder, you're using offline mode (--offline) which can sometimes cause \ +surprising resolution failures, if this error is too confusing you may wish to \ +retry without the offline flag. +"; + + p.cargo("check --offline") + .with_status(101) + .with_stderr(msg) + .run(); + + // While we're here, also check the config works. + p.change_file(".cargo/config", "net.offline = true"); + p.cargo("check").with_status(101).with_stderr(msg).run(); +} + +#[cargo_test] +fn compile_offline_without_maxvers_cached() { + Package::new("present_dep", "1.2.1").publish(); + Package::new("present_dep", "1.2.2").publish(); + + Package::new("present_dep", "1.2.3") + .file("Cargo.toml", &basic_manifest("present_dep", "1.2.3")) + .file( + "src/lib.rs", + r#"pub fn get_version()->&'static str {"1.2.3"}"#, + ) + .publish(); + + Package::new("present_dep", "1.2.5") + .file("Cargo.toml", &basic_manifest("present_dep", "1.2.5")) + .file("src/lib.rs", r#"pub fn get_version(){"1.2.5"}"#) + .publish(); + + // make package cached + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "=1.2.3" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build").run(); + + let p2 = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "1.2" + "#, + ) + .file( + "src/main.rs", + "\ +extern crate present_dep; +fn main(){ + println!(\"{}\", present_dep::get_version()); +}", + ) + .build(); + + p2.cargo("run --offline") + .with_stderr( + "\ +[COMPILING] present_dep v1.2.3 +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + Running `[..]`", + ) + .with_stdout("1.2.3") + .run(); +} + +#[cargo_test] +fn cargo_compile_forbird_git_httpsrepo_offline() { + let p = project() + .file( + "Cargo.toml", + r#" + + [package] + name = "foo" + version = "0.5.0" + authors = ["chabapok@example.com"] + + [dependencies.dep1] + git = 'https://github.com/some_user/dep1.git' + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("check --offline").with_status(101).with_stderr("\ +[ERROR] failed to get `dep1` as a dependency of package `foo v0.5.0 [..]` + +Caused by: + failed to load source for dependency `dep1` + +Caused by: + Unable to update https://github.com/some_user/dep1.git + +Caused by: + can't checkout from 'https://github.com/some_user/dep1.git': you are in the offline mode (--offline)").run(); +} + +#[cargo_test] +fn compile_offline_while_transitive_dep_not_cached() { + let baz = Package::new("baz", "1.0.0"); + let baz_path = baz.archive_dst(); + baz.publish(); + + let baz_content = fs::read(&baz_path).unwrap(); + // Truncate the file to simulate a download failure. + fs::write(&baz_path, &[]).unwrap(); + + Package::new("bar", "0.1.0").dep("baz", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main(){}") + .build(); + + // simulate download bar, but fail to download baz + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]failed to verify the checksum of `baz[..]") + .run(); + + // Restore the file contents. + fs::write(&baz_path, &baz_content).unwrap(); + + p.cargo("check --offline") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to download `bar v0.1.0` + +Caused by: + attempting to make an HTTP request, but --offline was specified +", + ) + .run(); +} + +#[cargo_test] +fn update_offline_not_cached() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("update --offline") + .with_status(101) + .with_stderr( + "\ +[ERROR] no matching package named `bar` found +location searched: registry `[..]` +required by package `foo v0.0.1 ([..]/foo)` +As a reminder, you're using offline mode (--offline) which can sometimes cause \ +surprising resolution failures, if this error is too confusing you may wish to \ +retry without the offline flag.", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_offline_with_cached_git_dep() { + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + .file( + "src/lib.rs", + r#" + pub static COOL_STR:&str = "cached git repo rev1"; + "#, + ) + }); + + let repo = git2::Repository::open(&git_project.root()).unwrap(); + let rev1 = repo.revparse_single("HEAD").unwrap().id(); + + // Commit the changes and make sure we trigger a recompile + git_project.change_file( + "src/lib.rs", + r#"pub static COOL_STR:&str = "cached git repo rev2";"#, + ); + git::add(&repo); + let rev2 = git::commit(&repo); + + // cache to registry rev1 and rev2 + let prj = project() + .at("cache_git_dep") + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "cache_git_dep" + version = "0.5.0" + + [dependencies.dep1] + git = '{}' + rev = "{}" + "#, + git_project.url(), + rev1 + ), + ) + .file("src/main.rs", "fn main(){}") + .build(); + prj.cargo("build").run(); + + prj.change_file( + "Cargo.toml", + &format!( + r#" + [package] + name = "cache_git_dep" + version = "0.5.0" + + [dependencies.dep1] + git = '{}' + rev = "{}" + "#, + git_project.url(), + rev2 + ), + ); + prj.cargo("build").run(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies.dep1] + git = '{}' + "#, + git_project.url() + ), + ) + .file( + "src/main.rs", + &main_file(r#""hello from {}", dep1::COOL_STR"#, &["dep1"]), + ) + .build(); + + let git_root = git_project.root(); + + p.cargo("build --offline") + .with_stderr(format!( + "\ +[COMPILING] dep1 v0.5.0 ({}#[..]) +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + path2url(git_root), + )) + .run(); + + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")) + .with_stdout("hello from cached git repo rev2\n") + .run(); + + p.change_file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies.dep1] + git = '{}' + rev = "{}" + "#, + git_project.url(), + rev1 + ), + ); + + p.cargo("build --offline").run(); + p.process(&p.bin("foo")) + .with_stdout("hello from cached git repo rev1\n") + .run(); +} + +#[cargo_test] +fn offline_resolve_optional_fail() { + // Example where resolve fails offline. + // + // This happens if at least 1 version of an optional dependency is + // available, but none of them satisfy the requirements. The current logic + // that handles this is `RegistryIndex::query_inner`, and it doesn't know + // if the package being queried is an optional one. This is not ideal, it + // would be best if it just ignored optional (unselected) dependencies. + Package::new("dep", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = { version = "1.0", optional = true } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fetch").run(); + + // Change dep to 2.0. + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = { version = "2.0", optional = true } + "#, + ); + + p.cargo("check --offline") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to select a version for the requirement `dep = \"^2.0\"` +candidate versions found which didn't match: 1.0.0 +location searched: `[..]` index (which is replacing registry `crates-io`) +required by package `foo v0.1.0 ([..]/foo)` +perhaps a crate was updated and forgotten to be re-vendored? +As a reminder, you're using offline mode (--offline) which can sometimes cause \ +surprising resolution failures, if this error is too confusing you may wish to \ +retry without the offline flag. +", + ) + .run(); +} + +#[cargo_test] +fn offline_with_all_patched() { + // Offline works if everything is patched. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = "1.0" + + [patch.crates-io] + dep = {path = "dep"} + "#, + ) + .file("src/lib.rs", "pub fn f() { dep::foo(); }") + .file("dep/Cargo.toml", &basic_manifest("dep", "1.0.0")) + .file("dep/src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("check --offline").run(); +} + +#[cargo_test] +fn update_offline_cached() { + // Cache a few versions to update against + let p = project().file("src/lib.rs", "").build(); + let versions = ["1.2.3", "1.2.5", "1.2.9"]; + for vers in versions.iter() { + Package::new("present_dep", vers) + .file("Cargo.toml", &basic_manifest("present_dep", vers)) + .file( + "src/lib.rs", + format!(r#"pub fn get_version()->&'static str {{ "{}" }}"#, vers).as_str(), + ) + .publish(); + // make package cached + p.change_file( + "Cargo.toml", + format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "={}" + "#, + vers + ) + .as_str(), + ); + p.cargo("build").run(); + } + + let p2 = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "1.2" + "#, + ) + .file( + "src/main.rs", + "\ +extern crate present_dep; +fn main(){ + println!(\"{}\", present_dep::get_version()); +}", + ) + .build(); + + p2.cargo("build --offline") + .with_stderr( + "\ +[COMPILING] present_dep v1.2.9 +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p2.rename_run("foo", "with_1_2_9") + .with_stdout("1.2.9") + .run(); + // updates happen without updating the index + p2.cargo("update -p present_dep --precise 1.2.3 --offline") + .with_status(0) + .with_stderr( + "\ +[UPDATING] present_dep v1.2.9 -> v1.2.3 +", + ) + .run(); + + p2.cargo("build --offline") + .with_stderr( + "\ +[COMPILING] present_dep v1.2.3 +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p2.rename_run("foo", "with_1_2_3") + .with_stdout("1.2.3") + .run(); + + // Offline update should only print package details and not index updating + p2.cargo("update --offline") + .with_status(0) + .with_stderr( + "\ +[UPDATING] present_dep v1.2.3 -> v1.2.9 +", + ) + .run(); + + // No v1.2.8 loaded into the cache so expect failure. + p2.cargo("update -p present_dep --precise 1.2.8 --offline") + .with_status(101) + .with_stderr( + "\ +[ERROR] no matching package named `present_dep` found +location searched: registry `[..]` +required by package `foo v0.1.0 ([..]/foo)` +As a reminder, you're using offline mode (--offline) which can sometimes cause \ +surprising resolution failures, if this error is too confusing you may wish to \ +retry without the offline flag. +", + ) + .run(); +} + +#[cargo_test] +fn offline_and_frozen_and_no_lock() { + let p = project().file("src/lib.rs", "").build(); + p.cargo("check --frozen --offline") + .with_status(101) + .with_stderr("\ +error: the lock file [ROOT]/foo/Cargo.lock needs to be updated but --frozen was passed to prevent this +If you want to try to generate the lock file without accessing the network, \ +remove the --frozen flag and use --offline instead. +") + .run(); +} + +#[cargo_test] +fn offline_and_locked_and_no_frozen() { + let p = project().file("src/lib.rs", "").build(); + p.cargo("check --locked --offline") + .with_status(101) + .with_stderr("\ +error: the lock file [ROOT]/foo/Cargo.lock needs to be updated but --locked was passed to prevent this +If you want to try to generate the lock file without accessing the network, \ +remove the --locked flag and use --offline instead. +") + .run(); +} diff --git a/tests/testsuite/old_cargos.rs b/tests/testsuite/old_cargos.rs new file mode 100644 index 0000000..a85e13d --- /dev/null +++ b/tests/testsuite/old_cargos.rs @@ -0,0 +1,679 @@ +//! Tests for checking behavior of old cargos. +//! +//! These tests are ignored because it is intended to be run on a developer +//! system with a bunch of toolchains installed. This requires `rustup` to be +//! installed. It will iterate over installed toolchains, and run some tests +//! over each one, producing a report at the end. As of this writing, I have +//! tested 1.0 to 1.51. Run this with: +//! +//! ```console +//! cargo test --test testsuite -- old_cargos --nocapture --ignored +//! ``` + +use cargo::CargoResult; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::{self, Dependency, Package}; +use cargo_test_support::{cargo_exe, execs, paths, process, project, rustc_host}; +use cargo_util::{ProcessBuilder, ProcessError}; +use semver::Version; +use std::fs; + +fn tc_process(cmd: &str, toolchain: &str) -> ProcessBuilder { + let mut p = if toolchain == "this" { + if cmd == "cargo" { + process(&cargo_exe()) + } else { + process(cmd) + } + } else { + let mut cmd = process(cmd); + cmd.arg(format!("+{}", toolchain)); + cmd + }; + // Reset PATH since `process` modifies it to remove rustup. + p.env("PATH", std::env::var_os("PATH").unwrap()); + p +} + +/// Returns a sorted list of all toolchains. +/// +/// The returned value includes the parsed version, and the rustup toolchain +/// name as a string. +fn collect_all_toolchains() -> Vec<(Version, String)> { + let rustc_version = |tc| { + let mut cmd = tc_process("rustc", tc); + cmd.arg("-V"); + let output = cmd.exec_with_output().expect("rustc installed"); + let version = std::str::from_utf8(&output.stdout).unwrap(); + let parts: Vec<_> = version.split_whitespace().collect(); + assert_eq!(parts[0], "rustc"); + assert!(parts[1].starts_with("1.")); + Version::parse(parts[1]).expect("valid version") + }; + + // Provide a way to override the list. + if let Ok(tcs) = std::env::var("OLD_CARGO") { + return tcs + .split(',') + .map(|tc| (rustc_version(tc), tc.to_string())) + .collect(); + } + + let host = rustc_host(); + // I tend to have lots of toolchains installed, but I don't want to test + // all of them (like dated nightlies, or toolchains for non-host targets). + let valid_names = &[ + format!("stable-{}", host), + format!("beta-{}", host), + format!("nightly-{}", host), + ]; + + let output = ProcessBuilder::new("rustup") + .args(&["toolchain", "list"]) + .exec_with_output() + .expect("rustup should be installed"); + let stdout = std::str::from_utf8(&output.stdout).unwrap(); + let mut toolchains: Vec<_> = stdout + .lines() + .map(|line| { + // Some lines say things like (default), just get the version. + line.split_whitespace().next().expect("non-empty line") + }) + .filter(|line| { + line.ends_with(&host) + && (line.starts_with("1.") || valid_names.iter().any(|name| name == line)) + }) + .map(|line| (rustc_version(line), line.to_string())) + .collect(); + + toolchains.sort_by(|a, b| a.0.cmp(&b.0)); + toolchains +} + +/// Returns whether the default toolchain is the stable version. +fn default_toolchain_is_stable() -> bool { + let default = tc_process("rustc", "this").arg("-V").exec_with_output(); + let stable = tc_process("rustc", "stable").arg("-V").exec_with_output(); + match (default, stable) { + (Ok(d), Ok(s)) => d.stdout == s.stdout, + _ => false, + } +} + +// This is a test for exercising the behavior of older versions of cargo with +// the new feature syntax. +// +// The test involves a few dependencies with different feature requirements: +// +// * `bar` 1.0.0 is the base version that does not use the new syntax. +// * `bar` 1.0.1 has a feature with the new syntax, but the feature is unused. +// The optional dependency `new-baz-dep` should not be activated. +// * `bar` 1.0.2 has a dependency on `baz` that *requires* the new feature +// syntax. +#[ignore = "must be run manually, requires old cargo installations"] +#[cargo_test] +fn new_features() { + let registry = registry::init(); + if std::process::Command::new("rustup").output().is_err() { + panic!("old_cargos requires rustup to be installed"); + } + Package::new("new-baz-dep", "1.0.0").publish(); + + Package::new("baz", "1.0.0").publish(); + let baz101_cksum = Package::new("baz", "1.0.1") + .add_dep(Dependency::new("new-baz-dep", "1.0").optional(true)) + .feature("new-feat", &["dep:new-baz-dep"]) + .publish(); + + let bar100_cksum = Package::new("bar", "1.0.0") + .add_dep(Dependency::new("baz", "1.0").optional(true)) + .feature("feat", &["baz"]) + .publish(); + let bar101_cksum = Package::new("bar", "1.0.1") + .add_dep(Dependency::new("baz", "1.0").optional(true)) + .feature("feat", &["dep:baz"]) + .publish(); + let bar102_cksum = Package::new("bar", "1.0.2") + .add_dep(Dependency::new("baz", "1.0").enable_features(&["new-feat"])) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + let lock_bar_to = |toolchain_version: &Version, bar_version| { + let lock = if toolchain_version < &Version::new(1, 12, 0) { + let url = registry.index_url(); + match bar_version { + 100 => format!( + r#" + [root] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar 1.0.0 (registry+{url})", + ] + + [[package]] + name = "bar" + version = "1.0.0" + source = "registry+{url}" + "#, + url = url + ), + 101 => format!( + r#" + [root] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar 1.0.1 (registry+{url})", + ] + + [[package]] + name = "bar" + version = "1.0.1" + source = "registry+{url}" + "#, + url = url + ), + 102 => format!( + r#" + [root] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar 1.0.2 (registry+{url})", + ] + + [[package]] + name = "bar" + version = "1.0.2" + source = "registry+{url}" + dependencies = [ + "baz 1.0.1 (registry+{url})", + ] + + [[package]] + name = "baz" + version = "1.0.1" + source = "registry+{url}" + "#, + url = url + ), + _ => panic!("unexpected version"), + } + } else { + match bar_version { + 100 => format!( + r#" + [root] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "bar" + version = "1.0.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + + [metadata] + "checksum bar 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "{}" + "#, + bar100_cksum + ), + 101 => format!( + r#" + [root] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "bar" + version = "1.0.1" + source = "registry+https://github.com/rust-lang/crates.io-index" + + [metadata] + "checksum bar 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "{}" + "#, + bar101_cksum + ), + 102 => format!( + r#" + [root] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "bar" + version = "1.0.2" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ + "baz 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "baz" + version = "1.0.1" + source = "registry+https://github.com/rust-lang/crates.io-index" + + [metadata] + "checksum bar 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "{bar102_cksum}" + "checksum baz 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "{baz101_cksum}" + "#, + bar102_cksum = bar102_cksum, + baz101_cksum = baz101_cksum + ), + _ => panic!("unexpected version"), + } + }; + p.change_file("Cargo.lock", &lock); + }; + + let toolchains = collect_all_toolchains(); + + let config_path = paths::home().join(".cargo/config"); + let lock_path = p.root().join("Cargo.lock"); + + struct ToolchainBehavior { + bar: Option, + baz: Option, + new_baz_dep: Option, + } + + // Collect errors to print at the end. One entry per toolchain, a list of + // strings to print. + let mut unexpected_results: Vec> = Vec::new(); + + for (version, toolchain) in &toolchains { + let mut tc_result = Vec::new(); + // Write a config appropriate for this version. + if version < &Version::new(1, 12, 0) { + fs::write( + &config_path, + format!( + r#" + [registry] + index = "{}" + "#, + registry.index_url() + ), + ) + .unwrap(); + } else { + fs::write( + &config_path, + format!( + " + [source.crates-io] + registry = 'https://wut' # only needed by 1.12 + replace-with = 'dummy-registry' + + [source.dummy-registry] + registry = '{}' + ", + registry.index_url() + ), + ) + .unwrap(); + } + + // Fetches the version of a package in the lock file. + let pkg_version = |pkg| -> Option { + let output = tc_process("cargo", toolchain) + .args(&["pkgid", pkg]) + .cwd(p.root()) + .exec_with_output() + .ok()?; + let stdout = std::str::from_utf8(&output.stdout).unwrap(); + let version = stdout + .trim() + .rsplitn(2, ':') + .next() + .expect("version after colon"); + Some(Version::parse(version).expect("parseable version")) + }; + + // Runs `cargo build` and returns the versions selected in the lock. + let run_cargo = || -> CargoResult { + match tc_process("cargo", toolchain) + .args(&["build", "--verbose"]) + .cwd(p.root()) + .exec_with_output() + { + Ok(_output) => { + eprintln!("{} ok", toolchain); + let bar = pkg_version("bar"); + let baz = pkg_version("baz"); + let new_baz_dep = pkg_version("new-baz-dep"); + Ok(ToolchainBehavior { + bar, + baz, + new_baz_dep, + }) + } + Err(e) => { + eprintln!("{} err {}", toolchain, e); + Err(e) + } + } + }; + + macro_rules! check_lock { + ($tc_result:ident, $pkg:expr, $which:expr, $actual:expr, None) => { + check_lock!(= $tc_result, $pkg, $which, $actual, None); + }; + ($tc_result:ident, $pkg:expr, $which:expr, $actual:expr, $expected:expr) => { + check_lock!(= $tc_result, $pkg, $which, $actual, Some(Version::parse($expected).unwrap())); + }; + (= $tc_result:ident, $pkg:expr, $which:expr, $actual:expr, $expected:expr) => { + let exp: Option = $expected; + if $actual != $expected { + $tc_result.push(format!( + "{} for {} saw {:?} but expected {:?}", + $which, $pkg, $actual, exp + )); + } + }; + } + + let check_err_contains = |tc_result: &mut Vec<_>, err: anyhow::Error, contents| { + if let Some(ProcessError { + stderr: Some(stderr), + .. + }) = err.downcast_ref::() + { + let stderr = std::str::from_utf8(stderr).unwrap(); + if !stderr.contains(contents) { + tc_result.push(format!( + "{} expected to see error contents:\n{}\nbut saw:\n{}", + toolchain, contents, stderr + )); + } + } else { + panic!("{} unexpected error {}", toolchain, err); + } + }; + + // Unlocked behavior. + let which = "unlocked"; + lock_path.rm_rf(); + p.build_dir().rm_rf(); + match run_cargo() { + Ok(behavior) => { + if version < &Version::new(1, 51, 0) { + check_lock!(tc_result, "bar", which, behavior.bar, "1.0.2"); + check_lock!(tc_result, "baz", which, behavior.baz, "1.0.1"); + check_lock!(tc_result, "new-baz-dep", which, behavior.new_baz_dep, None); + } else if version >= &Version::new(1, 51, 0) && version <= &Version::new(1, 59, 0) { + check_lock!(tc_result, "bar", which, behavior.bar, "1.0.0"); + check_lock!(tc_result, "baz", which, behavior.baz, None); + check_lock!(tc_result, "new-baz-dep", which, behavior.new_baz_dep, None); + } + // Starting with 1.60, namespaced-features has been stabilized. + else { + check_lock!(tc_result, "bar", which, behavior.bar, "1.0.2"); + check_lock!(tc_result, "baz", which, behavior.baz, "1.0.1"); + check_lock!( + tc_result, + "new-baz-dep", + which, + behavior.new_baz_dep, + "1.0.0" + ); + } + } + Err(e) => { + tc_result.push(format!("unlocked build failed: {}", e)); + } + } + + let which = "locked bar 1.0.0"; + lock_bar_to(version, 100); + match run_cargo() { + Ok(behavior) => { + check_lock!(tc_result, "bar", which, behavior.bar, "1.0.0"); + check_lock!(tc_result, "baz", which, behavior.baz, None); + check_lock!(tc_result, "new-baz-dep", which, behavior.new_baz_dep, None); + } + Err(e) => { + tc_result.push(format!("bar 1.0.0 locked build failed: {}", e)); + } + } + + let which = "locked bar 1.0.1"; + lock_bar_to(version, 101); + match run_cargo() { + Ok(behavior) => { + check_lock!(tc_result, "bar", which, behavior.bar, "1.0.1"); + check_lock!(tc_result, "baz", which, behavior.baz, None); + check_lock!(tc_result, "new-baz-dep", which, behavior.new_baz_dep, None); + } + Err(e) => { + // When version >= 1.51 and <= 1.59, + // 1.0.1 can't be used without -Znamespaced-features + // It gets filtered out of the index. + check_err_contains( + &mut tc_result, + e, + "candidate versions found which didn't match: 1.0.2, 1.0.0", + ); + } + } + + let which = "locked bar 1.0.2"; + lock_bar_to(version, 102); + match run_cargo() { + Ok(behavior) => { + if version <= &Version::new(1, 59, 0) { + check_lock!(tc_result, "bar", which, behavior.bar, "1.0.2"); + check_lock!(tc_result, "baz", which, behavior.baz, "1.0.1"); + check_lock!(tc_result, "new-baz-dep", which, behavior.new_baz_dep, None); + } + // Starting with 1.60, namespaced-features has been stabilized. + else { + check_lock!(tc_result, "bar", which, behavior.bar, "1.0.2"); + check_lock!(tc_result, "baz", which, behavior.baz, "1.0.1"); + check_lock!( + tc_result, + "new-baz-dep", + which, + behavior.new_baz_dep, + "1.0.0" + ); + } + } + Err(e) => { + // When version >= 1.51 and <= 1.59, + // baz can't lock to 1.0.1, it requires -Znamespaced-features + check_err_contains( + &mut tc_result, + e, + "candidate versions found which didn't match: 1.0.0", + ); + } + } + + unexpected_results.push(tc_result); + } + + // Generate a report. + let mut has_err = false; + for ((tc_vers, tc_name), errs) in toolchains.iter().zip(unexpected_results) { + if errs.is_empty() { + continue; + } + eprintln!("error: toolchain {} (version {}):", tc_name, tc_vers); + for err in errs { + eprintln!(" {}", err); + } + has_err = true; + } + if has_err { + panic!("at least one toolchain did not run as expected"); + } +} + +#[cargo_test] +#[ignore = "must be run manually, requires old cargo installations"] +fn index_cache_rebuild() { + // Checks that the index cache gets rebuilt. + // + // 1.48 will not cache entries with features with the same name as a + // dependency. If the cache does not get rebuilt, then running with + // `-Znamespaced-features` would prevent the new cargo from seeing those + // entries. The index cache version was changed to prevent this from + // happening, and switching between versions should work correctly + // (although it will thrash the cash, that's better than not working + // correctly. + Package::new("baz", "1.0.0").publish(); + Package::new("bar", "1.0.0").publish(); + Package::new("bar", "1.0.1") + .add_dep(Dependency::new("baz", "1.0").optional(true)) + .feature("baz", &["dep:baz"]) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // This version of Cargo errors on index entries that have overlapping + // feature names, so 1.0.1 will be missing. + execs() + .with_process_builder(tc_process("cargo", "1.48.0")) + .arg("check") + .cwd(p.root()) + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 [..] +[CHECKING] bar v1.0.0 +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + fs::remove_file(p.root().join("Cargo.lock")).unwrap(); + + // This should rebuild the cache and use 1.0.1. + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.1 [..] +[CHECKING] bar v1.0.1 +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + fs::remove_file(p.root().join("Cargo.lock")).unwrap(); + + // Verify 1.48 can still resolve, and is at 1.0.0. + execs() + .with_process_builder(tc_process("cargo", "1.48.0")) + .arg("tree") + .cwd(p.root()) + .with_stdout( + "\ +foo v0.1.0 [..] +└── bar v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +#[ignore = "must be run manually, requires old cargo installations"] +fn avoids_split_debuginfo_collision() { + // Test needs two different toolchains. + // If the default toolchain is stable, then it won't work. + if default_toolchain_is_stable() { + return; + } + // Checks for a bug where .o files were being incorrectly shared between + // different toolchains using incremental and split-debuginfo on macOS. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.dev] + split-debuginfo = "unpacked" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + execs() + .with_process_builder(tc_process("cargo", "stable")) + .arg("build") + .env("CARGO_INCREMENTAL", "1") + .cwd(p.root()) + .with_stderr( + "\ +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("build") + .env("CARGO_INCREMENTAL", "1") + .with_stderr( + "\ +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + execs() + .with_process_builder(tc_process("cargo", "stable")) + .arg("build") + .env("CARGO_INCREMENTAL", "1") + .cwd(p.root()) + .with_stderr( + "\ +[FINISHED] [..] +", + ) + .run(); +} diff --git a/tests/testsuite/out_dir.rs b/tests/testsuite/out_dir.rs new file mode 100644 index 0000000..fe647f5 --- /dev/null +++ b/tests/testsuite/out_dir.rs @@ -0,0 +1,317 @@ +//! Tests for --out-dir flag. + +use cargo_test_support::sleep_ms; +use cargo_test_support::{basic_manifest, project}; +use std::env; +use std::fs; +use std::path::Path; + +#[cargo_test] +fn binary_with_debug() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .build(); + + p.cargo("build -Z unstable-options --out-dir out") + .masquerade_as_nightly_cargo(&["out-dir"]) + .enable_mac_dsym() + .run(); + check_dir_contents( + &p.root().join("out"), + &["foo"], + &["foo", "foo.dSYM"], + &["foo.exe", "foo.pdb"], + &["foo.exe"], + ); +} + +#[cargo_test] +fn static_library_with_debug() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + crate-type = ["staticlib"] + "#, + ) + .file( + "src/lib.rs", + r#" + #[no_mangle] + pub extern "C" fn foo() { println!("Hello, World!") } + "#, + ) + .build(); + + p.cargo("build -Z unstable-options --out-dir out") + .masquerade_as_nightly_cargo(&["out-dir"]) + .run(); + check_dir_contents( + &p.root().join("out"), + &["libfoo.a"], + &["libfoo.a"], + &["foo.lib"], + &["libfoo.a"], + ); +} + +#[cargo_test] +fn dynamic_library_with_debug() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + crate-type = ["cdylib"] + "#, + ) + .file( + "src/lib.rs", + r#" + #[no_mangle] + pub extern "C" fn foo() { println!("Hello, World!") } + "#, + ) + .build(); + + p.cargo("build -Z unstable-options --out-dir out") + .masquerade_as_nightly_cargo(&["out-dir"]) + .enable_mac_dsym() + .run(); + check_dir_contents( + &p.root().join("out"), + &["libfoo.so"], + &["libfoo.dylib", "libfoo.dylib.dSYM"], + &["foo.dll", "foo.dll.exp", "foo.dll.lib", "foo.pdb"], + &["foo.dll", "libfoo.dll.a"], + ); +} + +#[cargo_test] +fn rlib_with_debug() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + crate-type = ["rlib"] + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn foo() { println!("Hello, World!") } + "#, + ) + .build(); + + p.cargo("build -Z unstable-options --out-dir out") + .masquerade_as_nightly_cargo(&["out-dir"]) + .run(); + check_dir_contents( + &p.root().join("out"), + &["libfoo.rlib"], + &["libfoo.rlib"], + &["libfoo.rlib"], + &["libfoo.rlib"], + ); +} + +#[cargo_test] +fn include_only_the_binary_from_the_current_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [workspace] + + [dependencies] + utils = { path = "./utils" } + "#, + ) + .file("src/lib.rs", "extern crate utils;") + .file( + "src/main.rs", + r#" + extern crate foo; + extern crate utils; + fn main() { + println!("Hello, World!") + } + "#, + ) + .file("utils/Cargo.toml", &basic_manifest("utils", "0.0.1")) + .file("utils/src/lib.rs", "") + .build(); + + p.cargo("build -Z unstable-options --bin foo --out-dir out") + .masquerade_as_nightly_cargo(&["out-dir"]) + .enable_mac_dsym() + .run(); + check_dir_contents( + &p.root().join("out"), + &["foo"], + &["foo", "foo.dSYM"], + &["foo.exe", "foo.pdb"], + &["foo.exe"], + ); +} + +#[cargo_test] +fn out_dir_is_a_file() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file("out", "") + .build(); + + p.cargo("build -Z unstable-options --out-dir out") + .masquerade_as_nightly_cargo(&["out-dir"]) + .with_status(101) + .with_stderr_contains("[ERROR] failed to create directory [..]") + .run(); +} + +#[cargo_test] +fn replaces_artifacts() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("foo") }"#) + .build(); + + p.cargo("build -Z unstable-options --out-dir out") + .masquerade_as_nightly_cargo(&["out-dir"]) + .run(); + p.process( + &p.root() + .join(&format!("out/foo{}", env::consts::EXE_SUFFIX)), + ) + .with_stdout("foo") + .run(); + + sleep_ms(1000); + p.change_file("src/main.rs", r#"fn main() { println!("bar") }"#); + + p.cargo("build -Z unstable-options --out-dir out") + .masquerade_as_nightly_cargo(&["out-dir"]) + .run(); + p.process( + &p.root() + .join(&format!("out/foo{}", env::consts::EXE_SUFFIX)), + ) + .with_stdout("bar") + .run(); +} + +#[cargo_test] +fn avoid_build_scripts() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/main.rs", "fn main() {}") + .file("a/build.rs", r#"fn main() { println!("hello-build-a"); }"#) + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/main.rs", "fn main() {}") + .file("b/build.rs", r#"fn main() { println!("hello-build-b"); }"#) + .build(); + + p.cargo("build -Z unstable-options --out-dir out -vv") + .masquerade_as_nightly_cargo(&["out-dir"]) + .enable_mac_dsym() + .with_stdout_contains("[a 0.0.1] hello-build-a") + .with_stdout_contains("[b 0.0.1] hello-build-b") + .run(); + check_dir_contents( + &p.root().join("out"), + &["a", "b"], + &["a", "a.dSYM", "b", "b.dSYM"], + &["a.exe", "a.pdb", "b.exe", "b.pdb"], + &["a.exe", "b.exe"], + ); +} + +#[cargo_test] +fn cargo_build_out_dir() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file( + ".cargo/config", + r#" + [build] + out-dir = "out" + "#, + ) + .build(); + + p.cargo("build -Z unstable-options") + .masquerade_as_nightly_cargo(&["out-dir"]) + .enable_mac_dsym() + .run(); + check_dir_contents( + &p.root().join("out"), + &["foo"], + &["foo", "foo.dSYM"], + &["foo.exe", "foo.pdb"], + &["foo.exe"], + ); +} + +fn check_dir_contents( + out_dir: &Path, + expected_linux: &[&str], + expected_mac: &[&str], + expected_win_msvc: &[&str], + expected_win_gnu: &[&str], +) { + let expected = if cfg!(target_os = "windows") { + if cfg!(target_env = "msvc") { + expected_win_msvc + } else { + expected_win_gnu + } + } else if cfg!(target_os = "macos") { + expected_mac + } else { + expected_linux + }; + + let actual = list_dir(out_dir); + let mut expected = expected.iter().map(|s| s.to_string()).collect::>(); + expected.sort_unstable(); + assert_eq!(actual, expected); +} + +fn list_dir(dir: &Path) -> Vec { + let mut res = Vec::new(); + for entry in fs::read_dir(dir).unwrap() { + let entry = entry.unwrap(); + res.push(entry.file_name().into_string().unwrap()); + } + res.sort_unstable(); + res +} diff --git a/tests/testsuite/owner.rs b/tests/testsuite/owner.rs new file mode 100644 index 0000000..9fc960c --- /dev/null +++ b/tests/testsuite/owner.rs @@ -0,0 +1,192 @@ +//! Tests for the `cargo owner` command. + +use std::fs; + +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::project; +use cargo_test_support::registry::{self, api_path}; + +fn setup(name: &str, content: Option<&str>) { + let dir = api_path().join(format!("api/v1/crates/{}", name)); + dir.mkdir_p(); + if let Some(body) = content { + fs::write(dir.join("owners"), body).unwrap(); + } +} + +#[cargo_test] +fn simple_list() { + let registry = registry::init(); + let content = r#"{ + "users": [ + { + "id": 70, + "login": "github:rust-lang:core", + "name": "Core" + }, + { + "id": 123, + "login": "octocat" + } + ] + }"#; + setup("foo", Some(content)); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("owner -l") + .replace_crates_io(registry.index_url()) + .with_stdout( + "\ +github:rust-lang:core (Core) +octocat +", + ) + .run(); +} + +#[cargo_test] +fn simple_add() { + let registry = registry::init(); + setup("foo", None); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("owner -a username") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + " Updating crates.io index +error: failed to invite owners to crate `foo` on registry at file://[..] + +Caused by: + EOF while parsing a value at line 1 column 0", + ) + .run(); +} + +#[cargo_test] +fn simple_add_with_asymmetric() { + let registry = registry::RegistryBuilder::new() + .http_api() + .token(cargo_test_support::registry::Token::rfc_key()) + .build(); + setup("foo", None); + + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + // The http_api server will check that the authorization is correct. + // If the authorization was not sent then we would get an unauthorized error. + p.cargo("owner -a username") + .arg("-Zregistry-auth") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(registry.index_url()) + .with_status(0) + .run(); +} + +#[cargo_test] +fn simple_remove() { + let registry = registry::init(); + setup("foo", None); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("owner -r username") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + " Updating crates.io index + Owner removing [\"username\"] from crate foo +error: failed to remove owners from crate `foo` on registry at file://[..] + +Caused by: + EOF while parsing a value at line 1 column 0", + ) + .run(); +} + +#[cargo_test] +fn simple_remove_with_asymmetric() { + let registry = registry::RegistryBuilder::new() + .http_api() + .token(cargo_test_support::registry::Token::rfc_key()) + .build(); + setup("foo", None); + + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + // The http_api server will check that the authorization is correct. + // If the authorization was not sent then we would get an unauthorized error. + p.cargo("owner -r username") + .arg("-Zregistry-auth") + .replace_crates_io(registry.index_url()) + .masquerade_as_nightly_cargo(&["registry-auth"]) + .with_status(0) + .run(); +} diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs new file mode 100644 index 0000000..14bac66 --- /dev/null +++ b/tests/testsuite/package.rs @@ -0,0 +1,2764 @@ +//! Tests for the `cargo package` command. + +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::publish::validate_crate_contents; +use cargo_test_support::registry::{self, Package}; +use cargo_test_support::{ + basic_manifest, cargo_process, git, path2url, paths, project, symlink_supported, t, +}; +use flate2::read::GzDecoder; +use std::fs::{self, read_to_string, File}; +use std::path::Path; +use tar::Archive; + +#[cargo_test] +fn simple() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + exclude = ["*.txt"] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file("src/bar.txt", "") // should be ignored when packaging + .build(); + + p.cargo("package") + .with_stderr( + "\ +[WARNING] manifest has no documentation[..] +See [..] +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 4 files, [..] ([..] compressed) +", + ) + .run(); + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + p.cargo("package -l") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + p.cargo("package").with_stdout("").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + &[], + ); +} + +#[cargo_test] +fn metadata_warning() { + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("package") + .with_stderr( + "\ +warning: manifest has no description, license, license-file, documentation, \ +homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("package") + .with_stderr( + "\ +warning: manifest has no description, documentation, homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + repository = "bar" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("package") + .with_stderr( + "\ +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn package_verbose() { + let root = paths::root().join("all"); + let repo = git::repo(&root) + .file("Cargo.toml", &basic_manifest("foo", "0.0.1")) + .file("src/main.rs", "fn main() {}") + .file("a/a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/a/src/lib.rs", "") + .build(); + cargo_process("build").cwd(repo.root()).run(); + + println!("package main repo"); + cargo_process("package -v --no-verify") + .cwd(repo.root()) + .with_stderr( + "\ +[WARNING] manifest has no description[..] +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] foo v0.0.1 ([..]) +[ARCHIVING] .cargo_vcs_info.json +[ARCHIVING] Cargo.lock +[ARCHIVING] Cargo.toml +[ARCHIVING] Cargo.toml.orig +[ARCHIVING] src/main.rs +[PACKAGED] 5 files, [..] ([..] compressed) +", + ) + .run(); + + let f = File::open(&repo.root().join("target/package/foo-0.0.1.crate")).unwrap(); + let vcs_contents = format!( + r#"{{ + "git": {{ + "sha1": "{}" + }}, + "path_in_vcs": "" +}} +"#, + repo.revparse_head() + ); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "src/main.rs", + ".cargo_vcs_info.json", + ], + &[(".cargo_vcs_info.json", &vcs_contents)], + ); + + println!("package sub-repo"); + cargo_process("package -v --no-verify") + .cwd(repo.root().join("a/a")) + .with_stderr( + "\ +[WARNING] manifest has no description[..] +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] a v0.0.1 ([..]) +[ARCHIVING] .cargo_vcs_info.json +[ARCHIVING] Cargo.toml +[ARCHIVING] Cargo.toml.orig +[ARCHIVING] src/lib.rs +[PACKAGED] 4 files, [..] ([..] compressed) +", + ) + .run(); + + let f = File::open(&repo.root().join("a/a/target/package/a-0.0.1.crate")).unwrap(); + let vcs_contents = format!( + r#"{{ + "git": {{ + "sha1": "{}" + }}, + "path_in_vcs": "a/a" +}} +"#, + repo.revparse_head() + ); + validate_crate_contents( + f, + "a-0.0.1.crate", + &[ + "Cargo.toml", + "Cargo.toml.orig", + "src/lib.rs", + ".cargo_vcs_info.json", + ], + &[(".cargo_vcs_info.json", &vcs_contents)], + ); +} + +#[cargo_test] +fn package_verification() { + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("build").run(); + p.cargo("package") + .with_stderr( + "\ +[WARNING] manifest has no description[..] +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn vcs_file_collision() { + let p = project().build(); + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + description = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + documentation = "foo" + homepage = "foo" + repository = "foo" + exclude = ["*.no-existe"] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() {} + "#, + ) + .file(".cargo_vcs_info.json", "foo") + .build(); + p.cargo("package") + .arg("--no-verify") + .with_status(101) + .with_stderr( + "\ +[ERROR] invalid inclusion of reserved file name .cargo_vcs_info.json \ +in package source +", + ) + .run(); +} + +#[cargo_test] +fn orig_file_collision() { + let p = project().build(); + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + description = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + documentation = "foo" + homepage = "foo" + repository = "foo" + exclude = ["*.no-existe"] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() {} + "#, + ) + .file("Cargo.toml.orig", "oops") + .build(); + p.cargo("package") + .arg("--no-verify") + .with_status(101) + .with_stderr( + "\ +[ERROR] invalid inclusion of reserved file name Cargo.toml.orig \ +in package source +", + ) + .run(); +} + +#[cargo_test] +fn path_dependency_no_version() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("package") + .with_status(101) + .with_stderr( + "\ +[WARNING] manifest has no documentation, homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[ERROR] all dependencies must have a version specified when packaging. +dependency `bar` does not specify a version\n\ +Note: The packaged dependency will use the version from crates.io, +the `path` specification will be removed from the dependency declaration. +", + ) + .run(); +} + +#[cargo_test] +fn git_dependency_no_version() { + registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [dependencies.foo] + git = "git://path/to/nowhere" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("package") + .with_status(101) + .with_stderr( + "\ +[WARNING] manifest has no documentation, homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[ERROR] all dependencies must have a version specified when packaging. +dependency `foo` does not specify a version +Note: The packaged dependency will use the version from crates.io, +the `git` specification will be removed from the dependency declaration. +", + ) + .run(); +} + +#[cargo_test] +fn exclude() { + let root = paths::root().join("exclude"); + let repo = git::repo(&root) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + exclude = [ + "*.txt", + # file in root + "file_root_1", # NO_CHANGE (ignored) + "/file_root_2", # CHANGING (packaged -> ignored) + "file_root_3/", # NO_CHANGE (packaged) + "file_root_4/*", # NO_CHANGE (packaged) + "file_root_5/**", # NO_CHANGE (packaged) + # file in sub-dir + "file_deep_1", # CHANGING (packaged -> ignored) + "/file_deep_2", # NO_CHANGE (packaged) + "file_deep_3/", # NO_CHANGE (packaged) + "file_deep_4/*", # NO_CHANGE (packaged) + "file_deep_5/**", # NO_CHANGE (packaged) + # dir in root + "dir_root_1", # CHANGING (packaged -> ignored) + "/dir_root_2", # CHANGING (packaged -> ignored) + "dir_root_3/", # CHANGING (packaged -> ignored) + "dir_root_4/*", # NO_CHANGE (ignored) + "dir_root_5/**", # NO_CHANGE (ignored) + # dir in sub-dir + "dir_deep_1", # CHANGING (packaged -> ignored) + "/dir_deep_2", # NO_CHANGE + "dir_deep_3/", # CHANGING (packaged -> ignored) + "dir_deep_4/*", # CHANGING (packaged -> ignored) + "dir_deep_5/**", # CHANGING (packaged -> ignored) + ] + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file("bar.txt", "") + .file("src/bar.txt", "") + // File in root. + .file("file_root_1", "") + .file("file_root_2", "") + .file("file_root_3", "") + .file("file_root_4", "") + .file("file_root_5", "") + // File in sub-dir. + .file("some_dir/file_deep_1", "") + .file("some_dir/file_deep_2", "") + .file("some_dir/file_deep_3", "") + .file("some_dir/file_deep_4", "") + .file("some_dir/file_deep_5", "") + // Dir in root. + .file("dir_root_1/some_dir/file", "") + .file("dir_root_2/some_dir/file", "") + .file("dir_root_3/some_dir/file", "") + .file("dir_root_4/some_dir/file", "") + .file("dir_root_5/some_dir/file", "") + // Dir in sub-dir. + .file("some_dir/dir_deep_1/some_dir/file", "") + .file("some_dir/dir_deep_2/some_dir/file", "") + .file("some_dir/dir_deep_3/some_dir/file", "") + .file("some_dir/dir_deep_4/some_dir/file", "") + .file("some_dir/dir_deep_5/some_dir/file", "") + .build(); + + cargo_process("package --no-verify -v") + .cwd(repo.root()) + .with_stdout("") + .with_stderr( + "\ +[WARNING] manifest has no description[..] +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] foo v0.0.1 ([..]) +[ARCHIVING] .cargo_vcs_info.json +[ARCHIVING] Cargo.lock +[ARCHIVING] Cargo.toml +[ARCHIVING] Cargo.toml.orig +[ARCHIVING] file_root_3 +[ARCHIVING] file_root_4 +[ARCHIVING] file_root_5 +[ARCHIVING] some_dir/dir_deep_2/some_dir/file +[ARCHIVING] some_dir/dir_deep_4/some_dir/file +[ARCHIVING] some_dir/dir_deep_5/some_dir/file +[ARCHIVING] some_dir/file_deep_2 +[ARCHIVING] some_dir/file_deep_3 +[ARCHIVING] some_dir/file_deep_4 +[ARCHIVING] some_dir/file_deep_5 +[ARCHIVING] src/main.rs +[PACKAGED] 15 files, [..] ([..] compressed) +", + ) + .run(); + + assert!(repo.root().join("target/package/foo-0.0.1.crate").is_file()); + + cargo_process("package -l") + .cwd(repo.root()) + .with_stdout( + "\ +.cargo_vcs_info.json +Cargo.lock +Cargo.toml +Cargo.toml.orig +file_root_3 +file_root_4 +file_root_5 +some_dir/dir_deep_2/some_dir/file +some_dir/dir_deep_4/some_dir/file +some_dir/dir_deep_5/some_dir/file +some_dir/file_deep_2 +some_dir/file_deep_3 +some_dir/file_deep_4 +some_dir/file_deep_5 +src/main.rs +", + ) + .run(); +} + +#[cargo_test] +fn include() { + let root = paths::root().join("include"); + let repo = git::repo(&root) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + exclude = ["*.txt"] + include = ["foo.txt", "**/*.rs", "Cargo.toml", ".dotfile"] + "#, + ) + .file("foo.txt", "") + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file(".dotfile", "") + // Should be ignored when packaging. + .file("src/bar.txt", "") + .build(); + + cargo_process("package --no-verify -v") + .cwd(repo.root()) + .with_stderr( + "\ +[WARNING] manifest has no description[..] +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[WARNING] both package.include and package.exclude are specified; the exclude list will be ignored +[PACKAGING] foo v0.0.1 ([..]) +[ARCHIVING] .cargo_vcs_info.json +[ARCHIVING] .dotfile +[ARCHIVING] Cargo.lock +[ARCHIVING] Cargo.toml +[ARCHIVING] Cargo.toml.orig +[ARCHIVING] foo.txt +[ARCHIVING] src/main.rs +[PACKAGED] 7 files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn package_lib_with_bin() { + let p = project() + .file("src/main.rs", "extern crate foo; fn main() {}") + .file("src/lib.rs", "") + .build(); + + p.cargo("package -v").run(); +} + +#[cargo_test] +fn package_git_submodule() { + let project = git::new("foo", |project| { + project + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = ["foo@example.com"] + license = "MIT" + description = "foo" + repository = "foo" + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + }); + let library = git::new("bar", |library| { + library.no_manifest().file("Makefile", "all:") + }); + + let repository = git2::Repository::open(&project.root()).unwrap(); + let url = path2url(library.root()).to_string(); + git::add_submodule(&repository, &url, Path::new("bar")); + git::commit(&repository); + + let repository = git2::Repository::open(&project.root().join("bar")).unwrap(); + repository + .reset( + &repository.revparse_single("HEAD").unwrap(), + git2::ResetType::Hard, + None, + ) + .unwrap(); + + project + .cargo("package --no-verify -v") + .with_stderr_contains("[ARCHIVING] bar/Makefile") + .run(); +} + +#[cargo_test] +/// Tests if a symlink to a git submodule is properly handled. +/// +/// This test requires you to be able to make symlinks. +/// For windows, this may require you to enable developer mode. +fn package_symlink_to_submodule() { + #[cfg(unix)] + use std::os::unix::fs::symlink; + #[cfg(windows)] + use std::os::windows::fs::symlink_dir as symlink; + + if !symlink_supported() { + return; + } + + let project = git::new("foo", |project| { + project.file("src/lib.rs", "pub fn foo() {}") + }); + + let library = git::new("submodule", |library| { + library.no_manifest().file("Makefile", "all:") + }); + + let repository = git2::Repository::open(&project.root()).unwrap(); + let url = path2url(library.root()).to_string(); + git::add_submodule(&repository, &url, Path::new("submodule")); + t!(symlink( + &project.root().join("submodule"), + &project.root().join("submodule-link") + )); + git::add(&repository); + git::commit(&repository); + + let repository = git2::Repository::open(&project.root().join("submodule")).unwrap(); + repository + .reset( + &repository.revparse_single("HEAD").unwrap(), + git2::ResetType::Hard, + None, + ) + .unwrap(); + + project + .cargo("package --no-verify -v") + .with_stderr_contains("[ARCHIVING] submodule/Makefile") + .run(); +} + +#[cargo_test] +fn no_duplicates_from_modified_tracked_files() { + let p = git::new("all", |p| p.file("src/main.rs", "fn main() {}")); + p.change_file("src/main.rs", r#"fn main() { println!("A change!"); }"#); + p.cargo("build").run(); + p.cargo("package --list --allow-dirty") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); +} + +#[cargo_test] +fn ignore_nested() { + let cargo_toml = r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#; + let main_rs = r#" + fn main() { println!("hello"); } + "#; + let p = project() + .file("Cargo.toml", cargo_toml) + .file("src/main.rs", main_rs) + // If a project happens to contain a copy of itself, we should + // ignore it. + .file("a_dir/foo/Cargo.toml", cargo_toml) + .file("a_dir/foo/src/main.rs", main_rs) + .build(); + + p.cargo("package") + .with_stderr( + "\ +[WARNING] manifest has no documentation[..] +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 4 files, [..] ([..] compressed) +", + ) + .run(); + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + p.cargo("package -l") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + p.cargo("package").with_stdout("").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + &[], + ); +} + +// Windows doesn't allow these characters in filenames. +#[cfg(unix)] +#[cargo_test] +fn package_weird_characters() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file("src/:foo", "") + .build(); + + p.cargo("package") + .with_status(101) + .with_stderr( + "\ +warning: [..] +See [..] +[ERROR] cannot package a filename with a special character `:`: src/:foo +", + ) + .run(); +} + +#[cargo_test] +fn repackage_on_source_change() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("package").run(); + + // Add another source file + p.change_file("src/foo.rs", r#"fn main() { println!("foo"); }"#); + + // Check that cargo rebuilds the tarball + p.cargo("package") + .with_stderr( + "\ +[WARNING] [..] +See [..] +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 5 files, [..] ([..] compressed) +", + ) + .run(); + + // Check that the tarball contains the added file + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "src/main.rs", + "src/foo.rs", + ], + &[], + ); +} + +#[cargo_test] +/// Tests if a broken symlink is properly handled when packaging. +/// +/// This test requires you to be able to make symlinks. +/// For windows, this may require you to enable developer mode. +fn broken_symlink() { + #[cfg(unix)] + use std::os::unix::fs::symlink; + #[cfg(windows)] + use std::os::windows::fs::symlink_dir as symlink; + + if !symlink_supported() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = 'foo' + documentation = 'foo' + homepage = 'foo' + repository = 'foo' + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + t!(symlink("nowhere", &p.root().join("src/foo.rs"))); + + p.cargo("package -v") + .with_status(101) + .with_stderr_contains( + "\ +[ERROR] failed to prepare local package for uploading + +Caused by: + failed to open for archiving: `[..]foo.rs` + +Caused by: + [..] +", + ) + .run(); +} + +#[cargo_test] +/// Tests if a broken but excluded symlink is ignored. +/// See issue rust-lang/cargo#10917 +/// +/// This test requires you to be able to make symlinks. +/// For windows, this may require you to enable developer mode. +fn broken_but_excluded_symlink() { + #[cfg(unix)] + use std::os::unix::fs::symlink; + #[cfg(windows)] + use std::os::windows::fs::symlink_dir as symlink; + + if !symlink_supported() { + return; + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = 'foo' + documentation = 'foo' + homepage = 'foo' + repository = 'foo' + exclude = ["src/foo.rs"] + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + t!(symlink("nowhere", &p.root().join("src/foo.rs"))); + + p.cargo("package -v --list") + // `src/foo.rs` is excluded. + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); +} + +#[cargo_test] +#[cfg(not(windows))] // https://github.com/libgit2/libgit2/issues/6250 +/// Test that /dir and /dir/ matches symlinks to directories. +fn gitignore_symlink_dir() { + if !symlink_supported() { + return; + } + + let (p, _repo) = git::new_repo("foo", |p| { + p.file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .symlink_dir("src", "src1") + .symlink_dir("src", "src2") + .symlink_dir("src", "src3") + .symlink_dir("src", "src4") + .file(".gitignore", "/src1\n/src2/\nsrc3\nsrc4/") + }); + + p.cargo("package -l --no-metadata") + .with_stderr("") + .with_stdout( + "\ +.cargo_vcs_info.json +.gitignore +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); +} + +#[cargo_test] +#[cfg(not(windows))] // https://github.com/libgit2/libgit2/issues/6250 +/// Test that /dir and /dir/ matches symlinks to directories in dirty working directory. +fn gitignore_symlink_dir_dirty() { + if !symlink_supported() { + return; + } + + let (p, _repo) = git::new_repo("foo", |p| { + p.file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file(".gitignore", "/src1\n/src2/\nsrc3\nsrc4/") + }); + + p.symlink("src", "src1"); + p.symlink("src", "src2"); + p.symlink("src", "src3"); + p.symlink("src", "src4"); + + p.cargo("package -l --no-metadata") + .with_stderr("") + .with_stdout( + "\ +.cargo_vcs_info.json +.gitignore +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + + p.cargo("package -l --no-metadata --allow-dirty") + .with_stderr("") + .with_stdout( + "\ +.gitignore +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); +} + +#[cargo_test] +/// Tests if a symlink to a directory is properly included. +/// +/// This test requires you to be able to make symlinks. +/// For windows, this may require you to enable developer mode. +fn package_symlink_to_dir() { + if !symlink_supported() { + return; + } + + project() + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file("bla/Makefile", "all:") + .symlink_dir("bla", "foo") + .build() + .cargo("package -v") + .with_stderr_contains("[ARCHIVING] foo/Makefile") + .run(); +} + +#[cargo_test] +/// Tests if a symlink to ancestor causes filesystem loop error. +/// +/// This test requires you to be able to make symlinks. +/// For windows, this may require you to enable developer mode. +fn filesystem_loop() { + if !symlink_supported() { + return; + } + + project() + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .symlink_dir("a/b", "a/b/c/d/foo") + .build() + .cargo("package -v") + .with_stderr_contains( + "[WARNING] File system loop found: [..]/a/b/c/d/foo points to an ancestor [..]/a/b", + ) + .run(); +} + +#[cargo_test] +fn do_not_package_if_repository_is_dirty() { + let p = project().build(); + + // Create a Git repository containing a minimal Rust project. + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + // Modify Cargo.toml without committing the change. + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + # change + "#, + ); + + p.cargo("package") + .with_status(101) + .with_stderr( + "\ +error: 1 files in the working directory contain changes that were not yet \ +committed into git: + +Cargo.toml + +to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag +", + ) + .run(); +} + +#[cargo_test] +fn dirty_ignored() { + // Cargo warns about an ignored file that will be published. + let (p, repo) = git::new_repo("foo", |p| { + p.file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + description = "foo" + license = "foo" + documentation = "foo" + include = ["src", "build"] + "#, + ) + .file("src/lib.rs", "") + .file(".gitignore", "build") + }); + // Example of adding a file that is confusingly ignored by an overzealous + // gitignore rule. + p.change_file("src/build/mod.rs", ""); + p.cargo("package --list") + .with_status(101) + .with_stderr( + "\ +error: 1 files in the working directory contain changes that were not yet committed into git: + +src/build/mod.rs + +to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag +", + ) + .run(); + // Add the ignored file and make sure it is included. + let mut index = t!(repo.index()); + t!(index.add_path(Path::new("src/build/mod.rs"))); + t!(index.write()); + git::commit(&repo); + p.cargo("package --list") + .with_stderr("") + .with_stdout( + "\ +.cargo_vcs_info.json +Cargo.toml +Cargo.toml.orig +src/build/mod.rs +src/lib.rs +", + ) + .run(); +} + +#[cargo_test] +fn generated_manifest() { + let registry = registry::alt_init(); + Package::new("abc", "1.0.0").publish(); + Package::new("def", "1.0.0").alternative(true).publish(); + Package::new("ghi", "1.0.0").publish(); + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + exclude = ["*.txt"] + license = "MIT" + description = "foo" + + [package.metadata] + foo = 'bar' + + [workspace] + + [dependencies] + bar = { path = "bar", version = "0.1" } + def = { version = "1.0", registry = "alternative" } + ghi = "1.0" + abc = "1.0" + "#, + ) + .file("src/main.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("package --no-verify").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + let rewritten_toml = format!( + r#"{} +[package] +name = "foo" +version = "0.0.1" +authors = [] +exclude = ["*.txt"] +description = "foo" +license = "MIT" + +[package.metadata] +foo = "bar" + +[dependencies.abc] +version = "1.0" + +[dependencies.bar] +version = "0.1" + +[dependencies.def] +version = "1.0" +registry-index = "{}" + +[dependencies.ghi] +version = "1.0" +"#, + cargo::core::package::MANIFEST_PREAMBLE, + registry.index_url() + ); + + validate_crate_contents( + f, + "foo-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + &[("Cargo.toml", &rewritten_toml)], + ); +} + +#[cargo_test] +fn ignore_workspace_specifier() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + authors = [] + + [workspace] + + [dependencies] + bar = { path = "bar", version = "0.1" } + "#, + ) + .file("src/main.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = ".." + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("package --no-verify").cwd("bar").run(); + + let f = File::open(&p.root().join("target/package/bar-0.1.0.crate")).unwrap(); + let rewritten_toml = format!( + r#"{} +[package] +name = "bar" +version = "0.1.0" +authors = [] +"#, + cargo::core::package::MANIFEST_PREAMBLE + ); + validate_crate_contents( + f, + "bar-0.1.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[("Cargo.toml", &rewritten_toml)], + ); +} + +#[cargo_test] +fn package_two_kinds_of_deps() { + Package::new("other", "1.0.0").publish(); + Package::new("other1", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + other = "1.0" + other1 = { version = "1.0" } + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("package --no-verify").run(); +} + +#[cargo_test] +fn test_edition() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["edition"] + [package] + name = "foo" + version = "0.0.1" + authors = [] + edition = "2018" + "#, + ) + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("check -v") + .with_stderr_contains( + "\ +[CHECKING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..]--edition=2018 [..] +", + ) + .run(); +} + +#[cargo_test] +fn edition_with_metadata() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + edition = "2018" + + [package.metadata.docs.rs] + features = ["foobar"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("package").run(); +} + +#[cargo_test] +fn test_edition_malformed() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + edition = "chicken" + "#, + ) + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + failed to parse the `edition` key + +Caused by: + supported edition values are `2015`, `2018`, or `2021`, but `chicken` is unknown +" + .to_string(), + ) + .run(); +} + +#[cargo_test] +fn test_edition_from_the_future() { + let p = project() + .file( + "Cargo.toml", + r#"[package] + edition = "2038" + name = "foo" + version = "99.99.99" + authors = [] + "#, + ) + .file("src/main.rs", r#""#) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + failed to parse the `edition` key + +Caused by: + this version of Cargo is older than the `2038` edition, and only supports `2015`, `2018`, and `2021` editions. +" + .to_string(), + ) + .run(); +} + +#[cargo_test] +fn do_not_package_if_src_was_modified() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file("dir/foo.txt", "") + .file("bar.txt", "") + .file( + "build.rs", + r#" + use std::fs; + + fn main() { + fs::write("src/generated.txt", + "Hello, world of generated files." + ).expect("failed to create file"); + fs::remove_file("dir/foo.txt").expect("failed to remove file"); + fs::remove_dir("dir").expect("failed to remove dir"); + fs::write("bar.txt", "updated content").expect("failed to update"); + fs::create_dir("new-dir").expect("failed to create dir"); + } + "#, + ) + .build(); + + p.cargo("package") + .with_status(101) + .with_stderr_contains( + "\ +error: failed to verify package tarball + +Caused by: + Source directory was modified by build.rs during cargo publish. \ + Build scripts should not modify anything outside of OUT_DIR. + Changed: [CWD]/target/package/foo-0.0.1/bar.txt + Added: [CWD]/target/package/foo-0.0.1/new-dir + [CWD]/target/package/foo-0.0.1/src/generated.txt + Removed: [CWD]/target/package/foo-0.0.1/dir + [CWD]/target/package/foo-0.0.1/dir/foo.txt + + To proceed despite this, pass the `--no-verify` flag.", + ) + .run(); + + p.cargo("package --no-verify").run(); +} + +#[cargo_test] +fn package_with_select_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [features] + required = [] + optional = [] + "#, + ) + .file( + "src/main.rs", + "#[cfg(not(feature = \"required\"))] + compile_error!(\"This crate requires `required` feature!\"); + fn main() {}", + ) + .build(); + + p.cargo("package --features required").run(); +} + +#[cargo_test] +fn package_with_all_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [features] + required = [] + optional = [] + "#, + ) + .file( + "src/main.rs", + "#[cfg(not(feature = \"required\"))] + compile_error!(\"This crate requires `required` feature!\"); + fn main() {}", + ) + .build(); + + p.cargo("package --all-features").run(); +} + +#[cargo_test] +fn package_no_default_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [features] + default = ["required"] + required = [] + "#, + ) + .file( + "src/main.rs", + "#[cfg(not(feature = \"required\"))] + compile_error!(\"This crate requires `required` feature!\"); + fn main() {}", + ) + .build(); + + p.cargo("package --no-default-features") + .with_stderr_contains("error: This crate requires `required` feature!") + .with_status(101) + .run(); +} + +#[cargo_test] +fn include_cargo_toml_implicit() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + include = ["src/lib.rs"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("package --list") + .with_stdout("Cargo.toml\nCargo.toml.orig\nsrc/lib.rs\n") + .run(); +} + +fn include_exclude_test(include: &str, exclude: &str, files: &[&str], expected: &str) { + let mut pb = project().file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + include = {} + exclude = {} + "#, + include, exclude + ), + ); + for file in files { + pb = pb.file(file, ""); + } + let p = pb.build(); + + p.cargo("package --list") + .with_stderr("") + .with_stdout(expected) + .run(); + p.root().rm_rf(); +} + +#[cargo_test] +fn package_include_ignore_only() { + // Test with a gitignore pattern that fails to parse with glob. + // This is a somewhat nonsense pattern, but is an example of something git + // allows and glob does not. + assert!(glob::Pattern::new("src/abc**").is_err()); + + include_exclude_test( + r#"["Cargo.toml", "src/abc**", "src/lib.rs"]"#, + "[]", + &["src/lib.rs", "src/abc1.rs", "src/abc2.rs", "src/abc/mod.rs"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + src/abc/mod.rs\n\ + src/abc1.rs\n\ + src/abc2.rs\n\ + src/lib.rs\n\ + ", + ) +} + +#[cargo_test] +fn gitignore_patterns() { + include_exclude_test( + r#"["Cargo.toml", "foo"]"#, // include + "[]", + &["src/lib.rs", "foo", "a/foo", "a/b/foo", "x/foo/y", "bar"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + a/b/foo\n\ + a/foo\n\ + foo\n\ + x/foo/y\n\ + ", + ); + + include_exclude_test( + r#"["Cargo.toml", "/foo"]"#, // include + "[]", + &["src/lib.rs", "foo", "a/foo", "a/b/foo", "x/foo/y", "bar"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + foo\n\ + ", + ); + + include_exclude_test( + "[]", + r#"["foo/"]"#, // exclude + &["src/lib.rs", "foo", "a/foo", "x/foo/y", "bar"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + a/foo\n\ + bar\n\ + foo\n\ + src/lib.rs\n\ + ", + ); + + include_exclude_test( + "[]", + r#"["*.txt", "[ab]", "[x-z]"]"#, // exclude + &[ + "src/lib.rs", + "foo.txt", + "bar/foo.txt", + "other", + "a", + "b", + "c", + "x", + "y", + "z", + ], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + c\n\ + other\n\ + src/lib.rs\n\ + ", + ); + + include_exclude_test( + r#"["Cargo.toml", "**/foo/bar"]"#, // include + "[]", + &["src/lib.rs", "a/foo/bar", "foo", "bar"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + a/foo/bar\n\ + ", + ); + + include_exclude_test( + r#"["Cargo.toml", "foo/**"]"#, // include + "[]", + &["src/lib.rs", "a/foo/bar", "foo/x/y/z"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + foo/x/y/z\n\ + ", + ); + + include_exclude_test( + r#"["Cargo.toml", "a/**/b"]"#, // include + "[]", + &["src/lib.rs", "a/b", "a/x/b", "a/x/y/b"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + a/b\n\ + a/x/b\n\ + a/x/y/b\n\ + ", + ); +} + +#[cargo_test] +fn gitignore_negate() { + include_exclude_test( + r#"["Cargo.toml", "*.rs", "!foo.rs", "\\!important"]"#, // include + "[]", + &["src/lib.rs", "foo.rs", "!important"], + "!important\n\ + Cargo.toml\n\ + Cargo.toml.orig\n\ + src/lib.rs\n\ + ", + ); + + // NOTE: This is unusual compared to git. Git treats `src/` as a + // short-circuit which means rules like `!src/foo.rs` would never run. + // However, because Cargo only works by iterating over *files*, it doesn't + // short-circuit. + include_exclude_test( + r#"["Cargo.toml", "src/", "!src/foo.rs"]"#, // include + "[]", + &["src/lib.rs", "src/foo.rs"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + src/lib.rs\n\ + ", + ); + + include_exclude_test( + r#"["Cargo.toml", "src/*.rs", "!foo.rs"]"#, // include + "[]", + &["src/lib.rs", "foo.rs", "src/foo.rs", "src/bar/foo.rs"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + src/lib.rs\n\ + ", + ); + + include_exclude_test( + "[]", + r#"["*.rs", "!foo.rs", "\\!important"]"#, // exclude + &["src/lib.rs", "foo.rs", "!important"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + foo.rs\n\ + ", + ); +} + +#[cargo_test] +fn exclude_dot_files_and_directories_by_default() { + include_exclude_test( + "[]", + "[]", + &["src/lib.rs", ".dotfile", ".dotdir/file"], + "Cargo.toml\n\ + Cargo.toml.orig\n\ + src/lib.rs\n\ + ", + ); + + include_exclude_test( + r#"["Cargo.toml", "src/lib.rs", ".dotfile", ".dotdir/file"]"#, + "[]", + &["src/lib.rs", ".dotfile", ".dotdir/file"], + ".dotdir/file\n\ + .dotfile\n\ + Cargo.toml\n\ + Cargo.toml.orig\n\ + src/lib.rs\n\ + ", + ); +} + +#[cargo_test] +fn invalid_license_file_path() { + // Test warning when license-file points to a non-existent file. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + license-file = "does-not-exist" + description = "foo" + homepage = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("package --no-verify") + .with_stderr( + "\ +[WARNING] license-file `does-not-exist` does not appear to exist (relative to `[..]/foo`). +Please update the license-file setting in the manifest at `[..]/foo/Cargo.toml` +This may become a hard error in the future. +[PACKAGING] foo v1.0.0 ([..]/foo) +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn license_file_implicit_include() { + // license-file should be automatically included even if not listed. + let p = git::new("foo", |p| { + p.file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + license-file = "subdir/LICENSE" + description = "foo" + homepage = "foo" + include = ["src"] + "#, + ) + .file("src/lib.rs", "") + .file("subdir/LICENSE", "license text") + }); + + p.cargo("package --list") + .with_stdout( + "\ +.cargo_vcs_info.json +Cargo.toml +Cargo.toml.orig +src/lib.rs +subdir/LICENSE +", + ) + .with_stderr("") + .run(); + + p.cargo("package --no-verify -v") + .with_stderr( + "\ +[PACKAGING] foo v1.0.0 [..] +[ARCHIVING] .cargo_vcs_info.json +[ARCHIVING] Cargo.toml +[ARCHIVING] Cargo.toml.orig +[ARCHIVING] src/lib.rs +[ARCHIVING] subdir/LICENSE +[PACKAGED] 5 files, [..] ([..] compressed) +", + ) + .run(); + let f = File::open(&p.root().join("target/package/foo-1.0.0.crate")).unwrap(); + validate_crate_contents( + f, + "foo-1.0.0.crate", + &[ + ".cargo_vcs_info.json", + "Cargo.toml", + "Cargo.toml.orig", + "subdir/LICENSE", + "src/lib.rs", + ], + &[("subdir/LICENSE", "license text")], + ); +} + +#[cargo_test] +fn relative_license_included() { + // license-file path outside of package will copy into root. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + license-file = "../LICENSE" + description = "foo" + homepage = "foo" + "#, + ) + .file("src/lib.rs", "") + .file("../LICENSE", "license text") + .build(); + + p.cargo("package --list") + .with_stdout( + "\ +Cargo.toml +Cargo.toml.orig +LICENSE +src/lib.rs +", + ) + .with_stderr("") + .run(); + + p.cargo("package") + .with_stderr( + "\ +[PACKAGING] foo v1.0.0 [..] +[VERIFYING] foo v1.0.0 [..] +[COMPILING] foo v1.0.0 [..] +[FINISHED] [..] +[PACKAGED] 4 files, [..] ([..] compressed) +", + ) + .run(); + let f = File::open(&p.root().join("target/package/foo-1.0.0.crate")).unwrap(); + validate_crate_contents( + f, + "foo-1.0.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "LICENSE", "src/lib.rs"], + &[("LICENSE", "license text")], + ); + let manifest = + std::fs::read_to_string(p.root().join("target/package/foo-1.0.0/Cargo.toml")).unwrap(); + assert!(manifest.contains("license-file = \"LICENSE\"")); + let orig = + std::fs::read_to_string(p.root().join("target/package/foo-1.0.0/Cargo.toml.orig")).unwrap(); + assert!(orig.contains("license-file = \"../LICENSE\"")); +} + +#[cargo_test] +fn relative_license_include_collision() { + // Can't copy a relative license-file if there is a file with that name already. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + license-file = "../LICENSE" + description = "foo" + homepage = "foo" + "#, + ) + .file("src/lib.rs", "") + .file("../LICENSE", "outer license") + .file("LICENSE", "inner license") + .build(); + + p.cargo("package --list") + .with_stdout( + "\ +Cargo.toml +Cargo.toml.orig +LICENSE +src/lib.rs +", + ) + .with_stderr("[WARNING] license-file `../LICENSE` appears to be [..]") + .run(); + + p.cargo("package") + .with_stderr( + "\ +[WARNING] license-file `../LICENSE` appears to be [..] +[PACKAGING] foo v1.0.0 [..] +[VERIFYING] foo v1.0.0 [..] +[COMPILING] foo v1.0.0 [..] +[FINISHED] [..] +[PACKAGED] 4 files, [..] ([..] compressed) +", + ) + .run(); + let f = File::open(&p.root().join("target/package/foo-1.0.0.crate")).unwrap(); + validate_crate_contents( + f, + "foo-1.0.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "LICENSE", "src/lib.rs"], + &[("LICENSE", "inner license")], + ); + let manifest = read_to_string(p.root().join("target/package/foo-1.0.0/Cargo.toml")).unwrap(); + assert!(manifest.contains("license-file = \"LICENSE\"")); + let orig = read_to_string(p.root().join("target/package/foo-1.0.0/Cargo.toml.orig")).unwrap(); + assert!(orig.contains("license-file = \"../LICENSE\"")); +} + +#[cargo_test] +#[cfg(not(windows))] // Don't want to create invalid files on Windows. +fn package_restricted_windows() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + license = "MIT" + description = "foo" + homepage = "foo" + "#, + ) + .file("src/lib.rs", "pub mod con;\npub mod aux;") + .file("src/con.rs", "pub fn f() {}") + .file("src/aux/mod.rs", "pub fn f() {}") + .build(); + + p.cargo("package") + // use unordered here because the order of the warning is different on each platform. + .with_stderr_unordered( + "\ +[WARNING] file src/aux/mod.rs is a reserved Windows filename, it will not work on Windows platforms +[WARNING] file src/con.rs is a reserved Windows filename, it will not work on Windows platforms +[PACKAGING] foo [..] +[VERIFYING] foo [..] +[COMPILING] foo [..] +[PACKAGED] [..] files, [..] ([..] compressed) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn finds_git_in_parent() { + // Test where `Cargo.toml` is not in the root of the git repo. + let repo_path = paths::root().join("repo"); + fs::create_dir(&repo_path).unwrap(); + let p = project() + .at("repo/foo") + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/lib.rs", "") + .build(); + let repo = git::init(&repo_path); + git::add(&repo); + git::commit(&repo); + p.change_file("ignoreme", ""); + p.change_file("ignoreme2", ""); + p.cargo("package --list --allow-dirty") + .with_stdout( + "\ +Cargo.toml +Cargo.toml.orig +ignoreme +ignoreme2 +src/lib.rs +", + ) + .run(); + + p.change_file(".gitignore", "ignoreme"); + p.cargo("package --list --allow-dirty") + .with_stdout( + "\ +.gitignore +Cargo.toml +Cargo.toml.orig +ignoreme2 +src/lib.rs +", + ) + .run(); + + fs::write(repo_path.join(".gitignore"), "ignoreme2").unwrap(); + p.cargo("package --list --allow-dirty") + .with_stdout( + "\ +.gitignore +Cargo.toml +Cargo.toml.orig +src/lib.rs +", + ) + .run(); +} + +#[cargo_test] +#[cfg(windows)] +fn reserved_windows_name() { + // If we are running on a version of Windows that allows these reserved filenames, + // skip this test. + if paths::windows_reserved_names_are_allowed() { + return; + } + + Package::new("bar", "1.0.0") + .file("src/lib.rs", "pub mod aux;") + .file("src/aux.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [dependencies] + bar = "1.0.0" + "#, + ) + .file("src/main.rs", "extern crate bar;\nfn main() { }") + .build(); + p.cargo("package") + .with_status(101) + .with_stderr_contains( + "\ +error: failed to verify package tarball + +Caused by: + failed to download replaced source registry `[..]` + +Caused by: + failed to unpack package `[..] `[..]`)` + +Caused by: + failed to unpack entry at `[..]aux.rs` + +Caused by: + `[..]aux.rs` appears to contain a reserved Windows path, it cannot be extracted on Windows + +Caused by: + failed to unpack `[..]aux.rs` + +Caused by: + failed to unpack `[..]aux.rs` into `[..]aux.rs`", + ) + .run(); +} + +#[cargo_test] +fn list_with_path_and_lock() { + // Allow --list even for something that isn't packageable. + + // Init an empty registry because a versionless path dep will search for + // the package on crates.io. + registry::init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + license = "MIT" + description = "foo" + homepage = "foo" + + [dependencies] + bar = {path="bar"} + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("package --list") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + + p.cargo("package") + .with_status(101) + .with_stderr( + "\ +[ERROR] all dependencies must have a version specified when packaging. +dependency `bar` does not specify a version +Note: The packaged dependency will use the version from crates.io, +the `path` specification will be removed from the dependency declaration. +", + ) + .run(); +} + +#[cargo_test] +fn long_file_names() { + // Filenames over 100 characters require a GNU extension tarfile. + // See #8453. + + registry::init(); + let long_name = concat!( + "012345678901234567890123456789012345678901234567890123456789", + "012345678901234567890123456789012345678901234567890123456789", + "012345678901234567890123456789012345678901234567890123456789" + ); + if cfg!(windows) { + // Long paths on Windows require a special registry entry that is + // disabled by default (even on Windows 10). + // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + // If the directory where Cargo runs happens to be more than 80 characters + // long, then it will bump into this limit. + // + // First create a directory to account for various paths Cargo will + // be using in the target directory (such as "target/package/foo-0.1.0"). + let test_path = paths::root().join("test-dir-probe-long-path-support"); + test_path.mkdir_p(); + let test_path = test_path.join(long_name); + if let Err(e) = File::create(&test_path) { + // write to stderr directly to avoid output from being captured + // and always display text, even without --nocapture + use std::io::Write; + writeln!( + std::io::stderr(), + "\nSkipping long_file_names test, this OS or filesystem does not \ + appear to support long file paths: {:?}\n{:?}", + e, + test_path + ) + .unwrap(); + return; + } + } + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + license = "MIT" + description = "foo" + homepage = "foo" + + [dependencies] + "#, + ) + .file(long_name, "something") + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("package").run(); + p.cargo("package --list") + .with_stdout(&format!( + "\ +{} +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + long_name + )) + .run(); +} + +#[cargo_test] +fn reproducible_output() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + exclude = ["*.txt"] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("package").run(); + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + let decoder = GzDecoder::new(f); + let mut archive = Archive::new(decoder); + for ent in archive.entries().unwrap() { + let ent = ent.unwrap(); + println!("checking {:?}", ent.path()); + let header = ent.header(); + assert_eq!(header.mode().unwrap(), 0o644); + assert!(header.mtime().unwrap() != 0); + assert_eq!(header.username().unwrap().unwrap(), ""); + assert_eq!(header.groupname().unwrap().unwrap(), ""); + } +} + +#[cargo_test] +fn package_with_resolver_and_metadata() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + resolver = '2' + + [package.metadata.docs.rs] + all-features = true + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("package").run(); +} + +#[cargo_test] +fn deleted_git_working_tree() { + // When deleting a file, but not staged, cargo should ignore the file. + let (p, repo) = git::new_repo("foo", |p| { + p.file("src/lib.rs", "").file("src/main.rs", "fn main() {}") + }); + p.root().join("src/lib.rs").rm_rf(); + p.cargo("package --allow-dirty --list") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + p.cargo("package --allow-dirty").run(); + let mut index = t!(repo.index()); + t!(index.remove(Path::new("src/lib.rs"), 0)); + t!(index.write()); + p.cargo("package --allow-dirty --list") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + p.cargo("package --allow-dirty").run(); +} + +#[cargo_test] +fn in_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + license = "MIT" + description = "bar" + workspace = ".." + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("package --workspace") + .with_stderr( + "\ +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] bar v0.0.1 ([CWD]/bar) +[VERIFYING] bar v0.0.1 ([CWD]/bar) +[COMPILING] bar v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); + + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + assert!(p.root().join("target/package/bar-0.0.1.crate").is_file()); +} + +#[cargo_test] +fn workspace_overrides_resolver() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + edition = "2021" + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + edition = "2015" + "#, + ) + .file("baz/src/lib.rs", "") + .build(); + + p.cargo("package --no-verify -p bar -p baz").run(); + + let f = File::open(&p.root().join("target/package/bar-0.1.0.crate")).unwrap(); + let rewritten_toml = format!( + r#"{} +[package] +edition = "2021" +name = "bar" +version = "0.1.0" +resolver = "1" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ); + validate_crate_contents( + f, + "bar-0.1.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[("Cargo.toml", &rewritten_toml)], + ); + + // When the crate has the same implicit resolver as the workspace it is not overridden + let f = File::open(&p.root().join("target/package/baz-0.1.0.crate")).unwrap(); + let rewritten_toml = format!( + r#"{} +[package] +edition = "2015" +name = "baz" +version = "0.1.0" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ); + validate_crate_contents( + f, + "baz-0.1.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[("Cargo.toml", &rewritten_toml)], + ); +} + +fn verify_packaged_status_line( + output: std::process::Output, + num_files: usize, + uncompressed_size: u64, + compressed_size: u64, +) { + use cargo::util::human_readable_bytes; + + let stderr = String::from_utf8(output.stderr).unwrap(); + let mut packaged_lines = stderr + .lines() + .filter(|line| line.trim().starts_with("Packaged")); + let packaged_line = packaged_lines + .next() + .expect("`Packaged` status line should appear in stderr"); + assert!( + packaged_lines.next().is_none(), + "Only one `Packaged` status line should appear in stderr" + ); + let size_info = packaged_line.trim().trim_start_matches("Packaged").trim(); + let uncompressed = human_readable_bytes(uncompressed_size); + let compressed = human_readable_bytes(compressed_size); + let expected = format!( + "{} files, {:.1}{} ({:.1}{} compressed)", + num_files, uncompressed.0, uncompressed.1, compressed.0, compressed.1 + ); + assert_eq!(size_info, expected); +} + +#[cargo_test] +fn basic_filesizes() { + let cargo_toml_orig_contents = r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + exclude = ["*.txt"] + license = "MIT" + description = "foo" + "#; + let main_rs_contents = r#"fn main() { println!("🦀"); }"#; + let cargo_toml_contents = format!( + r#"{} +[package] +name = "foo" +version = "0.0.1" +authors = [] +exclude = ["*.txt"] +description = "foo" +license = "MIT" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ); + let cargo_lock_contents = r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "foo" +version = "0.0.1" +"#; + let p = project() + .file("Cargo.toml", cargo_toml_orig_contents) + .file("src/main.rs", main_rs_contents) + .file("src/bar.txt", "Ignored text file contents") // should be ignored when packaging + .build(); + + let uncompressed_size = (cargo_toml_orig_contents.len() + + main_rs_contents.len() + + cargo_toml_contents.len() + + cargo_lock_contents.len()) as u64; + let output = p.cargo("package").exec_with_output().unwrap(); + + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + p.cargo("package -l") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + p.cargo("package").with_stdout("").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + let compressed_size = f.metadata().unwrap().len(); + verify_packaged_status_line(output, 4, uncompressed_size, compressed_size); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + &[ + ("Cargo.lock", cargo_lock_contents), + ("Cargo.toml", &cargo_toml_contents), + ("Cargo.toml.orig", cargo_toml_orig_contents), + ("src/main.rs", main_rs_contents), + ], + ); +} + +#[cargo_test] +fn larger_filesizes() { + let cargo_toml_orig_contents = r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#; + let lots_of_crabs = std::iter::repeat("🦀").take(1337).collect::(); + let main_rs_contents = format!(r#"fn main() {{ println!("{}"); }}"#, lots_of_crabs); + let bar_txt_contents = "This file is relatively uncompressible, to increase the compressed + package size beyond 1KiB. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + laborum."; + let cargo_toml_contents = format!( + r#"{} +[package] +name = "foo" +version = "0.0.1" +authors = [] +description = "foo" +license = "MIT" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ); + let cargo_lock_contents = r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "foo" +version = "0.0.1" +"#; + let p = project() + .file("Cargo.toml", cargo_toml_orig_contents) + .file("src/main.rs", &main_rs_contents) + .file("src/bar.txt", bar_txt_contents) + .build(); + + let uncompressed_size = (cargo_toml_orig_contents.len() + + main_rs_contents.len() + + cargo_toml_contents.len() + + cargo_lock_contents.len() + + bar_txt_contents.len()) as u64; + + let output = p.cargo("package").exec_with_output().unwrap(); + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + p.cargo("package -l") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/bar.txt +src/main.rs +", + ) + .run(); + p.cargo("package").with_stdout("").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + let compressed_size = f.metadata().unwrap().len(); + verify_packaged_status_line(output, 5, uncompressed_size, compressed_size); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "src/bar.txt", + "src/main.rs", + ], + &[ + ("Cargo.lock", cargo_lock_contents), + ("Cargo.toml", &cargo_toml_contents), + ("Cargo.toml.orig", cargo_toml_orig_contents), + ("src/bar.txt", bar_txt_contents), + ("src/main.rs", &main_rs_contents), + ], + ); +} + +#[cargo_test] +fn symlink_filesizes() { + if !symlink_supported() { + return; + } + + let cargo_toml_orig_contents = r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#; + let lots_of_crabs = std::iter::repeat("🦀").take(1337).collect::(); + let main_rs_contents = format!(r#"fn main() {{ println!("{}"); }}"#, lots_of_crabs); + let bar_txt_contents = "This file is relatively uncompressible, to increase the compressed + package size beyond 1KiB. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + laborum."; + let cargo_toml_contents = format!( + r#"{} +[package] +name = "foo" +version = "0.0.1" +authors = [] +description = "foo" +license = "MIT" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ); + let cargo_lock_contents = r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "foo" +version = "0.0.1" +"#; + + let p = project() + .file("Cargo.toml", cargo_toml_orig_contents) + .file("src/main.rs", &main_rs_contents) + .file("bla/bar.txt", bar_txt_contents) + .symlink("src/main.rs", "src/main.rs.bak") + .symlink_dir("bla", "foo") + .build(); + + let uncompressed_size = (cargo_toml_orig_contents.len() + + main_rs_contents.len() * 2 + + cargo_toml_contents.len() + + cargo_lock_contents.len() + + bar_txt_contents.len() * 2) as u64; + + let output = p.cargo("package").exec_with_output().unwrap(); + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + p.cargo("package -l") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +bla/bar.txt +foo/bar.txt +src/main.rs +src/main.rs.bak +", + ) + .run(); + p.cargo("package").with_stdout("").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + let compressed_size = f.metadata().unwrap().len(); + verify_packaged_status_line(output, 7, uncompressed_size, compressed_size); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "bla/bar.txt", + "foo/bar.txt", + "src/main.rs", + "src/main.rs.bak", + ], + &[ + ("Cargo.lock", cargo_lock_contents), + ("Cargo.toml", &cargo_toml_contents), + ("Cargo.toml.orig", cargo_toml_orig_contents), + ("bla/bar.txt", bar_txt_contents), + ("foo/bar.txt", bar_txt_contents), + ("src/main.rs", &main_rs_contents), + ("src/main.rs.bak", &main_rs_contents), + ], + ); +} diff --git a/tests/testsuite/package_features.rs b/tests/testsuite/package_features.rs new file mode 100644 index 0000000..15f726b --- /dev/null +++ b/tests/testsuite/package_features.rs @@ -0,0 +1,704 @@ +//! Tests for feature selection on the command-line. + +use super::features2::switch_to_resolver_2; +use cargo_test_support::registry::{Dependency, Package}; +use cargo_test_support::{basic_manifest, project}; +use std::fmt::Write; + +#[cargo_test] +fn virtual_no_default_features() { + // --no-default-features in root of virtual workspace. + Package::new("dep1", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + dep1 = {version = "1.0", optional = true} + + [features] + default = ["dep1"] + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [features] + default = ["f1"] + f1 = [] + "#, + ) + .file( + "b/src/lib.rs", + r#" + #[cfg(feature = "f1")] + compile_error!{"expected f1 off"} + "#, + ) + .build(); + + p.cargo("check --no-default-features") + .with_stderr_unordered( + "\ +[UPDATING] [..] +[CHECKING] a v0.1.0 [..] +[CHECKING] b v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("check --features foo") + .with_status(101) + .with_stderr( + "[ERROR] none of the selected packages contains these features: foo, did you mean: f1?", + ) + .run(); + + p.cargo("check --features a/dep1,b/f1,b/f2,f2") + .with_status(101) + .with_stderr("[ERROR] none of the selected packages contains these features: b/f2, f2, did you mean: f1?") + .run(); + + p.cargo("check --features a/dep,b/f1,b/f2,f2") + .with_status(101) + .with_stderr("[ERROR] none of the selected packages contains these features: a/dep, b/f2, f2, did you mean: a/dep1, f1?") + .run(); + + p.cargo("check --features a/dep,a/dep1") + .with_status(101) + .with_stderr("[ERROR] none of the selected packages contains these features: a/dep, did you mean: b/f1?") + .run(); +} + +#[cargo_test] +fn virtual_typo_member_feature() { + project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + resolver = "2" + + [features] + deny-warnings = [] + "#, + ) + .file("src/lib.rs", "") + .build() + .cargo("check --features a/deny-warning") + .with_status(101) + .with_stderr( + "[ERROR] none of the selected packages contains these features: a/deny-warning, did you mean: a/deny-warnings?", + ) + .run(); +} + +#[cargo_test] +fn virtual_features() { + // --features in root of virtual workspace. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [features] + f1 = [] + "#, + ) + .file( + "a/src/lib.rs", + r#" + #[cfg(not(feature = "f1"))] + compile_error!{"f1 is missing"} + "#, + ) + .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("check --features f1") + .with_stderr_unordered( + "\ +[CHECKING] a [..] +[CHECKING] b [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn virtual_with_specific() { + // -p flags with --features in root of virtual. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [features] + f1 = [] + f2 = [] + "#, + ) + .file( + "a/src/lib.rs", + r#" + #[cfg(not_feature = "f1")] + compile_error!{"f1 is missing"} + #[cfg(not_feature = "f2")] + compile_error!{"f2 is missing"} + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [features] + f2 = [] + f3 = [] + "#, + ) + .file( + "b/src/lib.rs", + r#" + #[cfg(not_feature = "f2")] + compile_error!{"f2 is missing"} + #[cfg(not_feature = "f3")] + compile_error!{"f3 is missing"} + "#, + ) + .build(); + + p.cargo("check -p a -p b --features f1,f2,f3") + .with_stderr_unordered( + "\ +[CHECKING] a [..] +[CHECKING] b [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn other_member_from_current() { + // -p for another member while in the current directory. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path="bar", features=["f3"] } + + [features] + f1 = ["bar/f4"] + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [features] + f1 = [] + f2 = [] + f3 = [] + f4 = [] + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "bar/src/main.rs", + r#" + fn main() { + if cfg!(feature = "f1") { + print!("f1"); + } + if cfg!(feature = "f2") { + print!("f2"); + } + if cfg!(feature = "f3") { + print!("f3"); + } + if cfg!(feature = "f4") { + print!("f4"); + } + println!(); + } + "#, + ) + .build(); + + // Old behavior. + p.cargo("run -p bar --features f1") + .with_stdout("f3f4") + .run(); + + p.cargo("run -p bar --features f1,f2") + .with_status(101) + .with_stderr("[ERROR] Package `foo[..]` does not have the feature `f2`") + .run(); + + p.cargo("run -p bar --features bar/f1") + .with_stdout("f1f3") + .run(); + + // New behavior. + switch_to_resolver_2(&p); + p.cargo("run -p bar --features f1").with_stdout("f1").run(); + + p.cargo("run -p bar --features f1,f2") + .with_stdout("f1f2") + .run(); + + p.cargo("run -p bar --features bar/f1") + .with_stdout("f1") + .run(); +} + +#[cargo_test] +fn feature_default_resolver() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [features] + test = [] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + if cfg!(feature = "test") { + println!("feature set"); + } + } + "#, + ) + .build(); + + p.cargo("check --features testt") + .with_status(101) + .with_stderr("[ERROR] Package `a[..]` does not have the feature `testt`") + .run(); + + p.cargo("run --features test") + .with_status(0) + .with_stdout("feature set") + .run(); + + p.cargo("run --features a/test") + .with_status(101) + .with_stderr("[ERROR] package `a[..]` does not have a dependency named `a`") + .run(); +} + +#[cargo_test] +fn virtual_member_slash() { + // member slash feature syntax + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + b = {path="../b", optional=true} + + [features] + default = ["f1"] + f1 = [] + f2 = [] + "#, + ) + .file( + "a/src/lib.rs", + r#" + #[cfg(feature = "f1")] + compile_error!{"f1 is set"} + + #[cfg(feature = "f2")] + compile_error!{"f2 is set"} + + #[cfg(feature = "b")] + compile_error!{"b is set"} + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [features] + bfeat = [] + "#, + ) + .file( + "b/src/lib.rs", + r#" + #[cfg(feature = "bfeat")] + compile_error!{"bfeat is set"} + "#, + ) + .build(); + + p.cargo("check -p a") + .with_status(101) + .with_stderr_contains("[..]f1 is set[..]") + .with_stderr_does_not_contain("[..]f2 is set[..]") + .with_stderr_does_not_contain("[..]b is set[..]") + .run(); + + p.cargo("check -p a --features a/f1") + .with_status(101) + .with_stderr_contains("[..]f1 is set[..]") + .with_stderr_does_not_contain("[..]f2 is set[..]") + .with_stderr_does_not_contain("[..]b is set[..]") + .run(); + + p.cargo("check -p a --features a/f2") + .with_status(101) + .with_stderr_contains("[..]f1 is set[..]") + .with_stderr_contains("[..]f2 is set[..]") + .with_stderr_does_not_contain("[..]b is set[..]") + .run(); + + p.cargo("check -p a --features b/bfeat") + .with_status(101) + .with_stderr_contains("[..]bfeat is set[..]") + .run(); + + p.cargo("check -p a --no-default-features").run(); + + p.cargo("check -p a --no-default-features --features b") + .with_status(101) + .with_stderr_contains("[..]b is set[..]") + .run(); +} + +#[cargo_test] +fn non_member() { + // -p for a non-member + Package::new("dep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "2" + + [dependencies] + dep = "1.0" + + [features] + f1 = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -p dep --features f1") + .with_status(101) + .with_stderr("[ERROR] cannot specify features for packages outside of workspace") + .run(); + + p.cargo("check -p dep --all-features") + .with_status(101) + .with_stderr("[ERROR] cannot specify features for packages outside of workspace") + .run(); + + p.cargo("check -p dep --no-default-features") + .with_status(101) + .with_stderr("[ERROR] cannot specify features for packages outside of workspace") + .run(); + + p.cargo("check -p dep") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] [..] +[DOWNLOADED] [..] +[CHECKING] dep [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn resolver1_member_features() { + // --features member-name/feature-name with resolver="1" + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["member1", "member2"] + "#, + ) + .file( + "member1/Cargo.toml", + r#" + [package] + name = "member1" + version = "0.1.0" + + [features] + m1-feature = [] + "#, + ) + .file( + "member1/src/main.rs", + r#" + fn main() { + if cfg!(feature = "m1-feature") { + println!("m1-feature set"); + } + } + "#, + ) + .file("member2/Cargo.toml", &basic_manifest("member2", "0.1.0")) + .file("member2/src/lib.rs", "") + .build(); + + p.cargo("run -p member1 --features member1/m1-feature") + .cwd("member2") + .with_stdout("m1-feature set") + .run(); + + p.cargo("check -p member1 --features member1/m2-feature") + .cwd("member2") + .with_status(101) + .with_stderr("[ERROR] Package `member1[..]` does not have the feature `m2-feature`") + .run(); +} + +#[cargo_test] +fn non_member_feature() { + // --features for a non-member + Package::new("jazz", "1.0.0").publish(); + Package::new("bar", "1.0.0") + .add_dep(Dependency::new("jazz", "1.0").optional(true)) + .publish(); + let make_toml = |resolver, optional| { + let mut s = String::new(); + write!( + s, + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "{}" + + [dependencies] + "#, + resolver + ) + .unwrap(); + if optional { + s.push_str(r#"bar = { version = "1.0", optional = true } "#); + } else { + s.push_str(r#"bar = "1.0""#) + } + s.push('\n'); + s + }; + let p = project() + .file("Cargo.toml", &make_toml("1", false)) + .file("src/lib.rs", "") + .build(); + p.cargo("fetch").run(); + ///////////////////////// V1 non-optional + eprintln!("V1 non-optional"); + p.cargo("check -p bar") + .with_stderr( + "\ +[CHECKING] bar v1.0.0 +[FINISHED] [..] +", + ) + .run(); + // TODO: This should not be allowed (future warning?) + p.cargo("check --features bar/jazz") + .with_stderr( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] jazz v1.0.0 [..] +[CHECKING] jazz v1.0.0 +[CHECKING] bar v1.0.0 +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + // TODO: This should not be allowed (future warning?) + p.cargo("check -p bar --features bar/jazz -v") + .with_stderr( + "\ +[FRESH] jazz v1.0.0 +[FRESH] bar v1.0.0 +[FINISHED] [..] +", + ) + .run(); + + ///////////////////////// V1 optional + eprintln!("V1 optional"); + p.change_file("Cargo.toml", &make_toml("1", true)); + + // This error isn't great, but is probably unlikely to be common in + // practice, so I'm not going to put much effort into improving it. + p.cargo("check -p bar") + .with_status(101) + .with_stderr( + "\ +error: package ID specification `bar` did not match any packages + +Did you mean `foo`? +", + ) + .run(); + + p.cargo("check -p bar --features bar -v") + .with_stderr( + "\ +[FRESH] bar v1.0.0 +[FINISHED] [..] +", + ) + .run(); + + // TODO: This should not be allowed (future warning?) + p.cargo("check -p bar --features bar/jazz -v") + .with_stderr( + "\ +[FRESH] jazz v1.0.0 +[FRESH] bar v1.0.0 +[FINISHED] [..] +", + ) + .run(); + + ///////////////////////// V2 non-optional + eprintln!("V2 non-optional"); + p.change_file("Cargo.toml", &make_toml("2", false)); + // TODO: This should not be allowed (future warning?) + p.cargo("check --features bar/jazz -v") + .with_stderr( + "\ +[FRESH] jazz v1.0.0 +[FRESH] bar v1.0.0 +[FRESH] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + p.cargo("check -p bar -v") + .with_stderr( + "\ +[FRESH] bar v1.0.0 +[FINISHED] [..] +", + ) + .run(); + p.cargo("check -p bar --features bar/jazz") + .with_status(101) + .with_stderr("error: cannot specify features for packages outside of workspace") + .run(); + + ///////////////////////// V2 optional + eprintln!("V2 optional"); + p.change_file("Cargo.toml", &make_toml("2", true)); + p.cargo("check -p bar") + .with_status(101) + .with_stderr( + "\ +error: package ID specification `bar` did not match any packages + +Did you mean `foo`? +", + ) + .run(); + // New --features behavior does not look at cwd. + p.cargo("check -p bar --features bar") + .with_status(101) + .with_stderr("error: cannot specify features for packages outside of workspace") + .run(); + p.cargo("check -p bar --features bar/jazz") + .with_status(101) + .with_stderr("error: cannot specify features for packages outside of workspace") + .run(); + p.cargo("check -p bar --features foo/bar") + .with_status(101) + .with_stderr("error: cannot specify features for packages outside of workspace") + .run(); +} diff --git a/tests/testsuite/patch.rs b/tests/testsuite/patch.rs new file mode 100644 index 0000000..dd8b84a --- /dev/null +++ b/tests/testsuite/patch.rs @@ -0,0 +1,2543 @@ +//! Tests for `[patch]` table source replacement. + +use cargo_test_support::git; +use cargo_test_support::paths; +use cargo_test_support::registry::{self, Package}; +use cargo_test_support::{basic_manifest, project}; +use std::fs; + +#[cargo_test] +fn replace() { + Package::new("bar", "0.1.0").publish(); + Package::new("baz", "0.1.0") + .file( + "src/lib.rs", + "extern crate bar; pub fn baz() { bar::bar(); }", + ) + .dep("bar", "0.1.0") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + baz = "0.1.0" + + [patch.crates-io] + bar = { path = "bar" } + "#, + ) + .file( + "src/lib.rs", + " + extern crate bar; + extern crate baz; + pub fn bar() { + bar::bar(); + baz::baz(); + } + ", + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] baz v0.1.0 ([..]) +[CHECKING] bar v0.1.0 ([CWD]/bar) +[CHECKING] baz v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("check").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn from_config() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file( + ".cargo/config.toml", + r#" + [patch.crates-io] + bar = { path = 'bar' } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.1 ([..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn from_config_relative() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file( + "../.cargo/config.toml", + r#" + [patch.crates-io] + bar = { path = 'foo/bar' } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.1 ([..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn from_config_precedence() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [patch.crates-io] + bar = { path = 'no-such-path' } + "#, + ) + .file( + ".cargo/config.toml", + r#" + [patch.crates-io] + bar = { path = 'bar' } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.1 ([..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn nonexistent() { + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [patch.crates-io] + bar = { path = "bar" } + "#, + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.0 ([CWD]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn patch_git() { + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = {{ git = '{}' }} + + [patch.'{0}'] + bar = {{ path = "bar" }} + "#, + bar.url() + ), + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `file://[..]` +[CHECKING] bar v0.1.0 ([CWD]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn patch_to_git() { + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + + [patch.crates-io] + bar = {{ git = '{}' }} + "#, + bar.url() + ), + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `file://[..]` +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.0 (file://[..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn unused() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [patch.crates-io] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0")) + .file("bar/src/lib.rs", "not rust code") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[WARNING] Patch `bar v0.2.0 ([CWD]/bar)` was not used in the crate graph. +Check that [..] +with the [..] +what is [..] +version. [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 [..] +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check") + .with_stderr( + "\ +[WARNING] Patch `bar v0.2.0 ([CWD]/bar)` was not used in the crate graph. +Check that [..] +with the [..] +what is [..] +version. [..] +[FINISHED] [..] +", + ) + .run(); + + // unused patch should be in the lock file + let lock = p.read_lockfile(); + let toml: toml::Table = toml::from_str(&lock).unwrap(); + assert_eq!(toml["patch"]["unused"].as_array().unwrap().len(), 1); + assert_eq!(toml["patch"]["unused"][0]["name"].as_str(), Some("bar")); + assert_eq!( + toml["patch"]["unused"][0]["version"].as_str(), + Some("0.2.0") + ); +} + +#[cargo_test] +fn unused_with_mismatch_source_being_patched() { + registry::alt_init(); + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [patch.alternative] + bar = { path = "bar" } + + [patch.crates-io] + bar = { path = "baz" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0")) + .file("bar/src/lib.rs", "not rust code") + .file("baz/Cargo.toml", &basic_manifest("bar", "0.3.0")) + .file("baz/src/lib.rs", "not rust code") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[WARNING] Patch `bar v0.2.0 ([CWD]/bar)` was not used in the crate graph. +Perhaps you misspelled the source URL being patched. +Possible URLs for `[patch.]`: + crates-io +[WARNING] Patch `bar v0.3.0 ([CWD]/baz)` was not used in the crate graph. +Check that [..] +with the [..] +what is [..] +version. [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 [..] +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn prefer_patch_version() { + Package::new("bar", "0.1.2").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [patch.crates-io] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.1 ([CWD]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check") + .with_stderr( + "\ +[FINISHED] [..] +", + ) + .run(); + + // there should be no patch.unused in the toml file + let lock = p.read_lockfile(); + let toml: toml::Table = toml::from_str(&lock).unwrap(); + assert!(toml.get("patch").is_none()); +} + +#[cargo_test] +fn unused_from_config() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file( + ".cargo/config.toml", + r#" + [patch.crates-io] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0")) + .file("bar/src/lib.rs", "not rust code") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[WARNING] Patch `bar v0.2.0 ([CWD]/bar)` was not used in the crate graph. +Check that [..] +with the [..] +what is [..] +version. [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 [..] +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check") + .with_stderr( + "\ +[WARNING] Patch `bar v0.2.0 ([CWD]/bar)` was not used in the crate graph. +Check that [..] +with the [..] +what is [..] +version. [..] +[FINISHED] [..] +", + ) + .run(); + + // unused patch should be in the lock file + let lock = p.read_lockfile(); + let toml: toml::Table = toml::from_str(&lock).unwrap(); + assert_eq!(toml["patch"]["unused"].as_array().unwrap().len(), 1); + assert_eq!(toml["patch"]["unused"][0]["name"].as_str(), Some("bar")); + assert_eq!( + toml["patch"]["unused"][0]["version"].as_str(), + Some("0.2.0") + ); +} + +#[cargo_test] +fn unused_git() { + Package::new("bar", "0.1.0").publish(); + + let foo = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.2.0")) + .file("src/lib.rs", "") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + + [patch.crates-io] + bar = {{ git = '{}' }} + "#, + foo.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `file://[..]` +[UPDATING] `dummy-registry` index +[WARNING] Patch `bar v0.2.0 ([..])` was not used in the crate graph. +Check that [..] +with the [..] +what is [..] +version. [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 [..] +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check") + .with_stderr( + "\ +[WARNING] Patch `bar v0.2.0 ([..])` was not used in the crate graph. +Check that [..] +with the [..] +what is [..] +version. [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn add_patch() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 [..] +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [patch.crates-io] + bar = { path = 'bar' } + "#, + ); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 ([CWD]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn add_patch_from_config() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 [..] +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + + p.change_file( + ".cargo/config.toml", + r#" + [patch.crates-io] + bar = { path = 'bar' } + "#, + ); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 ([CWD]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn add_ignored_patch() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 [..] +[CHECKING] bar v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [patch.crates-io] + bar = { path = 'bar' } + "#, + ); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] Patch `bar v0.1.1 ([CWD]/bar)` was not used in the crate graph. +Check that [..] +with the [..] +what is [..] +version. [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); + p.cargo("check") + .with_stderr( + "\ +[WARNING] Patch `bar v0.1.1 ([CWD]/bar)` was not used in the crate graph. +Check that [..] +with the [..] +what is [..] +version. [..] +[FINISHED] [..]", + ) + .run(); + + p.cargo("update").run(); + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.1.1 ([CWD]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [..] +", + ) + .run(); +} + +#[cargo_test] +fn add_patch_with_features() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [patch.crates-io] + bar = { path = 'bar', features = ["some_feature"] } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] patch for `bar` uses the features mechanism. \ +default-features and features will not take effect because the patch dependency does not support this mechanism +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.0 ([CWD]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check") + .with_stderr( + "\ +[WARNING] patch for `bar` uses the features mechanism. \ +default-features and features will not take effect because the patch dependency does not support this mechanism +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn add_patch_with_setting_default_features() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [patch.crates-io] + bar = { path = 'bar', default-features = false, features = ["none_default_feature"] } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] patch for `bar` uses the features mechanism. \ +default-features and features will not take effect because the patch dependency does not support this mechanism +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.0 ([CWD]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + p.cargo("check") + .with_stderr( + "\ +[WARNING] patch for `bar` uses the features mechanism. \ +default-features and features will not take effect because the patch dependency does not support this mechanism +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn no_warn_ws_patch() { + Package::new("c", "0.1.0").publish(); + + // Don't issue an unused patch warning when the patch isn't used when + // partially building a workspace. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b", "c"] + + [patch.crates-io] + c = { path = "c" } + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + [dependencies] + c = "0.1.0" + "#, + ) + .file("b/src/lib.rs", "") + .file("c/Cargo.toml", &basic_manifest("c", "0.1.0")) + .file("c/src/lib.rs", "") + .build(); + + p.cargo("check -p a") + .with_stderr( + "\ +[UPDATING] [..] +[CHECKING] a [..] +[FINISHED] [..]", + ) + .run(); +} + +#[cargo_test] +fn new_minor() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [patch.crates-io] + bar = { path = 'bar' } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.1 [..] +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn transitive_new_minor() { + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = 'bar' } + + [patch.crates-io] + baz = { path = 'baz' } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + baz = '0.1.0' + "#, + ) + .file("bar/src/lib.rs", r#""#) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.1")) + .file("baz/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] baz v0.1.1 [..] +[CHECKING] bar v0.1.0 [..] +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn new_major() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.2.0" + + [patch.crates-io] + bar = { path = 'bar' } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.2.0 [..] +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + Package::new("bar", "0.2.0").publish(); + p.cargo("update").run(); + p.cargo("check") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.2.0" + "#, + ); + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.2.0 [..] +[CHECKING] bar v0.2.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn transitive_new_major() { + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = 'bar' } + + [patch.crates-io] + baz = { path = 'baz' } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + baz = '0.2.0' + "#, + ) + .file("bar/src/lib.rs", r#""#) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.2.0")) + .file("baz/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] baz v0.2.0 [..] +[CHECKING] bar v0.1.0 [..] +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn shared_by_transitive() { + Package::new("baz", "0.1.1").publish(); + + let baz = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("baz", "0.1.2")) + .file("src/lib.rs", "") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = " 0.1.0" + + [dependencies] + bar = {{ path = "bar" }} + baz = "0.1" + + [patch.crates-io] + baz = {{ git = "{}", version = "0.1" }} + "#, + baz.url(), + ), + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + baz = "0.1.1" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] git repository `file://[..]` +[UPDATING] `dummy-registry` index +[CHECKING] baz v0.1.2 [..] +[CHECKING] bar v0.1.0 [..] +[CHECKING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn remove_patch() { + Package::new("foo", "0.1.0").publish(); + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + + [patch.crates-io] + foo = { path = 'foo' } + bar = { path = 'bar' } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", r#""#) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", r#""#) + .build(); + + // Generate a lock file where `foo` is unused + p.cargo("check").run(); + let lock_file1 = p.read_lockfile(); + + // Remove `foo` and generate a new lock file form the old one + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + + [patch.crates-io] + bar = { path = 'bar' } + "#, + ); + p.cargo("check").run(); + let lock_file2 = p.read_lockfile(); + + // Remove the lock file and build from scratch + fs::remove_file(p.root().join("Cargo.lock")).unwrap(); + p.cargo("check").run(); + let lock_file3 = p.read_lockfile(); + + assert!(lock_file1.contains("foo")); + assert_eq!(lock_file2, lock_file3); + assert_ne!(lock_file1, lock_file2); +} + +#[cargo_test] +fn non_crates_io() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [patch.some-other-source] + bar = { path = 'bar' } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + [patch] entry `some-other-source` should be a URL or registry name + +Caused by: + invalid url `some-other-source`: relative URL without a base +", + ) + .run(); +} + +#[cargo_test] +fn replace_with_crates_io() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [patch.crates-io] + bar = "0.1" + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", r#""#) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +error: failed to resolve patches for `[..]` + +Caused by: + patch for `bar` in `[..]` points to the same source, but patches must point \ + to different sources +", + ) + .run(); +} + +#[cargo_test] +fn patch_in_virtual() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + + [patch.crates-io] + bar = { path = "bar" } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", r#""#) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = "0.1" + "#, + ) + .file("foo/src/lib.rs", r#""#) + .build(); + + p.cargo("check").run(); + p.cargo("check").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn patch_depends_on_another_patch() { + Package::new("bar", "0.1.0") + .file("src/lib.rs", "broken code") + .publish(); + + Package::new("baz", "0.1.0") + .dep("bar", "0.1") + .file("src/lib.rs", "broken code") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.1.0" + + [dependencies] + bar = "0.1" + baz = "0.1" + + [patch.crates-io] + bar = { path = "bar" } + baz = { path = "baz" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("bar/src/lib.rs", r#""#) + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.1" + authors = [] + + [dependencies] + bar = "0.1" + "#, + ) + .file("baz/src/lib.rs", r#""#) + .build(); + + p.cargo("check").run(); + + // Nothing should be rebuilt, no registry should be updated. + p.cargo("check").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn replace_prerelease() { + Package::new("baz", "1.1.0-pre.1").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + + [patch.crates-io] + baz = { path = "./baz" } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + baz = "1.1.0-pre.1" + "#, + ) + .file( + "bar/src/main.rs", + "extern crate baz; fn main() { baz::baz() }", + ) + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "1.1.0-pre.1" + authors = [] + [workspace] + "#, + ) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn patch_older() { + Package::new("baz", "1.0.2").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = 'bar' } + baz = "=1.0.1" + + [patch.crates-io] + baz = { path = "./baz" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + baz = "1.0.0" + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "1.0.1" + authors = [] + "#, + ) + .file("baz/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] +[CHECKING] baz v1.0.1 [..] +[CHECKING] bar v0.5.0 [..] +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn cycle() { + Package::new("a", "1.0.0").publish(); + Package::new("b", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + + [patch.crates-io] + a = {path="a"} + b = {path="b"} + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "1.0.0" + + [dependencies] + b = "1.0" + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "1.0.0" + + [dependencies] + a = "1.0" + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] cyclic package dependency: [..] +package `[..]` + ... which satisfies dependency `[..]` of package `[..]` + ... which satisfies dependency `[..]` of package `[..]` +", + ) + .run(); +} + +#[cargo_test] +fn multipatch() { + Package::new("a", "1.0.0").publish(); + Package::new("a", "2.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + a1 = { version = "1", package = "a" } + a2 = { version = "2", package = "a" } + + [patch.crates-io] + b1 = { path = "a1", package = "a" } + b2 = { path = "a2", package = "a" } + "#, + ) + .file("src/lib.rs", "pub fn foo() { a1::f1(); a2::f2(); }") + .file( + "a1/Cargo.toml", + r#" + [package] + name = "a" + version = "1.0.0" + "#, + ) + .file("a1/src/lib.rs", "pub fn f1() {}") + .file( + "a2/Cargo.toml", + r#" + [package] + name = "a" + version = "2.0.0" + "#, + ) + .file("a2/src/lib.rs", "pub fn f2() {}") + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn patch_same_version() { + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + .build(); + + cargo_test_support::registry::init(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + [dependencies] + bar = "0.1" + [patch.crates-io] + bar = {{ path = "bar" }} + bar2 = {{ git = '{}', package = 'bar' }} + "#, + bar.url(), + ), + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +error: cannot have two `[patch]` entries which both resolve to `bar v0.1.0` +", + ) + .run(); +} + +#[cargo_test] +fn two_semver_compatible() { + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("src/lib.rs", "") + .build(); + + cargo_test_support::registry::init(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + [dependencies] + bar = "0.1" + [patch.crates-io] + bar = {{ path = "bar" }} + bar2 = {{ git = '{}', package = 'bar' }} + "#, + bar.url(), + ), + ) + .file("src/lib.rs", "pub fn foo() { bar::foo() }") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.2" + "#, + ) + .file("bar/src/lib.rs", "pub fn foo() {}") + .build(); + + // assert the build succeeds and doesn't panic anywhere, and then afterwards + // assert that the build succeeds again without updating anything or + // building anything else. + p.cargo("check").run(); + p.cargo("check") + .with_stderr( + "\ +warning: Patch `bar v0.1.1 [..]` was not used in the crate graph. +Perhaps you misspelled the source URL being patched. +Possible URLs for `[patch.]`: + [CWD]/bar +[FINISHED] [..]", + ) + .run(); +} + +#[cargo_test] +fn multipatch_select_big() { + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + .build(); + + cargo_test_support::registry::init(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + [dependencies] + bar = "*" + [patch.crates-io] + bar = {{ path = "bar" }} + bar2 = {{ git = '{}', package = 'bar' }} + "#, + bar.url(), + ), + ) + .file("src/lib.rs", "pub fn foo() { bar::foo() }") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.2.0" + "#, + ) + .file("bar/src/lib.rs", "pub fn foo() {}") + .build(); + + // assert the build succeeds, which is only possible if 0.2.0 is selected + // since 0.1.0 is missing the function we need. Afterwards assert that the + // build succeeds again without updating anything or building anything else. + p.cargo("check").run(); + p.cargo("check") + .with_stderr( + "\ +warning: Patch `bar v0.1.0 [..]` was not used in the crate graph. +Perhaps you misspelled the source URL being patched. +Possible URLs for `[patch.]`: + [CWD]/bar +[FINISHED] [..]", + ) + .run(); +} + +#[cargo_test] +fn canonicalize_a_bunch() { + let base = git::repo(&paths::root().join("base")) + .file("Cargo.toml", &basic_manifest("base", "0.1.0")) + .file("src/lib.rs", "") + .build(); + + let intermediate = git::repo(&paths::root().join("intermediate")) + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "intermediate" + version = "0.1.0" + + [dependencies] + # Note the lack of trailing slash + base = {{ git = '{}' }} + "#, + base.url(), + ), + ) + .file("src/lib.rs", "pub fn f() { base::f() }") + .build(); + + let newbase = git::repo(&paths::root().join("newbase")) + .file("Cargo.toml", &basic_manifest("base", "0.1.0")) + .file("src/lib.rs", "pub fn f() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + # Note the trailing slashes + base = {{ git = '{base}/' }} + intermediate = {{ git = '{intermediate}/' }} + + [patch.'{base}'] # Note the lack of trailing slash + base = {{ git = '{newbase}' }} + "#, + base = base.url(), + intermediate = intermediate.url(), + newbase = newbase.url(), + ), + ) + .file("src/lib.rs", "pub fn a() { base::f(); intermediate::f() }") + .build(); + + // Once to make sure it actually works + p.cargo("check").run(); + + // Then a few more times for good measure to ensure no weird warnings about + // `[patch]` are printed. + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + p.cargo("check").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn update_unused_new_version() { + // If there is an unused patch entry, and then you update the patch, + // make sure `cargo update` will be able to fix the lock file. + Package::new("bar", "0.1.5").publish(); + + // Start with a lock file to 0.1.5, and an "unused" patch because the + // version is too old. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = "0.1.5" + + [patch.crates-io] + bar = { path = "../bar" } + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Patch is too old. + let bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.4")) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr_contains("[WARNING] Patch `bar v0.1.4 [..] was not used in the crate graph.") + .run(); + // unused patch should be in the lock file + let lock = p.read_lockfile(); + let toml: toml::Table = toml::from_str(&lock).unwrap(); + assert_eq!(toml["patch"]["unused"].as_array().unwrap().len(), 1); + assert_eq!(toml["patch"]["unused"][0]["name"].as_str(), Some("bar")); + assert_eq!( + toml["patch"]["unused"][0]["version"].as_str(), + Some("0.1.4") + ); + + // Oh, OK, let's update to the latest version. + bar.change_file("Cargo.toml", &basic_manifest("bar", "0.1.6")); + + // Create a backup so we can test it with different options. + fs::copy(p.root().join("Cargo.lock"), p.root().join("Cargo.lock.bak")).unwrap(); + + // Try to build again, this should automatically update Cargo.lock. + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.6 ([..]/bar) +[CHECKING] foo v0.0.1 ([..]/foo) +[FINISHED] [..] +", + ) + .run(); + // This should not update any registry. + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + assert!(!p.read_lockfile().contains("unused")); + + // Restore the lock file, and see if `update` will work, too. + fs::copy(p.root().join("Cargo.lock.bak"), p.root().join("Cargo.lock")).unwrap(); + + // Try `update -p`. + p.cargo("update -p bar") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[ADDING] bar v0.1.6 ([..]/bar) +[REMOVING] bar v0.1.5 +", + ) + .run(); + + // Try with bare `cargo update`. + fs::copy(p.root().join("Cargo.lock.bak"), p.root().join("Cargo.lock")).unwrap(); + p.cargo("update") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[ADDING] bar v0.1.6 ([..]/bar) +[REMOVING] bar v0.1.5 +", + ) + .run(); +} + +#[cargo_test] +fn too_many_matches() { + // The patch locations has multiple versions that match. + registry::alt_init(); + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.1.0").alternative(true).publish(); + Package::new("bar", "0.1.1").alternative(true).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + + [patch.crates-io] + bar = { version = "0.1", registry = "alternative" } + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Picks 0.1.1, the most recent version. + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `alternative` index +[ERROR] failed to resolve patches for `https://github.com/rust-lang/crates.io-index` + +Caused by: + patch for `bar` in `https://github.com/rust-lang/crates.io-index` failed to resolve + +Caused by: + patch for `bar` in `registry `alternative`` resolved to more than one candidate + Found versions: 0.1.0, 0.1.1 + Update the patch definition to select only one package. + For example, add an `=` version requirement to the patch definition, such as `version = \"=0.1.1\"`. +", + ) + .run(); +} + +#[cargo_test] +fn no_matches() { + // A patch to a location that does not contain the named package. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + + [patch.crates-io] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("abc", "0.1.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to resolve patches for `https://github.com/rust-lang/crates.io-index` + +Caused by: + patch for `bar` in `https://github.com/rust-lang/crates.io-index` failed to resolve + +Caused by: + The patch location `[..]/foo/bar` does not appear to contain any packages matching the name `bar`. +", + ) + .run(); +} + +#[cargo_test] +fn mismatched_version() { + // A patch to a location that has an old version. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1.1" + + [patch.crates-io] + bar = { path = "bar", version = "0.1.1" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to resolve patches for `https://github.com/rust-lang/crates.io-index` + +Caused by: + patch for `bar` in `https://github.com/rust-lang/crates.io-index` failed to resolve + +Caused by: + The patch location `[..]/foo/bar` contains a `bar` package with version `0.1.0`, \ + but the patch definition requires `^0.1.1`. + Check that the version in the patch location is what you expect, \ + and update the patch definition to match. +", + ) + .run(); +} + +#[cargo_test] +fn patch_walks_backwards() { + // Starting with a locked patch, change the patch so it points to an older version. + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + + [patch.crates-io] + bar = {path="bar"} + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.1 ([..]/foo/bar) +[CHECKING] foo v0.1.0 ([..]/foo) +[FINISHED] [..] +", + ) + .run(); + + // Somehow the user changes the version backwards. + p.change_file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.0 ([..]/foo/bar) +[CHECKING] foo v0.1.0 ([..]/foo) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn patch_walks_backwards_restricted() { + // This is the same as `patch_walks_backwards`, but the patch contains a + // `version` qualifier. This is unusual, just checking a strange edge case. + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + + [patch.crates-io] + bar = {path="bar", version="0.1.1"} + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.1 ([..]/foo/bar) +[CHECKING] foo v0.1.0 ([..]/foo) +[FINISHED] [..] +", + ) + .run(); + + // Somehow the user changes the version backwards. + p.change_file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to resolve patches for `https://github.com/rust-lang/crates.io-index` + +Caused by: + patch for `bar` in `https://github.com/rust-lang/crates.io-index` failed to resolve + +Caused by: + The patch location `[..]/foo/bar` contains a `bar` package with version `0.1.0`, but the patch definition requires `^0.1.1`. + Check that the version in the patch location is what you expect, and update the patch definition to match. +", + ) + .run(); +} + +#[cargo_test] +fn patched_dep_new_version() { + // What happens when a patch is locked, and then one of the patched + // dependencies needs to be updated. In this case, the baz requirement + // gets updated from 0.1.0 to 0.1.1. + Package::new("bar", "0.1.0").dep("baz", "0.1.0").publish(); + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + + [patch.crates-io] + bar = {path="bar"} + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + baz = "0.1" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + // Lock everything. + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] baz v0.1.0 [..] +[CHECKING] baz v0.1.0 +[CHECKING] bar v0.1.0 ([..]/foo/bar) +[CHECKING] foo v0.1.0 ([..]/foo) +[FINISHED] [..] +", + ) + .run(); + + Package::new("baz", "0.1.1").publish(); + + // Just the presence of the new version should not have changed anything. + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + + // Modify the patch so it requires the new version. + p.change_file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + baz = "0.1.1" + "#, + ); + + // Should unlock and update cleanly. + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] baz v0.1.1 (registry `dummy-registry`) +[CHECKING] baz v0.1.1 +[CHECKING] bar v0.1.0 ([..]/foo/bar) +[CHECKING] foo v0.1.0 ([..]/foo) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn patch_update_doesnt_update_other_sources() { + // Very extreme edge case, make sure a patch update doesn't update other + // sources. + registry::alt_init(); + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.1.0").alternative(true).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + bar_alt = { version = "0.1", registry = "alternative", package = "bar" } + + [patch.crates-io] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr_unordered( + "\ +[UPDATING] `dummy-registry` index +[UPDATING] `alternative` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 (registry `alternative`) +[CHECKING] bar v0.1.0 (registry `alternative`) +[CHECKING] bar v0.1.0 ([..]/foo/bar) +[CHECKING] foo v0.1.0 ([..]/foo) +[FINISHED] [..] +", + ) + .run(); + + // Publish new versions in both sources. + Package::new("bar", "0.1.1").publish(); + Package::new("bar", "0.1.1").alternative(true).publish(); + + // Since it is locked, nothing should change. + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + + // Require new version on crates.io. + p.change_file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1")); + + // This should not update bar_alt. + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.1 ([..]/foo/bar) +[CHECKING] foo v0.1.0 ([..]/foo) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn can_update_with_alt_reg() { + // A patch to an alt reg can update. + registry::alt_init(); + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.1.0").alternative(true).publish(); + Package::new("bar", "0.1.1").alternative(true).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + + [patch.crates-io] + bar = { version = "=0.1.1", registry = "alternative" } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `alternative` index +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.1 (registry `alternative`) +[CHECKING] bar v0.1.1 (registry `alternative`) +[CHECKING] foo v0.1.0 ([..]/foo) +[FINISHED] [..] +", + ) + .run(); + + Package::new("bar", "0.1.2").alternative(true).publish(); + + // Should remain locked. + p.cargo("check").with_stderr("[FINISHED] [..]").run(); + + // This does nothing, due to `=` requirement. + p.cargo("update -p bar") + .with_stderr( + "\ +[UPDATING] `alternative` index +[UPDATING] `dummy-registry` index +", + ) + .run(); + + // Bump to 0.1.2. + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + + [patch.crates-io] + bar = { version = "=0.1.2", registry = "alternative" } + "#, + ); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `alternative` index +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.2 (registry `alternative`) +[CHECKING] bar v0.1.2 (registry `alternative`) +[CHECKING] foo v0.1.0 ([..]/foo) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn old_git_patch() { + // Example where an old lockfile with an explicit branch="master" in Cargo.toml. + Package::new("bar", "1.0.0").publish(); + let (bar, bar_repo) = git::new_repo("bar", |p| { + p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) + .file("src/lib.rs", "") + }); + + let bar_oid = bar_repo.head().unwrap().target().unwrap(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + + [patch.crates-io] + bar = {{ git = "{}", branch = "master" }} + "#, + bar.url() + ), + ) + .file( + "Cargo.lock", + &format!( + r#" +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bar" +version = "1.0.0" +source = "git+{}#{}" + +[[package]] +name = "foo" +version = "0.1.0" +dependencies = [ + "bar", +] + "#, + bar.url(), + bar_oid + ), + ) + .file("src/lib.rs", "") + .build(); + + bar.change_file("Cargo.toml", &basic_manifest("bar", "2.0.0")); + git::add(&bar_repo); + git::commit(&bar_repo); + + // This *should* keep the old lock. + p.cargo("tree") + // .env("CARGO_LOG", "trace") + .with_stderr( + "\ +[UPDATING] [..] +", + ) + // .with_status(1) + .with_stdout(format!( + "\ +foo v0.1.0 [..] +└── bar v1.0.0 (file:///[..]branch=master#{}) +", + &bar_oid.to_string()[..8] + )) + .run(); +} diff --git a/tests/testsuite/path.rs b/tests/testsuite/path.rs new file mode 100644 index 0000000..ebbb72f --- /dev/null +++ b/tests/testsuite/path.rs @@ -0,0 +1,1139 @@ +//! Tests for `path` dependencies. + +use cargo_test_support::paths::{self, CargoPathExt}; +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_lib_manifest, basic_manifest, main_file, project}; +use cargo_test_support::{sleep_ms, t}; +use std::fs; + +#[cargo_test] +// I have no idea why this is failing spuriously on Windows; +// for more info, see #3466. +#[cfg(not(windows))] +fn cargo_compile_with_nested_deps_shorthand() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + + version = "0.5.0" + path = "bar" + "#, + ) + .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.baz] + + version = "0.5.0" + path = "baz" + + [lib] + + name = "bar" + "#, + ) + .file( + "bar/src/bar.rs", + r#" + extern crate baz; + + pub fn gimme() -> String { + baz::gimme() + } + "#, + ) + .file("bar/baz/Cargo.toml", &basic_lib_manifest("baz")) + .file( + "bar/baz/src/baz.rs", + r#" + pub fn gimme() -> String { + "test passed".to_string() + } + "#, + ) + .build(); + + p.cargo("build") + .with_stderr( + "[COMPILING] baz v0.5.0 ([CWD]/bar/baz)\n\ + [COMPILING] bar v0.5.0 ([CWD]/bar)\n\ + [COMPILING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); + + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("test passed\n").run(); + + println!("cleaning"); + p.cargo("clean -v").with_stdout("").run(); + println!("building baz"); + p.cargo("build -p baz") + .with_stderr( + "[COMPILING] baz v0.5.0 ([CWD]/bar/baz)\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); + println!("building foo"); + p.cargo("build -p foo") + .with_stderr( + "[COMPILING] bar v0.5.0 ([CWD]/bar)\n\ + [COMPILING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn cargo_compile_with_root_dev_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dev-dependencies.bar] + + version = "0.5.0" + path = "../bar" + + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .build(); + let _p2 = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file( + "src/lib.rs", + r#" + pub fn gimme() -> &'static str { + "zoidberg" + } + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]can't find crate for `bar`") + .run(); +} + +#[cargo_test] +fn cargo_compile_with_root_dev_deps_with_testing() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dev-dependencies.bar] + + version = "0.5.0" + path = "../bar" + + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .build(); + let _p2 = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file( + "src/lib.rs", + r#" + pub fn gimme() -> &'static str { + "zoidberg" + } + "#, + ) + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] [..] v0.5.0 ([..]) +[COMPILING] [..] v0.5.0 ([..]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("running 0 tests") + .run(); +} + +#[cargo_test] +fn cargo_compile_with_transitive_dev_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + + version = "0.5.0" + path = "bar" + "#, + ) + .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dev-dependencies.baz] + + git = "git://example.com/path/to/nowhere" + + [lib] + + name = "bar" + "#, + ) + .file( + "bar/src/bar.rs", + r#" + pub fn gimme() -> &'static str { + "zoidberg" + } + "#, + ) + .build(); + + p.cargo("build") + .with_stderr( + "[COMPILING] bar v0.5.0 ([CWD]/bar)\n\ + [COMPILING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in \ + [..]\n", + ) + .run(); + + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("zoidberg\n").run(); +} + +#[cargo_test] +fn no_rebuild_dependency() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() { bar::bar() }") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/bar.rs", "pub fn bar() {}") + .build(); + // First time around we should compile both foo and bar + p.cargo("check") + .with_stderr( + "[CHECKING] bar v0.5.0 ([CWD]/bar)\n\ + [CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); + + sleep_ms(1000); + p.change_file( + "src/main.rs", + r#" + extern crate bar; + fn main() { bar::bar(); } + "#, + ); + // Don't compile bar, but do recompile foo. + p.cargo("check") + .with_stderr( + "[CHECKING] foo v0.5.0 ([..])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn deep_dependencies_trigger_rebuild() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() { bar::bar() }") + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + name = "bar" + [dependencies.baz] + path = "../baz" + "#, + ) + .file( + "bar/src/bar.rs", + "extern crate baz; pub fn bar() { baz::baz() }", + ) + .file("baz/Cargo.toml", &basic_lib_manifest("baz")) + .file("baz/src/baz.rs", "pub fn baz() {}") + .build(); + p.cargo("check") + .with_stderr( + "[CHECKING] baz v0.5.0 ([CWD]/baz)\n\ + [CHECKING] bar v0.5.0 ([CWD]/bar)\n\ + [CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); + p.cargo("check").with_stdout("").run(); + + // Make sure an update to baz triggers a rebuild of bar + // + // We base recompilation off mtime, so sleep for at least a second to ensure + // that this write will change the mtime. + sleep_ms(1000); + p.change_file("baz/src/baz.rs", r#"pub fn baz() { println!("hello!"); }"#); + sleep_ms(1000); + p.cargo("check") + .with_stderr( + "[CHECKING] baz v0.5.0 ([CWD]/baz)\n\ + [CHECKING] bar v0.5.0 ([CWD]/bar)\n\ + [CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); + + // Make sure an update to bar doesn't trigger baz + sleep_ms(1000); + p.change_file( + "bar/src/bar.rs", + r#" + extern crate baz; + pub fn bar() { println!("hello!"); baz::baz(); } + "#, + ); + sleep_ms(1000); + p.cargo("check") + .with_stderr( + "[CHECKING] bar v0.5.0 ([CWD]/bar)\n\ + [CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn no_rebuild_two_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + [dependencies.baz] + path = "baz" + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() { bar::bar() }") + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + name = "bar" + [dependencies.baz] + path = "../baz" + "#, + ) + .file("bar/src/bar.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_lib_manifest("baz")) + .file("baz/src/baz.rs", "pub fn baz() {}") + .build(); + p.cargo("build") + .with_stderr( + "[COMPILING] baz v0.5.0 ([CWD]/baz)\n\ + [COMPILING] bar v0.5.0 ([CWD]/bar)\n\ + [COMPILING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); + assert!(p.bin("foo").is_file()); + p.cargo("build").with_stdout("").run(); + assert!(p.bin("foo").is_file()); +} + +#[cargo_test] +fn nested_deps_recompile() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + + version = "0.5.0" + path = "src/bar" + "#, + ) + .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file("src/bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("src/bar/src/bar.rs", "pub fn gimme() -> i32 { 92 }") + .build(); + + p.cargo("check") + .with_stderr( + "[CHECKING] bar v0.5.0 ([CWD]/src/bar)\n\ + [CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); + sleep_ms(1000); + + p.change_file("src/main.rs", r#"fn main() {}"#); + + // This shouldn't recompile `bar` + p.cargo("check") + .with_stderr( + "[CHECKING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn error_message_for_missing_manifest() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + + path = "src/bar" + "#, + ) + .file("src/lib.rs", "") + .file("src/bar/not-a-manifest", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to get `bar` as a dependency of package `foo v0.5.0 [..]` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update [CWD]/src/bar + +Caused by: + failed to read `[..]bar/Cargo.toml` + +Caused by: + [..] (os error [..]) +", + ) + .run(); +} + +#[cargo_test] +fn override_relative() { + let bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "") + .build(); + + fs::create_dir(&paths::root().join(".cargo")).unwrap(); + fs::write(&paths::root().join(".cargo/config"), r#"paths = ["bar"]"#).unwrap(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = '{}' + "#, + bar.root().display() + ), + ) + .file("src/lib.rs", "") + .build(); + p.cargo("check -v").run(); +} + +#[cargo_test] +fn override_self() { + let bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "") + .build(); + + let p = project(); + let root = p.root(); + let p = p + .file(".cargo/config", &format!("paths = ['{}']", root.display())) + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = '{}' + + "#, + bar.root().display() + ), + ) + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn override_path_dep() { + let bar = project() + .at("bar") + .file( + "p1/Cargo.toml", + r#" + [package] + name = "p1" + version = "0.5.0" + authors = [] + + [dependencies.p2] + path = "../p2" + "#, + ) + .file("p1/src/lib.rs", "") + .file("p2/Cargo.toml", &basic_manifest("p2", "0.5.0")) + .file("p2/src/lib.rs", "") + .build(); + + let p = project() + .file( + ".cargo/config", + &format!( + "paths = ['{}', '{}']", + bar.root().join("p1").display(), + bar.root().join("p2").display() + ), + ) + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.p2] + path = '{}' + + "#, + bar.root().join("p2").display() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v").run(); +} + +#[cargo_test] +fn path_dep_build_cmd() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + + version = "0.5.0" + path = "bar" + "#, + ) + .file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file( + "bar/Cargo.toml", + r#" + [package] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + + [lib] + name = "bar" + path = "src/bar.rs" + "#, + ) + .file( + "bar/build.rs", + r#" + use std::fs; + fn main() { + fs::copy("src/bar.rs.in", "src/bar.rs").unwrap(); + } + "#, + ) + .file("bar/src/bar.rs.in", "pub fn gimme() -> i32 { 0 }") + .build(); + p.root().join("bar").move_into_the_past(); + + p.cargo("build") + .with_stderr( + "[COMPILING] bar v0.5.0 ([CWD]/bar)\n\ + [COMPILING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in \ + [..]\n", + ) + .run(); + + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("0\n").run(); + + // Touching bar.rs.in should cause the `build` command to run again. + p.change_file("bar/src/bar.rs.in", "pub fn gimme() -> i32 { 1 }"); + + p.cargo("build") + .with_stderr( + "[COMPILING] bar v0.5.0 ([CWD]/bar)\n\ + [COMPILING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in \ + [..]\n", + ) + .run(); + + p.process(&p.bin("foo")).with_stdout("1\n").run(); +} + +#[cargo_test] +fn dev_deps_no_rebuild_lib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dev-dependencies.bar] + path = "bar" + + [lib] + name = "foo" + doctest = false + "#, + ) + .file( + "src/lib.rs", + r#" + #[cfg(test)] #[allow(unused_extern_crates)] extern crate bar; + #[cfg(not(test))] pub fn foo() { env!("FOO"); } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + p.cargo("build") + .env("FOO", "bar") + .with_stderr( + "[COMPILING] foo v0.5.0 ([CWD])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) \ + in [..]\n", + ) + .run(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] [..] v0.5.0 ([CWD][..]) +[COMPILING] [..] v0.5.0 ([CWD][..]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("running 0 tests") + .run(); +} + +#[cargo_test] +fn custom_target_no_rebuild() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies] + a = { path = "a" } + [workspace] + members = ["a", "b"] + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.5.0")) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + [dependencies] + a = { path = "../a" } + "#, + ) + .file("b/src/lib.rs", "") + .build(); + p.cargo("check") + .with_stderr( + "\ +[CHECKING] a v0.5.0 ([..]) +[CHECKING] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + t!(fs::rename( + p.root().join("target"), + p.root().join("target_moved") + )); + p.cargo("check --manifest-path=b/Cargo.toml") + .env("CARGO_TARGET_DIR", "target_moved") + .with_stderr( + "\ +[CHECKING] b v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn override_and_depend() { + let p = project() + .no_manifest() + .file( + "a/a1/Cargo.toml", + r#" + [package] + name = "a1" + version = "0.5.0" + authors = [] + [dependencies] + a2 = { path = "../a2" } + "#, + ) + .file("a/a1/src/lib.rs", "") + .file("a/a2/Cargo.toml", &basic_manifest("a2", "0.5.0")) + .file("a/a2/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.5.0" + authors = [] + [dependencies] + a1 = { path = "../a/a1" } + a2 = { path = "../a/a2" } + "#, + ) + .file("b/src/lib.rs", "") + .file("b/.cargo/config", r#"paths = ["../a"]"#) + .build(); + p.cargo("check") + .cwd("b") + .with_stderr( + "\ +[WARNING] skipping duplicate package `a2` found at `[..]` +[CHECKING] a2 v0.5.0 ([..]) +[CHECKING] a1 v0.5.0 ([..]) +[CHECKING] b v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn missing_path_dependency() { + let p = project() + .file("Cargo.toml", &basic_manifest("a", "0.5.0")) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#"paths = ["../whoa-this-does-not-exist"]"#, + ) + .build(); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to update path override `[..]../whoa-this-does-not-exist` \ +(defined in `[..]`) + +Caused by: + failed to read directory `[..]` + +Caused by: + [..] (os error [..]) +", + ) + .run(); +} + +#[cargo_test] +fn invalid_path_dep_in_workspace_with_lockfile() { + Package::new("bar", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "top" + version = "0.5.0" + authors = [] + + [workspace] + + [dependencies] + foo = { path = "foo" } + "#, + ) + .file("src/lib.rs", "") + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + + // Generate a lock file + p.cargo("check").run(); + + // Change the dependency on `bar` to an invalid path + p.change_file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + bar = { path = "" } + "#, + ); + + // Make sure we get a nice error. In the past this actually stack + // overflowed! + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: no matching package found +searched package name: `bar` +perhaps you meant: foo +location searched: [..] +required by package `foo v0.5.0 ([..])` +", + ) + .run(); +} + +#[cargo_test] +fn workspace_produces_rlib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "top" + version = "0.5.0" + authors = [] + + [workspace] + + [dependencies] + foo = { path = "foo" } + "#, + ) + .file("src/lib.rs", "") + .file("foo/Cargo.toml", &basic_manifest("foo", "0.5.0")) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + assert!(p.root().join("target/debug/libtop.rlib").is_file()); + assert!(!p.root().join("target/debug/libfoo.rlib").is_file()); +} + +#[cargo_test] +fn deep_path_error() { + // Test for an error loading a path deep in the dependency graph. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + a = {path="a"} + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + [dependencies] + b = {path="../b"} + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + [dependencies] + c = {path="../c"} + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to get `c` as a dependency of package `b v0.1.0 [..]` + ... which satisfies path dependency `b` of package `a v0.1.0 [..]` + ... which satisfies path dependency `a` of package `foo v0.1.0 [..]` + +Caused by: + failed to load source for dependency `c` + +Caused by: + Unable to update [..]/foo/c + +Caused by: + failed to read `[..]/foo/c/Cargo.toml` + +Caused by: + [..] +", + ) + .run(); +} + +#[cargo_test] +fn catch_tricky_cycle() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "message" + version = "0.1.0" + + [dev-dependencies] + test = { path = "test" } + "#, + ) + .file("src/lib.rs", "") + .file( + "tangle/Cargo.toml", + r#" + [package] + name = "tangle" + version = "0.1.0" + + [dependencies] + message = { path = ".." } + snapshot = { path = "../snapshot" } + "#, + ) + .file("tangle/src/lib.rs", "") + .file( + "snapshot/Cargo.toml", + r#" + [package] + name = "snapshot" + version = "0.1.0" + + [dependencies] + ledger = { path = "../ledger" } + "#, + ) + .file("snapshot/src/lib.rs", "") + .file( + "ledger/Cargo.toml", + r#" + [package] + name = "ledger" + version = "0.1.0" + + [dependencies] + tangle = { path = "../tangle" } + "#, + ) + .file("ledger/src/lib.rs", "") + .file( + "test/Cargo.toml", + r#" + [package] + name = "test" + version = "0.1.0" + + [dependencies] + snapshot = { path = "../snapshot" } + "#, + ) + .file("test/src/lib.rs", "") + .build(); + + p.cargo("test") + .with_stderr_contains("[..]cyclic package dependency[..]") + .with_status(101) + .run(); +} diff --git a/tests/testsuite/paths.rs b/tests/testsuite/paths.rs new file mode 100644 index 0000000..31e00ae --- /dev/null +++ b/tests/testsuite/paths.rs @@ -0,0 +1,226 @@ +//! Tests for `paths` overrides. + +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_manifest, project}; + +#[cargo_test] +fn broken_path_override_warns() { + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.2.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a1" } + "#, + ) + .file("src/lib.rs", "") + .file( + "a1/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + "#, + ) + .file("a1/src/lib.rs", "") + .file( + "a2/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.2" + "#, + ) + .file("a2/src/lib.rs", "") + .file(".cargo/config", r#"paths = ["a2"]"#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] +warning: path override for crate `a` has altered the original list of +dependencies; the dependency on `bar` was either added or +modified to not match the previously resolved version + +This is currently allowed but is known to produce buggy behavior with spurious +recompiles and changes to the crate graph. Path overrides unfortunately were +never intended to support this feature, so for now this message is just a +warning. In the future, however, this message will become a hard error. + +To change the dependency graph via an override it's recommended to use the +`[patch]` feature of Cargo instead of the path override feature. This is +documented online at the url below for more information. + +https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html + +[DOWNLOADING] crates ... +[DOWNLOADED] [..] +[CHECKING] [..] +[CHECKING] [..] +[CHECKING] [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn override_to_path_dep() { + Package::new("bar", "0.1.0").dep("baz", "0.1").publish(); + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + baz = { path = "baz" } + "#, + ) + .file("bar/src/lib.rs", "") + .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) + .file("bar/baz/src/lib.rs", "") + .file(".cargo/config", r#"paths = ["bar"]"#) + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn paths_ok_with_optional() { + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + baz = { version = "0.1", optional = true } + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "bar2/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + baz = { version = "0.1", optional = true } + "#, + ) + .file("bar2/src/lib.rs", "") + .file(".cargo/config", r#"paths = ["bar2"]"#) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.1.0 ([..]bar2) +[CHECKING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn paths_add_optional_bad() { + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .file( + "bar2/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + baz = { version = "0.1", optional = true } + "#, + ) + .file("bar2/src/lib.rs", "") + .file(".cargo/config", r#"paths = ["bar2"]"#) + .build(); + + p.cargo("check") + .with_stderr_contains( + "\ +warning: path override for crate `bar` has altered the original list of +dependencies; the dependency on `baz` was either added or\ +", + ) + .run(); +} diff --git a/tests/testsuite/pkgid.rs b/tests/testsuite/pkgid.rs new file mode 100644 index 0000000..3e3e469 --- /dev/null +++ b/tests/testsuite/pkgid.rs @@ -0,0 +1,128 @@ +//! Tests for the `cargo pkgid` command. + +use cargo_test_support::project; +use cargo_test_support::registry::Package; + +#[cargo_test] +fn simple() { + Package::new("bar", "0.1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("generate-lockfile").run(); + + p.cargo("pkgid foo") + .with_stdout(format!("file://[..]{}#0.1.0", p.root().to_str().unwrap())) + .run(); + + p.cargo("pkgid bar") + .with_stdout("https://github.com/rust-lang/crates.io-index#bar@0.1.0") + .run(); +} + +#[cargo_test] +fn suggestion_bad_pkgid() { + Package::new("crates-io", "0.1.0").publish(); + Package::new("two-ver", "0.1.0").publish(); + Package::new("two-ver", "0.2.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + crates-io = "0.1.0" + two-ver = "0.1.0" + two-ver2 = { package = "two-ver", version = "0.2.0" } + "#, + ) + .file("src/lib.rs", "") + .file("cratesio", "") + .build(); + + p.cargo("generate-lockfile").run(); + + // Bad URL. + p.cargo("pkgid https://example.com/crates-io") + .with_status(101) + .with_stderr( + "\ +error: package ID specification `https://example.com/crates-io` did not match any packages +Did you mean one of these? + + crates-io@0.1.0 +", + ) + .run(); + + // Bad name. + p.cargo("pkgid crates_io") + .with_status(101) + .with_stderr( + "\ +error: package ID specification `crates_io` did not match any packages + +Did you mean `crates-io`? +", + ) + .run(); + + // Bad version. + p.cargo("pkgid two-ver:0.3.0") + .with_status(101) + .with_stderr( + "\ +error: package ID specification `two-ver@0.3.0` did not match any packages +Did you mean one of these? + + two-ver@0.1.0 + two-ver@0.2.0 +", + ) + .run(); + + // Bad file URL. + p.cargo("pkgid ./Cargo.toml") + .with_status(101) + .with_stderr( + "\ +error: invalid package ID specification: `./Cargo.toml` + +Caused by: + package ID specification `./Cargo.toml` looks like a file path, maybe try file://[..]/Cargo.toml +", + ) + .run(); + + // Bad file URL with similar name. + p.cargo("pkgid './cratesio'") + .with_status(101) + .with_stderr( + "\ +error: invalid package ID specification: `./cratesio` + +Did you mean `crates-io`? + +Caused by: + package ID specification `./cratesio` looks like a file path, maybe try file://[..]/cratesio +", + ) + .run(); +} diff --git a/tests/testsuite/plugins.rs b/tests/testsuite/plugins.rs new file mode 100644 index 0000000..331ba32 --- /dev/null +++ b/tests/testsuite/plugins.rs @@ -0,0 +1,421 @@ +//! Tests for rustc plugins. + +use cargo_test_support::rustc_host; +use cargo_test_support::{basic_manifest, project}; + +#[cargo_test(nightly, reason = "plugins are unstable")] +fn plugin_to_the_max() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo_lib" + + [dependencies.bar] + path = "../bar" + "#, + ) + .file( + "src/main.rs", + r#" + #![feature(plugin)] + #![plugin(bar)] + extern crate foo_lib; + + fn main() { foo_lib::foo(); } + "#, + ) + .file( + "src/foo_lib.rs", + r#" + #![feature(plugin)] + #![plugin(bar)] + + pub fn foo() {} + "#, + ) + .build(); + let _bar = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + name = "bar" + plugin = true + + [dependencies.baz] + path = "../baz" + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(rustc_private)] + + extern crate baz; + extern crate rustc_driver; + + use rustc_driver::plugin::Registry; + + #[no_mangle] + pub fn __rustc_plugin_registrar(_reg: &mut Registry) { + println!("{}", baz::baz()); + } + "#, + ) + .build(); + let _baz = project() + .at("baz") + .file( + "Cargo.toml", + r#" + [package] + name = "baz" + version = "0.0.1" + authors = [] + + [lib] + name = "baz" + crate_type = ["dylib"] + "#, + ) + .file("src/lib.rs", "pub fn baz() -> i32 { 1 }") + .build(); + + foo.cargo("build").run(); + foo.cargo("doc").run(); +} + +#[cargo_test(nightly, reason = "plugins are unstable")] +fn plugin_with_dynamic_native_dependency() { + let build = project() + .at("builder") + .file( + "Cargo.toml", + r#" + [package] + name = "builder" + version = "0.0.1" + authors = [] + + [lib] + name = "builder" + crate-type = ["dylib"] + "#, + ) + .file("src/lib.rs", "#[no_mangle] pub extern fn foo() {}") + .build(); + + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file( + "src/main.rs", + r#" + #![feature(plugin)] + #![plugin(bar)] + + fn main() {} + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + build = 'build.rs' + + [lib] + name = "bar" + plugin = true + "#, + ) + .file( + "bar/build.rs", + r#" + use std::env; + use std::fs; + use std::path::PathBuf; + + fn main() { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let root = PathBuf::from(env::var("BUILDER_ROOT").unwrap()); + let file = format!("{}builder{}", + env::consts::DLL_PREFIX, + env::consts::DLL_SUFFIX); + let src = root.join(&file); + let dst = out_dir.join(&file); + fs::copy(src, dst).unwrap(); + if cfg!(target_env = "msvc") { + fs::copy(root.join("builder.dll.lib"), + out_dir.join("builder.dll.lib")).unwrap(); + } + println!("cargo:rustc-flags=-L {}", out_dir.display()); + } + "#, + ) + .file( + "bar/src/lib.rs", + r#" + #![feature(rustc_private)] + + extern crate rustc_driver; + use rustc_driver::plugin::Registry; + + #[cfg_attr(not(target_env = "msvc"), link(name = "builder"))] + #[cfg_attr(target_env = "msvc", link(name = "builder.dll"))] + extern { fn foo(); } + + #[no_mangle] + pub fn __rustc_plugin_registrar(_reg: &mut Registry) { + unsafe { foo() } + } + "#, + ) + .build(); + + build.cargo("build").run(); + + let root = build.root().join("target").join("debug"); + foo.cargo("build -v").env("BUILDER_ROOT", root).run(); +} + +#[cargo_test] +fn plugin_integration() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + + [lib] + name = "foo" + plugin = true + doctest = false + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .file("tests/it_works.rs", "") + .build(); + + p.cargo("test -v").run(); +} + +#[cargo_test] +fn doctest_a_plugin() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "#[macro_use] extern crate bar;") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + name = "bar" + plugin = true + "#, + ) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("test -v").run(); +} + +// See #1515 +#[cargo_test] +fn native_plugin_dependency_with_custom_linker() { + let target = rustc_host(); + + let _foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + plugin = true + "#, + ) + .file("src/lib.rs", "") + .build(); + + let bar = project() + .at("bar") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies.foo] + path = "../foo" + "#, + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + linker = "nonexistent-linker" + "#, + target + ), + ) + .build(); + + bar.cargo("build --verbose") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] -C linker=nonexistent-linker [..]` +[ERROR] [..]linker[..] +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "requires rustc_private")] +fn panic_abort_plugins() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.dev] + panic = 'abort' + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + plugin = true + "#, + ) + .file( + "bar/src/lib.rs", + r#" + #![feature(rustc_private)] + extern crate rustc_ast; + extern crate rustc_driver; + "#, + ) + .build(); + + p.cargo("build").run(); +} + +#[cargo_test(nightly, reason = "requires rustc_private")] +fn shared_panic_abort_plugins() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.dev] + panic = 'abort' + + [dependencies] + bar = { path = "bar" } + baz = { path = "baz" } + "#, + ) + .file("src/lib.rs", "extern crate baz;") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + plugin = true + + [dependencies] + baz = { path = "../baz" } + "#, + ) + .file( + "bar/src/lib.rs", + r#" + #![feature(rustc_private)] + extern crate rustc_ast; + extern crate rustc_driver; + extern crate baz; + "#, + ) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1")) + .file("baz/src/lib.rs", "") + .build(); + + p.cargo("build -v").run(); +} diff --git a/tests/testsuite/proc_macro.rs b/tests/testsuite/proc_macro.rs new file mode 100644 index 0000000..7d6f6ba --- /dev/null +++ b/tests/testsuite/proc_macro.rs @@ -0,0 +1,560 @@ +//! Tests for proc-macros. + +use cargo_test_support::project; + +#[cargo_test] +fn probe_cfg_before_crate_type_discovery() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [target.'cfg(not(stage300))'.dependencies.noop] + path = "../noop" + "#, + ) + .file( + "src/main.rs", + r#" + #[macro_use] + extern crate noop; + + #[derive(Noop)] + struct X; + + fn main() {} + "#, + ) + .build(); + let _noop = project() + .at("noop") + .file( + "Cargo.toml", + r#" + [package] + name = "noop" + version = "0.0.1" + authors = [] + + [lib] + proc-macro = true + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro_derive(Noop)] + pub fn noop(_input: TokenStream) -> TokenStream { + "".parse().unwrap() + } + "#, + ) + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn noop() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.noop] + path = "../noop" + "#, + ) + .file( + "src/main.rs", + r#" + #[macro_use] + extern crate noop; + + #[derive(Noop)] + struct X; + + fn main() {} + "#, + ) + .build(); + let _noop = project() + .at("noop") + .file( + "Cargo.toml", + r#" + [package] + name = "noop" + version = "0.0.1" + authors = [] + + [lib] + proc-macro = true + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro_derive(Noop)] + pub fn noop(_input: TokenStream) -> TokenStream { + "".parse().unwrap() + } + "#, + ) + .build(); + + p.cargo("check").run(); + p.cargo("check").run(); +} + +#[cargo_test] +fn impl_and_derive() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.transmogrify] + path = "../transmogrify" + "#, + ) + .file( + "src/main.rs", + r#" + #[macro_use] + extern crate transmogrify; + + trait ImplByTransmogrify { + fn impl_by_transmogrify(&self) -> bool; + } + + #[derive(Transmogrify, Debug)] + struct X { success: bool } + + fn main() { + let x = X::new(); + assert!(x.impl_by_transmogrify()); + println!("{:?}", x); + } + "#, + ) + .build(); + let _transmogrify = project() + .at("transmogrify") + .file( + "Cargo.toml", + r#" + [package] + name = "transmogrify" + version = "0.0.1" + authors = [] + + [lib] + proc-macro = true + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro_derive(Transmogrify)] + #[doc(hidden)] + pub fn transmogrify(input: TokenStream) -> TokenStream { + " + impl X { + fn new() -> Self { + X { success: true } + } + } + + impl ImplByTransmogrify for X { + fn impl_by_transmogrify(&self) -> bool { + true + } + } + ".parse().unwrap() + } + "#, + ) + .build(); + + p.cargo("build").run(); + p.cargo("run").with_stdout("X { success: true }").run(); +} + +#[cargo_test(nightly, reason = "plugins are unstable")] +fn plugin_and_proc_macro() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + plugin = true + proc-macro = true + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(rustc_private)] + #![feature(proc_macro, proc_macro_lib)] + + extern crate rustc_driver; + use rustc_driver::plugin::Registry; + + extern crate proc_macro; + use proc_macro::TokenStream; + + #[no_mangle] + pub fn __rustc_plugin_registrar(reg: &mut Registry) {} + + #[proc_macro_derive(Questionable)] + pub fn questionable(input: TokenStream) -> TokenStream { + input + } + "#, + ) + .build(); + + let msg = " `lib.plugin` and `lib.proc-macro` cannot both be `true`"; + p.cargo("check") + .with_status(101) + .with_stderr_contains(msg) + .run(); +} + +#[cargo_test] +fn proc_macro_doctest() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + [lib] + proc-macro = true + "#, + ) + .file( + "src/lib.rs", + r#" + #![crate_type = "proc-macro"] + + extern crate proc_macro; + + use proc_macro::TokenStream; + + /// ``` + /// assert!(true); + /// ``` + #[proc_macro_derive(Bar)] + pub fn derive(_input: TokenStream) -> TokenStream { + "".parse().unwrap() + } + + #[test] + fn a() { + assert!(true); + } + "#, + ) + .build(); + + foo.cargo("test") + .with_stdout_contains("test a ... ok") + .with_stdout_contains_n("test [..] ... ok", 2) + .run(); +} + +#[cargo_test] +fn proc_macro_crate_type() { + // Verify that `crate-type = ["proc-macro"]` is the same as `proc-macro = true` + // and that everything, including rustdoc, works correctly. + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + pm = { path = "pm" } + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! use foo::THING; + //! assert_eq!(THING, 123); + //! ``` + #[macro_use] + extern crate pm; + #[derive(MkItem)] + pub struct S; + #[cfg(test)] + mod tests { + use super::THING; + #[test] + fn it_works() { + assert_eq!(THING, 123); + } + } + "#, + ) + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + [lib] + crate-type = ["proc-macro"] + "#, + ) + .file( + "pm/src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro_derive(MkItem)] + pub fn mk_item(_input: TokenStream) -> TokenStream { + "pub const THING: i32 = 123;".parse().unwrap() + } + "#, + ) + .build(); + + foo.cargo("test") + .with_stdout_contains("test tests::it_works ... ok") + .with_stdout_contains_n("test [..] ... ok", 2) + .run(); +} + +#[cargo_test] +fn proc_macro_crate_type_warning() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [lib] + crate-type = ["proc-macro"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + foo.cargo("check") + .with_stderr_contains( + "[WARNING] library `foo` should only specify `proc-macro = true` instead of setting `crate-type`") + .run(); +} + +#[cargo_test] +fn proc_macro_conflicting_warning() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [lib] + proc-macro = false + proc_macro = true + "#, + ) + .file("src/lib.rs", "") + .build(); + + foo.cargo("check") + .with_stderr_contains( +"[WARNING] conflicting between `proc-macro` and `proc_macro` in the `foo` library target.\n + `proc_macro` is ignored and not recommended for use in the future", + ) + .run(); +} + +#[cargo_test] +fn proc_macro_crate_type_warning_plugin() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [lib] + crate-type = ["proc-macro"] + plugin = true + "#, + ) + .file("src/lib.rs", "") + .build(); + + foo.cargo("check") + .with_stderr_contains( + "[WARNING] proc-macro library `foo` should not specify `plugin = true`") + .with_stderr_contains( + "[WARNING] library `foo` should only specify `proc-macro = true` instead of setting `crate-type`") + .run(); +} + +#[cargo_test] +fn proc_macro_crate_type_multiple() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [lib] + crate-type = ["proc-macro", "rlib"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + foo.cargo("check") + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + cannot mix `proc-macro` crate type with others +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn proc_macro_extern_prelude() { + // Check that proc_macro is in the extern prelude. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + [lib] + proc-macro = true + "#, + ) + .file( + "src/lib.rs", + r#" + use proc_macro::TokenStream; + #[proc_macro] + pub fn foo(input: TokenStream) -> TokenStream { + "".parse().unwrap() + } + "#, + ) + .build(); + p.cargo("test").run(); + p.cargo("doc").run(); +} + +#[cargo_test] +fn proc_macro_built_once() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ['a', 'b'] + resolver = "2" + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [build-dependencies] + the-macro = { path = '../the-macro' } + "#, + ) + .file("a/build.rs", "fn main() {}") + .file("a/src/main.rs", "fn main() {}") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [dependencies] + the-macro = { path = '../the-macro', features = ['a'] } + "#, + ) + .file("b/src/main.rs", "fn main() {}") + .file( + "the-macro/Cargo.toml", + r#" + [package] + name = "the-macro" + version = "0.1.0" + + [lib] + proc_macro = true + + [features] + a = [] + "#, + ) + .file("the-macro/src/lib.rs", "") + .build(); + p.cargo("build --verbose") + .with_stderr_unordered( + "\ +[COMPILING] the-macro [..] +[RUNNING] `rustc --crate-name the_macro [..]` +[COMPILING] b [..] +[RUNNING] `rustc --crate-name b [..]` +[COMPILING] a [..] +[RUNNING] `rustc --crate-name build_script_build [..]` +[RUNNING] `[..]build[..]script[..]build[..]` +[RUNNING] `rustc --crate-name a [..]` +[FINISHED] [..] +", + ) + .run(); +} diff --git a/tests/testsuite/profile_config.rs b/tests/testsuite/profile_config.rs new file mode 100644 index 0000000..c59ed7a --- /dev/null +++ b/tests/testsuite/profile_config.rs @@ -0,0 +1,519 @@ +//! Tests for profiles defined in config files. + +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_lib_manifest, paths, project}; + +// TODO: this should be remove once -Zprofile-rustflags is stabilized +#[cargo_test] +fn rustflags_works_with_zflag() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config.toml", + r#" + [profile.dev] + rustflags = ["-C", "link-dead-code=yes"] + "#, + ) + .build(); + + p.cargo("check -v") + .masquerade_as_nightly_cargo(&["profile-rustflags"]) + .with_status(101) + .with_stderr_contains("[..]feature `profile-rustflags` is required[..]") + .run(); + + p.cargo("check -v -Zprofile-rustflags") + .masquerade_as_nightly_cargo(&["profile-rustflags"]) + .with_stderr( + "\ +[CHECKING] foo [..] +[RUNNING] `rustc --crate-name foo [..] -C link-dead-code=yes [..] +[FINISHED] [..] +", + ) + .run(); + + p.change_file( + ".cargo/config.toml", + r#" + [unstable] + profile-rustflags = true + + [profile.dev] + rustflags = ["-C", "link-dead-code=yes"] + "#, + ); + + p.cargo("check -v") + .masquerade_as_nightly_cargo(&["profile-rustflags"]) + .with_stderr( + "\ +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_config_validate_warnings() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [profile.test] + opt-level = 3 + + [profile.asdf] + opt-level = 3 + + [profile.dev] + bad-key = true + + [profile.dev.build-override] + bad-key-bo = true + + [profile.dev.package.bar] + bad-key-bar = true + "#, + ) + .build(); + + p.cargo("build") + .with_stderr_unordered( + "\ +[WARNING] unused config key `profile.dev.bad-key` in `[..].cargo/config` +[WARNING] unused config key `profile.dev.package.bar.bad-key-bar` in `[..].cargo/config` +[WARNING] unused config key `profile.dev.build-override.bad-key-bo` in `[..].cargo/config` +[COMPILING] foo [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_config_error_paths() { + // Errors in config show where the error is located. + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [profile.dev] + opt-level = 3 + "#, + ) + .file( + paths::home().join(".cargo/config"), + r#" + [profile.dev] + rpath = "foo" + "#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] error in [..]/foo/.cargo/config: could not load config key `profile.dev` + +Caused by: + error in [..]/home/.cargo/config: `profile.dev.rpath` expected true/false, but found a string +", + ) + .run(); +} + +#[cargo_test] +fn profile_config_validate_errors() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [profile.dev.package.foo] + panic = "abort" + "#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] config profile `dev` is not valid (defined in `[..]/foo/.cargo/config`) + +Caused by: + `panic` may not be specified in a `package` profile +", + ) + .run(); +} + +#[cargo_test] +fn profile_config_syntax_errors() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [profile.dev] + codegen-units = "foo" + "#, + ) + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] error in [..]/.cargo/config: could not load config key `profile.dev` + +Caused by: + error in [..]/foo/.cargo/config: `profile.dev.codegen-units` expected an integer, but found a string +", + ) + .run(); +} + +#[cargo_test] +fn profile_config_override_spec_multiple() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file( + ".cargo/config", + r#" + [profile.dev.package.bar] + opt-level = 3 + + [profile.dev.package."bar:0.5.0"] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + // Unfortunately this doesn't tell you which file, hopefully it's not too + // much of a problem. + p.cargo("build -v") + .with_status(101) + .with_stderr( + "\ +[ERROR] multiple package overrides in profile `dev` match package `bar v0.5.0 ([..])` +found package specs: bar, bar@0.5.0", + ) + .run(); +} + +#[cargo_test] +fn profile_config_all_options() { + // Ensure all profile options are supported. + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [profile.release] + opt-level = 1 + debug = true + debug-assertions = true + overflow-checks = false + rpath = true + lto = true + codegen-units = 2 + panic = "abort" + incremental = true + "#, + ) + .build(); + + p.cargo("build --release -v") + .env_remove("CARGO_INCREMENTAL") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..] \ + -C opt-level=1 \ + -C panic=abort \ + -C lto[..]\ + -C codegen-units=2 \ + -C debuginfo=2 \ + -C debug-assertions=on \ + -C overflow-checks=off [..]\ + -C rpath [..]\ + -C incremental=[..] +[FINISHED] release [optimized + debuginfo] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_config_override_precedence() { + // Config values take precedence over manifest values. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = {path = "bar"} + + [profile.dev] + codegen-units = 2 + + [profile.dev.package.bar] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .file( + ".cargo/config", + r#" + [profile.dev.package.bar] + opt-level = 2 + "#, + ) + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] bar [..] +[RUNNING] `rustc --crate-name bar [..] -C opt-level=2[..]-C codegen-units=2 [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..]-C codegen-units=2 [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn profile_config_no_warn_unknown_override() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [profile.dev.package.bar] + codegen-units = 4 + "#, + ) + .build(); + + p.cargo("build") + .with_stderr_does_not_contain("[..]warning[..]") + .run(); +} + +#[cargo_test] +fn profile_config_mixed_types() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [profile.dev] + opt-level = 3 + "#, + ) + .file( + paths::home().join(".cargo/config"), + r#" + [profile.dev] + opt-level = 's' + "#, + ) + .build(); + + p.cargo("build -v") + .with_stderr_contains("[..]-C opt-level=3 [..]") + .run(); +} + +#[cargo_test] +fn named_config_profile() { + // Exercises config named profies. + // foo -> middle -> bar -> dev + // middle exists in Cargo.toml, the others in .cargo/config + use super::config::ConfigBuilder; + use cargo::core::compiler::CompileKind; + use cargo::core::profiles::{Profiles, UnitFor}; + use cargo::core::{PackageId, Workspace}; + use cargo::util::interning::InternedString; + use std::fs; + paths::root().join(".cargo").mkdir_p(); + fs::write( + paths::root().join(".cargo/config"), + r#" + [profile.foo] + inherits = "middle" + codegen-units = 2 + [profile.foo.build-override] + codegen-units = 6 + [profile.foo.package.dep] + codegen-units = 7 + + [profile.middle] + inherits = "bar" + codegen-units = 3 + + [profile.bar] + inherits = "dev" + codegen-units = 4 + debug = 1 + "#, + ) + .unwrap(); + fs::write( + paths::root().join("Cargo.toml"), + r#" + [workspace] + + [profile.middle] + inherits = "bar" + codegen-units = 1 + opt-level = 1 + [profile.middle.package.dep] + overflow-checks = false + + [profile.foo.build-override] + codegen-units = 5 + debug-assertions = false + [profile.foo.package.dep] + codegen-units = 8 + "#, + ) + .unwrap(); + let config = ConfigBuilder::new().build(); + let profile_name = InternedString::new("foo"); + let ws = Workspace::new(&paths::root().join("Cargo.toml"), &config).unwrap(); + let profiles = Profiles::new(&ws, profile_name).unwrap(); + + let crates_io = cargo::core::source::SourceId::crates_io(&config).unwrap(); + let a_pkg = PackageId::new("a", "0.1.0", crates_io).unwrap(); + let dep_pkg = PackageId::new("dep", "0.1.0", crates_io).unwrap(); + + // normal package + let kind = CompileKind::Host; + let p = profiles.get_profile(a_pkg, true, true, UnitFor::new_normal(kind), kind); + assert_eq!(p.name, "foo"); + assert_eq!(p.codegen_units, Some(2)); // "foo" from config + assert_eq!(p.opt_level, "1"); // "middle" from manifest + assert_eq!(p.debuginfo.to_option(), Some(1)); // "bar" from config + assert_eq!(p.debug_assertions, true); // "dev" built-in (ignore build-override) + assert_eq!(p.overflow_checks, true); // "dev" built-in (ignore package override) + + // build-override + let bo = profiles.get_profile(a_pkg, true, true, UnitFor::new_host(false, kind), kind); + assert_eq!(bo.name, "foo"); + assert_eq!(bo.codegen_units, Some(6)); // "foo" build override from config + assert_eq!(bo.opt_level, "0"); // default to zero + assert_eq!(bo.debuginfo.to_option(), Some(1)); // SAME as normal + assert_eq!(bo.debug_assertions, false); // "foo" build override from manifest + assert_eq!(bo.overflow_checks, true); // SAME as normal + + // package overrides + let po = profiles.get_profile(dep_pkg, false, true, UnitFor::new_normal(kind), kind); + assert_eq!(po.name, "foo"); + assert_eq!(po.codegen_units, Some(7)); // "foo" package override from config + assert_eq!(po.opt_level, "1"); // SAME as normal + assert_eq!(po.debuginfo.to_option(), Some(1)); // SAME as normal + assert_eq!(po.debug_assertions, true); // SAME as normal + assert_eq!(po.overflow_checks, false); // "middle" package override from manifest +} + +#[cargo_test] +fn named_env_profile() { + // Environment variables used to define a named profile. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v --profile=other") + .env("CARGO_PROFILE_OTHER_CODEGEN_UNITS", "1") + .env("CARGO_PROFILE_OTHER_INHERITS", "dev") + .with_stderr_contains("[..]-C codegen-units=1 [..]") + .run(); +} + +#[cargo_test] +fn test_with_dev_profile() { + // The `test` profile inherits from `dev` for both local crates and + // dependencies. + Package::new("somedep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + somedep = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("test --lib --no-run -v") + .env("CARGO_PROFILE_DEV_DEBUG", "0") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] [..] +[DOWNLOADED] [..] +[COMPILING] somedep v1.0.0 +[RUNNING] `rustc --crate-name somedep [..]-C debuginfo=0[..] +[COMPILING] foo v0.1.0 [..] +[RUNNING] `rustc --crate-name foo [..]-C debuginfo=0[..] +[FINISHED] [..] +[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` +", + ) + .run(); +} diff --git a/tests/testsuite/profile_custom.rs b/tests/testsuite/profile_custom.rs new file mode 100644 index 0000000..ea6b54c --- /dev/null +++ b/tests/testsuite/profile_custom.rs @@ -0,0 +1,731 @@ +//! Tests for named profiles. + +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::{basic_lib_manifest, project}; + +#[cargo_test] +fn inherits_on_release() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.release] + inherits = "dev" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] `inherits` must not be specified in root profile `release` +", + ) + .run(); +} + +#[cargo_test] +fn missing_inherits() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.release-lto] + codegen-units = 7 + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] profile `release-lto` is missing an `inherits` directive \ + (`inherits` is required for all profiles except `dev` or `release`) +", + ) + .run(); +} + +#[cargo_test] +fn invalid_profile_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.'.release-lto'] + inherits = "release" + codegen-units = 7 + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at [..] + +Caused by: + invalid character `.` in profile name `.release-lto` + Allowed characters are letters, numbers, underscore, and hyphen. +", + ) + .run(); +} + +#[cargo_test] +// We are currently uncertain if dir-name will ever be exposed to the user. +// The code for it still roughly exists, but only for the internal profiles. +// This test was kept in case we ever want to enable support for it again. +#[ignore = "dir-name is disabled"] +fn invalid_dir_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.'release-lto'] + inherits = "release" + dir-name = ".subdir" + codegen-units = 7 + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at [..] + +Caused by: + Invalid character `.` in dir-name: `.subdir`", + ) + .run(); +} + +#[cargo_test] +fn dir_name_disabled() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.release-lto] + inherits = "release" + dir-name = "lto" + lto = true + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + dir-name=\"lto\" in profile `release-lto` is not currently allowed, \ + directory names are tied to the profile name for custom profiles +", + ) + .run(); +} + +#[cargo_test] +fn invalid_inherits() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.'release-lto'] + inherits = ".release" + codegen-units = 7 + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "error: profile `release-lto` inherits from `.release`, \ + but that profile is not defined", + ) + .run(); +} + +#[cargo_test] +fn non_existent_inherits() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.release-lto] + codegen-units = 7 + inherits = "non-existent" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] profile `release-lto` inherits from `non-existent`, but that profile is not defined +", + ) + .run(); +} + +#[cargo_test] +fn self_inherits() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.release-lto] + codegen-units = 7 + inherits = "release-lto" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] profile inheritance loop detected with profile `release-lto` inheriting `release-lto` +", + ) + .run(); +} + +#[cargo_test] +fn inherits_loop() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.release-lto] + codegen-units = 7 + inherits = "release-lto2" + + [profile.release-lto2] + codegen-units = 7 + inherits = "release-lto" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +[ERROR] profile inheritance loop detected with profile `release-lto2` inheriting `release-lto` +", + ) + .run(); +} + +#[cargo_test] +fn overrides_with_custom() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + xxx = {path = "xxx"} + yyy = {path = "yyy"} + + [profile.dev] + codegen-units = 7 + + [profile.dev.package.xxx] + codegen-units = 5 + [profile.dev.package.yyy] + codegen-units = 3 + + [profile.other] + inherits = "dev" + codegen-units = 2 + + [profile.other.package.yyy] + codegen-units = 6 + "#, + ) + .file("src/lib.rs", "") + .file("xxx/Cargo.toml", &basic_lib_manifest("xxx")) + .file("xxx/src/lib.rs", "") + .file("yyy/Cargo.toml", &basic_lib_manifest("yyy")) + .file("yyy/src/lib.rs", "") + .build(); + + // profile overrides are inherited between profiles using inherits and have a + // higher priority than profile options provided by custom profiles + p.cargo("build -v") + .with_stderr_unordered( + "\ +[COMPILING] xxx [..] +[COMPILING] yyy [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name xxx [..] -C codegen-units=5 [..]` +[RUNNING] `rustc --crate-name yyy [..] -C codegen-units=3 [..]` +[RUNNING] `rustc --crate-name foo [..] -C codegen-units=7 [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + // This also verifies that the custom profile names appears in the finished line. + p.cargo("build --profile=other -v") + .with_stderr_unordered( + "\ +[COMPILING] xxx [..] +[COMPILING] yyy [..] +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name xxx [..] -C codegen-units=5 [..]` +[RUNNING] `rustc --crate-name yyy [..] -C codegen-units=6 [..]` +[RUNNING] `rustc --crate-name foo [..] -C codegen-units=2 [..]` +[FINISHED] other [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn conflicting_usage() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --profile=dev --release") + .with_status(101) + .with_stderr( + "\ +error: conflicting usage of --profile=dev and --release +The `--release` flag is the same as `--profile=release`. +Remove one flag or the other to continue. +", + ) + .run(); + + p.cargo("install --profile=release --debug") + .with_status(101) + .with_stderr( + "\ +error: conflicting usage of --profile=release and --debug +The `--debug` flag is the same as `--profile=dev`. +Remove one flag or the other to continue. +", + ) + .run(); + + p.cargo("rustc --profile=dev --release") + .with_stderr( + "\ +warning: the `--release` flag should not be specified with the `--profile` flag +The `--release` flag will be ignored. +This was historically accepted, but will become an error in a future release. +[COMPILING] foo [..] +[FINISHED] dev [..] +", + ) + .run(); + + p.cargo("check --profile=dev --release") + .with_status(101) + .with_stderr( + "\ +error: conflicting usage of --profile=dev and --release +The `--release` flag is the same as `--profile=release`. +Remove one flag or the other to continue. +", + ) + .run(); + + p.cargo("check --profile=test --release") + .with_stderr( + "\ +warning: the `--release` flag should not be specified with the `--profile` flag +The `--release` flag will be ignored. +This was historically accepted, but will become an error in a future release. +[CHECKING] foo [..] +[FINISHED] test [..] +", + ) + .run(); + + // This is OK since the two are the same. + p.cargo("rustc --profile=release --release") + .with_stderr( + "\ +[COMPILING] foo [..] +[FINISHED] release [..] +", + ) + .run(); + + p.cargo("build --profile=release --release") + .with_stderr( + "\ +[FINISHED] release [..] +", + ) + .run(); + + p.cargo("install --path . --profile=dev --debug") + .with_stderr( + "\ +[INSTALLING] foo [..] +[FINISHED] dev [..] +[INSTALLING] [..] +[INSTALLED] [..] +[WARNING] be sure to add [..] +", + ) + .run(); + + p.cargo("install --path . --profile=release --debug") + .with_status(101) + .with_stderr( + "\ +error: conflicting usage of --profile=release and --debug +The `--debug` flag is the same as `--profile=dev`. +Remove one flag or the other to continue. +", + ) + .run(); +} + +#[cargo_test] +fn clean_custom_dirname() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.other] + inherits = "release" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --release") + .with_stdout("") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] release [optimized] target(s) in [..] +", + ) + .run(); + + p.cargo("clean -p foo").run(); + + p.cargo("build --release") + .with_stdout("") + .with_stderr( + "\ +[FINISHED] release [optimized] target(s) in [..] +", + ) + .run(); + + p.cargo("clean -p foo --release").run(); + + p.cargo("build --release") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] release [optimized] target(s) in [..] +", + ) + .run(); + + p.cargo("build") + .with_stdout("") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("build --profile=other") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] other [optimized] target(s) in [..] +", + ) + .run(); + + p.cargo("clean").arg("--release").run(); + + // Make sure that 'other' was not cleaned + assert!(p.build_dir().is_dir()); + assert!(p.build_dir().join("debug").is_dir()); + assert!(p.build_dir().join("other").is_dir()); + assert!(!p.build_dir().join("release").is_dir()); + + // This should clean 'other' + p.cargo("clean --profile=other").with_stderr("").run(); + assert!(p.build_dir().join("debug").is_dir()); + assert!(!p.build_dir().join("other").is_dir()); +} + +#[cargo_test] +fn unknown_profile() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build --profile alpha") + .with_stderr("[ERROR] profile `alpha` is not defined") + .with_status(101) + .run(); + // Clean has a separate code path, need to check it too. + p.cargo("clean --profile alpha") + .with_stderr("[ERROR] profile `alpha` is not defined") + .with_status(101) + .run(); +} + +#[cargo_test] +fn reserved_profile_names() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.doc] + opt-level = 1 + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build --profile=doc") + .with_status(101) + .with_stderr("error: profile `doc` is reserved and not allowed to be explicitly specified") + .run(); + // Not an exhaustive list, just a sample. + for name in ["build", "cargo", "check", "rustc", "CaRgO_startswith"] { + p.cargo(&format!("build --profile={}", name)) + .with_status(101) + .with_stderr(&format!( + "\ +error: profile name `{}` is reserved +Please choose a different name. +See https://doc.rust-lang.org/cargo/reference/profiles.html for more on configuring profiles. +", + name + )) + .run(); + } + for name in ["build", "check", "cargo", "rustc", "CaRgO_startswith"] { + p.change_file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.{}] + opt-level = 1 + "#, + name + ), + ); + + p.cargo("build") + .with_status(101) + .with_stderr(&format!( + "\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + profile name `{}` is reserved + Please choose a different name. + See https://doc.rust-lang.org/cargo/reference/profiles.html for more on configuring profiles. +", + name + )) + .run(); + } + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [profile.debug] + debug = 1 + inherits = "dev" + "#, + ); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + profile name `debug` is reserved + To configure the default development profile, use the name `dev` as in [profile.dev] + See https://doc.rust-lang.org/cargo/reference/profiles.html for more on configuring profiles. +", + ) + .run(); +} + +#[cargo_test] +fn legacy_commands_support_custom() { + // These commands have had `--profile` before custom named profiles. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.super-dev] + codegen-units = 3 + inherits = "dev" + "#, + ) + .file("src/lib.rs", "") + .build(); + + for command in ["rustc", "fix", "check"] { + let mut pb = p.cargo(command); + if command == "fix" { + pb.arg("--allow-no-vcs"); + } + pb.arg("--profile=super-dev") + .arg("-v") + .with_stderr_contains("[RUNNING] [..]codegen-units=3[..]") + .run(); + p.build_dir().rm_rf(); + } +} + +#[cargo_test] +fn legacy_rustc() { + // `cargo rustc` historically has supported dev/test/bench/check + // other profiles are covered in check::rustc_check + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.dev] + codegen-units = 3 + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("rustc --profile dev -v") + .with_stderr( + "\ +[COMPILING] foo v0.1.0 [..] +[RUNNING] `rustc --crate-name foo [..]-C codegen-units=3[..] +[FINISHED] [..] +", + ) + .run(); +} diff --git a/tests/testsuite/profile_overrides.rs b/tests/testsuite/profile_overrides.rs new file mode 100644 index 0000000..dc9bafb --- /dev/null +++ b/tests/testsuite/profile_overrides.rs @@ -0,0 +1,515 @@ +//! Tests for profile overrides (build-override and per-package overrides). + +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_lib_manifest, basic_manifest, project}; + +#[cargo_test] +fn profile_override_basic() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = {path = "bar"} + + [profile.dev] + opt-level = 1 + + [profile.dev.package.bar] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_stderr( + "[CHECKING] bar [..] +[RUNNING] `rustc --crate-name bar [..] -C opt-level=3 [..]` +[CHECKING] foo [..] +[RUNNING] `rustc --crate-name foo [..] -C opt-level=1 [..]` +[FINISHED] dev [optimized + debuginfo] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn profile_override_warnings() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = {path = "bar"} + + [profile.dev.package.bart] + opt-level = 3 + + [profile.dev.package.no-suggestion] + opt-level = 3 + + [profile.dev.package."bar:1.2.3"] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("build") + .with_stderr_contains( + "\ +[WARNING] profile package spec `bar@1.2.3` in profile `dev` \ + has a version or URL that does not match any of the packages: \ + bar v0.5.0 ([..]/foo/bar) +[WARNING] profile package spec `bart` in profile `dev` did not match any packages + +Did you mean `bar`? +[WARNING] profile package spec `no-suggestion` in profile `dev` did not match any packages +[COMPILING] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_override_bad_settings() { + let bad_values = [ + ( + "panic = \"abort\"", + "`panic` may not be specified in a `package` profile", + ), + ( + "lto = true", + "`lto` may not be specified in a `package` profile", + ), + ( + "rpath = true", + "`rpath` may not be specified in a `package` profile", + ), + ("package = {}", "package-specific profiles cannot be nested"), + ]; + for &(snippet, expected) in bad_values.iter() { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = {{path = "bar"}} + + [profile.dev.package.bar] + {} + "#, + snippet + ), + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains(format!("Caused by:\n {}", expected)) + .run(); + } +} + +#[cargo_test] +fn profile_override_hierarchy() { + // Test that the precedence rules are correct for different types. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["m1", "m2", "m3"] + + [profile.dev] + codegen-units = 1 + + [profile.dev.package.m2] + codegen-units = 2 + + [profile.dev.package."*"] + codegen-units = 3 + + [profile.dev.build-override] + codegen-units = 4 + "#, + ) + // m1 + .file( + "m1/Cargo.toml", + r#" + [package] + name = "m1" + version = "0.0.1" + + [dependencies] + m2 = { path = "../m2" } + dep = { path = "../../dep" } + "#, + ) + .file("m1/src/lib.rs", "extern crate m2; extern crate dep;") + .file("m1/build.rs", "fn main() {}") + // m2 + .file( + "m2/Cargo.toml", + r#" + [package] + name = "m2" + version = "0.0.1" + + [dependencies] + m3 = { path = "../m3" } + + [build-dependencies] + m3 = { path = "../m3" } + dep = { path = "../../dep" } + "#, + ) + .file("m2/src/lib.rs", "extern crate m3;") + .file( + "m2/build.rs", + "extern crate m3; extern crate dep; fn main() {}", + ) + // m3 + .file("m3/Cargo.toml", &basic_lib_manifest("m3")) + .file("m3/src/lib.rs", "") + .build(); + + // dep (outside of workspace) + let _dep = project() + .at("dep") + .file("Cargo.toml", &basic_lib_manifest("dep")) + .file("src/lib.rs", "") + .build(); + + // Profiles should be: + // m3: 4 (as build.rs dependency) + // m3: 1 (as [profile.dev] as workspace member) + // dep: 3 (as [profile.dev.package."*"] as non-workspace member) + // m1 build.rs: 4 (as [profile.dev.build-override]) + // m2 build.rs: 2 (as [profile.dev.package.m2]) + // m2: 2 (as [profile.dev.package.m2]) + // m1: 1 (as [profile.dev]) + + p.cargo("build -v").with_stderr_unordered("\ +[COMPILING] m3 [..] +[COMPILING] dep [..] +[RUNNING] `rustc --crate-name m3 m3/src/lib.rs [..] --crate-type lib --emit=[..]link[..]-C codegen-units=4 [..] +[RUNNING] `rustc --crate-name dep [..]dep/src/lib.rs [..] --crate-type lib --emit=[..]link[..]-C codegen-units=3 [..] +[RUNNING] `rustc --crate-name m3 m3/src/lib.rs [..] --crate-type lib --emit=[..]link[..]-C codegen-units=1 [..] +[RUNNING] `rustc --crate-name build_script_build m1/build.rs [..] --crate-type bin --emit=[..]link[..]-C codegen-units=4 [..] +[COMPILING] m2 [..] +[RUNNING] `rustc --crate-name build_script_build m2/build.rs [..] --crate-type bin --emit=[..]link[..]-C codegen-units=2 [..] +[RUNNING] `[..]/m1-[..]/build-script-build` +[RUNNING] `[..]/m2-[..]/build-script-build` +[RUNNING] `rustc --crate-name m2 m2/src/lib.rs [..] --crate-type lib --emit=[..]link[..]-C codegen-units=2 [..] +[COMPILING] m1 [..] +[RUNNING] `rustc --crate-name m1 m1/src/lib.rs [..] --crate-type lib --emit=[..]link[..]-C codegen-units=1 [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_override_spec_multiple() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = { path = "bar" } + + [profile.dev.package.bar] + opt-level = 3 + + [profile.dev.package."bar:0.5.0"] + opt-level = 3 + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_status(101) + .with_stderr_contains( + "\ +[ERROR] multiple package overrides in profile `dev` match package `bar v0.5.0 ([..])` +found package specs: bar, bar@0.5.0", + ) + .run(); +} + +#[cargo_test] +fn profile_override_spec() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["m1", "m2"] + + [profile.dev.package."dep:1.0.0"] + codegen-units = 1 + + [profile.dev.package."dep:2.0.0"] + codegen-units = 2 + "#, + ) + // m1 + .file( + "m1/Cargo.toml", + r#" + [package] + name = "m1" + version = "0.0.1" + + [dependencies] + dep = { path = "../../dep1" } + "#, + ) + .file("m1/src/lib.rs", "extern crate dep;") + // m2 + .file( + "m2/Cargo.toml", + r#" + [package] + name = "m2" + version = "0.0.1" + + [dependencies] + dep = {path = "../../dep2" } + "#, + ) + .file("m2/src/lib.rs", "extern crate dep;") + .build(); + + project() + .at("dep1") + .file("Cargo.toml", &basic_manifest("dep", "1.0.0")) + .file("src/lib.rs", "") + .build(); + + project() + .at("dep2") + .file("Cargo.toml", &basic_manifest("dep", "2.0.0")) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_stderr_contains("[RUNNING] `rustc [..]dep1/src/lib.rs [..] -C codegen-units=1 [..]") + .with_stderr_contains("[RUNNING] `rustc [..]dep2/src/lib.rs [..] -C codegen-units=2 [..]") + .run(); +} + +#[cargo_test] +fn override_proc_macro() { + Package::new("shared", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + shared = "1.0" + pm = {path = "pm"} + + [profile.dev.build-override] + codegen-units = 4 + "#, + ) + .file("src/lib.rs", r#"pm::eat!{}"#) + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + + [dependencies] + shared = "1.0" + "#, + ) + .file( + "pm/src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro] + pub fn eat(_item: TokenStream) -> TokenStream { + "".parse().unwrap() + } + "#, + ) + .build(); + + p.cargo("check -v") + // Shared built for the proc-macro. + .with_stderr_contains("[RUNNING] `rustc [..]--crate-name shared [..]-C codegen-units=4[..]") + // Shared built for the library. + .with_stderr_line_without( + &["[RUNNING] `rustc --crate-name shared"], + &["-C codegen-units"], + ) + .with_stderr_contains("[RUNNING] `rustc [..]--crate-name pm [..]-C codegen-units=4[..]") + .with_stderr_line_without( + &["[RUNNING] `rustc [..]--crate-name foo"], + &["-C codegen-units"], + ) + .run(); +} + +#[cargo_test] +fn no_warning_ws() { + // https://github.com/rust-lang/cargo/issues/7378, avoid warnings in a workspace. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + + [profile.dev.package.a] + codegen-units = 3 + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/lib.rs", "") + .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("check -p b") + .with_stderr( + "\ +[CHECKING] b [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_override_shared() { + // A dependency with a build script that is shared with a build + // dependency, using different profile settings. That is: + // + // foo DEBUG=2 + // ├── common DEBUG=2 + // │ └── common Run build.rs DEBUG=2 + // │ └── common build.rs DEBUG=0 (build_override) + // └── foo Run build.rs DEBUG=2 + // └── foo build.rs DEBUG=0 (build_override) + // └── common DEBUG=0 (build_override) + // └── common Run build.rs DEBUG=0 (build_override) + // └── common build.rs DEBUG=0 (build_override) + // + // The key part here is that `common` RunCustomBuild is run twice, once + // with DEBUG=2 (as a dependency of foo) and once with DEBUG=0 (as a + // build-dependency of foo's build script). + Package::new("common", "1.0.0") + .file( + "build.rs", + r#" + fn main() { + if std::env::var("DEBUG").unwrap() != "false" { + println!("cargo:rustc-cfg=foo_debug"); + } else { + println!("cargo:rustc-cfg=foo_release"); + } + } + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn foo() -> u32 { + if cfg!(foo_debug) { + assert!(cfg!(debug_assertions)); + 1 + } else if cfg!(foo_release) { + assert!(!cfg!(debug_assertions)); + 2 + } else { + panic!("not set"); + } + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [build-dependencies] + common = "1.0" + + [dependencies] + common = "1.0" + + [profile.dev.build-override] + debug = 0 + debug-assertions = false + "#, + ) + .file( + "build.rs", + r#" + fn main() { + assert_eq!(common::foo(), 2); + } + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + assert_eq!(common::foo(), 1); + } + "#, + ) + .build(); + + p.cargo("run").run(); +} diff --git a/tests/testsuite/profile_targets.rs b/tests/testsuite/profile_targets.rs new file mode 100644 index 0000000..b323597 --- /dev/null +++ b/tests/testsuite/profile_targets.rs @@ -0,0 +1,674 @@ +//! Tests for checking exactly how profiles correspond with each unit. For +//! example, the `test` profile applying to test targets, but not other +//! targets, etc. + +use cargo_test_support::{basic_manifest, project, Project}; + +fn all_target_project() -> Project { + // This abuses the `codegen-units` setting so that we can verify exactly + // which profile is used for each compiler invocation. + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = { path = "bar" } + + [build-dependencies] + bdep = { path = "bdep" } + + [profile.dev] + codegen-units = 1 + panic = "abort" + [profile.release] + codegen-units = 2 + panic = "abort" + [profile.test] + codegen-units = 3 + [profile.bench] + codegen-units = 4 + [profile.dev.build-override] + codegen-units = 5 + [profile.release.build-override] + codegen-units = 6 + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file("src/main.rs", "extern crate foo; fn main() {}") + .file("examples/ex1.rs", "extern crate foo; fn main() {}") + .file("tests/test1.rs", "extern crate foo;") + .file("benches/bench1.rs", "extern crate foo;") + .file( + "build.rs", + r#" + extern crate bdep; + fn main() { + eprintln!("foo custom build PROFILE={} DEBUG={} OPT_LEVEL={}", + std::env::var("PROFILE").unwrap(), + std::env::var("DEBUG").unwrap(), + std::env::var("OPT_LEVEL").unwrap(), + ); + } + "#, + ) + // `bar` package. + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + // `bdep` package. + .file( + "bdep/Cargo.toml", + r#" + [package] + name = "bdep" + version = "0.0.1" + + [dependencies] + bar = { path = "../bar" } + "#, + ) + .file("bdep/src/lib.rs", "extern crate bar;") + .build() +} + +#[cargo_test] +fn profile_selection_build() { + let p = all_target_project(); + + // `build` + // NOTES: + // - bdep `panic` is not set because it thinks `build.rs` is a plugin. + // - build_script_build is built without panic because it thinks `build.rs` is a plugin. + // - We make sure that the build dependencies bar, bdep, and build.rs + // are built without debuginfo. + p.cargo("build -vv") + .with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[COMPILING] bdep [..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..] +[RUNNING] `[..]/target/debug/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +" + ) + .with_stderr_line_without(&["[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]-C codegen-units=5 [..]"], &["-C debuginfo"]) + .with_stderr_line_without(&["[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]-C codegen-units=5 [..]"], &["-C debuginfo"]) + .with_stderr_line_without(&["[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]-C codegen-units=5 [..]"], &["-C debuginfo"]) + .run(); + p.cargo("build -vv") + .with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_selection_build_release() { + let p = all_target_project(); + + // `build --release` + p.cargo("build --release -vv").with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] +[COMPILING] bdep [..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=6 [..] +[RUNNING] `[..]/target/release/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[FINISHED] release [optimized] [..] +").run(); + p.cargo("build --release -vv") + .with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] release [optimized] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_selection_build_all_targets() { + let p = all_target_project(); + // `build` + // NOTES: + // - bdep `panic` is not set because it thinks `build.rs` is a plugin. + // - build_script_build is built without panic because it thinks + // `build.rs` is a plugin. + // - Benchmark dependencies are compiled in `dev` mode, which may be + // surprising. See issue rust-lang/cargo#4929. + // - We make sure that the build dependencies bar, bdep, and build.rs + // are built without debuginfo. + // + // - Dependency profiles: + // Pkg Target Profile Reason + // --- ------ ------- ------ + // bar lib dev For foo-bin + // bar lib dev-panic For tests/benches and bdep + // bdep lib dev-panic For foo build.rs + // foo custom dev-panic + // + // - `foo` target list is: + // Target Profile Mode + // ------ ------- ---- + // lib dev+panic build (a normal lib target) + // lib dev-panic build (used by tests/benches) + // lib dev dev + // test dev dev + // bench dev dev + // bin dev dev + // bin dev build + // example dev build + p.cargo("build --all-targets -vv") + .with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[COMPILING] bdep [..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..] +[RUNNING] `[..]/target/debug/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]` +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]` +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]` +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]` +[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]` +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` +[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` +[FINISHED] dev [unoptimized + debuginfo] [..] +" + ) + .with_stderr_line_without(&["[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]-C codegen-units=5 [..]"], &["-C debuginfo"]) + .with_stderr_line_without(&["[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]-C codegen-units=5 [..]"], &["-C debuginfo"]) + .with_stderr_line_without(&["[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]-C codegen-units=5 [..]"], &["-C debuginfo"]) + .run(); + p.cargo("build -vv") + .with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_selection_build_all_targets_release() { + let p = all_target_project(); + // `build --all-targets --release` + // NOTES: + // - bdep `panic` is not set because it thinks `build.rs` is a plugin. + // - bar compiled twice. It tries with and without panic, but the "is a + // plugin" logic is forcing it to be cleared. + // - build_script_build is built without panic because it thinks + // `build.rs` is a plugin. + // - build_script_build is being run two times. Once for the `dev` and + // `test` targets, once for the `bench` targets. + // TODO: "PROFILE" says debug both times, though! + // + // - Dependency profiles: + // Pkg Target Profile Reason + // --- ------ ------- ------ + // bar lib release For foo-bin + // bar lib release-panic For tests/benches and bdep + // bdep lib release-panic For foo build.rs + // foo custom release-panic + // + // - `foo` target list is: + // Target Profile Mode + // ------ ------- ---- + // lib release+panic build (a normal lib target) + // lib release-panic build (used by tests/benches) + // lib release test (bench/test de-duped) + // test release test + // bench release test + // bin release test (bench/test de-duped) + // bin release build + // example release build + p.cargo("build --all-targets --release -vv").with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] +[COMPILING] bdep [..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=6 [..] +[RUNNING] `[..]/target/release/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..]` +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3[..]-C codegen-units=2 [..]` +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` +[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..]` +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..]` +[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..]` +[FINISHED] release [optimized] [..] +").run(); + p.cargo("build --all-targets --release -vv") + .with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] release [optimized] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_selection_test() { + let p = all_target_project(); + // `test` + // NOTES: + // - Dependency profiles: + // Pkg Target Profile Reason + // --- ------ ------- ------ + // bar lib test For foo-bin + // bar lib test-panic For tests/benches and bdep + // bdep lib test-panic For foo build.rs + // foo custom test-panic + // + // - `foo` target list is: + // Target Profile Mode + // ------ ------- ---- + // lib test-panic build (for tests) + // lib test build (for bins) + // lib test test + // test test test + // example test-panic build + // bin test test + // bin test build + // + p.cargo("test -vv").with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=3 -C debuginfo=2 [..] +[COMPILING] bdep [..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..] +[RUNNING] `[..]/target/debug/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=3 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=3 -C debuginfo=2 [..] +[FINISHED] test [unoptimized + debuginfo] [..] +[RUNNING] `[..]/deps/foo-[..]` +[RUNNING] `[..]/deps/foo-[..]` +[RUNNING] `[..]/deps/test1-[..]` +[DOCTEST] foo +[RUNNING] `rustdoc [..]--test [..] +").run(); + p.cargo("test -vv") + .with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] test [unoptimized + debuginfo] [..] +[RUNNING] `[..]/deps/foo-[..]` +[RUNNING] `[..]/deps/foo-[..]` +[RUNNING] `[..]/deps/test1-[..]` +[DOCTEST] foo +[RUNNING] `rustdoc [..]--test [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_selection_test_release() { + let p = all_target_project(); + + // `test --release` + // NOTES: + // - Dependency profiles: + // Pkg Target Profile Reason + // --- ------ ------- ------ + // bar lib release For foo-bin + // bar lib release-panic For tests/benches and bdep + // bdep lib release-panic For foo build.rs + // foo custom release-panic + // + // - `foo` target list is: + // Target Profile Mode + // ------ ------- ---- + // lib release-panic build (for tests) + // lib release build (for bins) + // lib release test + // test release test + // example release-panic build + // bin release test + // bin release build + // + p.cargo("test --release -vv").with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C opt-level=3[..]-C codegen-units=2[..] +[COMPILING] bdep [..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=6 [..] +[RUNNING] `[..]/target/release/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..] +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=2 --test [..] +[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]link -C opt-level=3[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[FINISHED] release [optimized] [..] +[RUNNING] `[..]/deps/foo-[..]` +[RUNNING] `[..]/deps/foo-[..]` +[RUNNING] `[..]/deps/test1-[..]` +[DOCTEST] foo +[RUNNING] `rustdoc [..]--test [..]` +").run(); + p.cargo("test --release -vv") + .with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] release [optimized] [..] +[RUNNING] `[..]/deps/foo-[..]` +[RUNNING] `[..]/deps/foo-[..]` +[RUNNING] `[..]/deps/test1-[..]` +[DOCTEST] foo +[RUNNING] `rustdoc [..]--test [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_selection_bench() { + let p = all_target_project(); + + // `bench` + // NOTES: + // - Dependency profiles: + // Pkg Target Profile Reason + // --- ------ ------- ------ + // bar lib bench For foo-bin + // bar lib bench-panic For tests/benches and bdep + // bdep lib bench-panic For foo build.rs + // foo custom bench-panic + // + // - `foo` target list is: + // Target Profile Mode + // ------ ------- ---- + // lib bench-panic build (for benches) + // lib bench build (for bins) + // lib bench test(bench) + // bench bench test(bench) + // bin bench test(bench) + // bin bench build + // + p.cargo("bench -vv").with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3[..]-C codegen-units=4 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=4 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] +[COMPILING] bdep [..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=6 [..] +[RUNNING] `[..]target/release/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=4 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C opt-level=3[..]-C codegen-units=4 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=4 --test [..] +[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=4 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link -C opt-level=3[..]-C codegen-units=4 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C opt-level=3 -C panic=abort[..]-C codegen-units=4 [..] +[FINISHED] bench [optimized] [..] +[RUNNING] `[..]/deps/foo-[..] --bench` +[RUNNING] `[..]/deps/foo-[..] --bench` +[RUNNING] `[..]/deps/bench1-[..] --bench` +").run(); + p.cargo("bench -vv") + .with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] bench [optimized] [..] +[RUNNING] `[..]/deps/foo-[..] --bench` +[RUNNING] `[..]/deps/foo-[..] --bench` +[RUNNING] `[..]/deps/bench1-[..] --bench` +", + ) + .run(); +} + +#[cargo_test] +fn profile_selection_check_all_targets() { + let p = all_target_project(); + // `check` + // NOTES: + // - Dependency profiles: + // Pkg Target Profile Action Reason + // --- ------ ------- ------ ------ + // bar lib dev* link For bdep + // bar lib dev-panic metadata For tests/benches + // bar lib dev metadata For lib/bins + // bdep lib dev* link For foo build.rs + // foo custom dev* link For build.rs + // + // `*` = wants panic, but it is cleared when args are built. + // + // - foo target list is: + // Target Profile Mode + // ------ ------- ---- + // lib dev check + // lib dev-panic check (for tests/benches) + // lib dev-panic check-test (checking lib as a unittest) + // example dev check + // test dev-panic check-test + // bench dev-panic check-test + // bin dev check + // bin dev-panic check-test (checking bin as a unittest) + // + p.cargo("check --all-targets -vv").with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] bdep[..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..] +[RUNNING] `[..]target/debug/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +").run(); + // Starting with Rust 1.27, rustc emits `rmeta` files for bins, so + // everything should be completely fresh. Previously, bins were being + // rechecked. + // See PR rust-lang/rust#49289 and issue rust-lang/cargo#3624. + p.cargo("check --all-targets -vv") + .with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_selection_check_all_targets_release() { + let p = all_target_project(); + // `check --release` + // See issue rust-lang/cargo#5218. + // This is a pretty straightforward variant of + // `profile_selection_check_all_targets` that uses `release` instead of + // `dev` for all targets. + p.cargo("check --all-targets --release -vv").with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=6 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[COMPILING] bdep[..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link [..]-C codegen-units=6 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=6 [..] +[RUNNING] `[..]target/release/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=release DEBUG=false OPT_LEVEL=3 +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 --test [..] +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 --test [..] +[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata -C opt-level=3[..]-C codegen-units=2 --test [..] +[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]metadata -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]metadata -C opt-level=3 -C panic=abort[..]-C codegen-units=2 [..] +[FINISHED] release [optimized] [..] +").run(); + + p.cargo("check --all-targets --release -vv") + .with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] release [optimized] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_selection_check_all_targets_test() { + let p = all_target_project(); + + // `check --profile=test` + // - Dependency profiles: + // Pkg Target Profile Action Reason + // --- ------ ------- ------ ------ + // bar lib test* link For bdep + // bar lib test-panic metadata For tests/benches + // bdep lib test* link For foo build.rs + // foo custom test* link For build.rs + // + // `*` = wants panic, but it is cleared when args are built. + // + // - foo target list is: + // Target Profile Mode + // ------ ------- ---- + // lib test-panic check-test (for tests/benches) + // lib test-panic check-test (checking lib as a unittest) + // example test-panic check-test + // test test-panic check-test + // bench test-panic check-test + // bin test-panic check-test + // + p.cargo("check --all-targets --profile=test -vv").with_stderr_unordered("\ +[COMPILING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..] +[COMPILING] bdep[..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..] +[RUNNING] `[..]target/debug/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[FINISHED] test [unoptimized + debuginfo] [..] +").run(); + + p.cargo("check --all-targets --profile=test -vv") + .with_stderr_unordered( + "\ +[FRESH] bar [..] +[FRESH] bdep [..] +[FRESH] foo [..] +[FINISHED] test [unoptimized + debuginfo] [..] +", + ) + .run(); +} + +#[cargo_test] +fn profile_selection_doc() { + let p = all_target_project(); + // `doc` + // NOTES: + // - Dependency profiles: + // Pkg Target Profile Action Reason + // --- ------ ------- ------ ------ + // bar lib dev* link For bdep + // bar lib dev metadata For rustdoc + // bdep lib dev* link For foo build.rs + // foo custom dev* link For build.rs + // + // `*` = wants panic, but it is cleared when args are built. + p.cargo("doc -vv").with_stderr_unordered("\ +[COMPILING] bar [..] +[DOCUMENTING] bar [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[RUNNING] `rustdoc [..]--crate-name bar bar/src/lib.rs [..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] +[COMPILING] bdep [..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] +[COMPILING] foo [..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..] +[RUNNING] `[..]target/debug/build/foo-[..]/build-script-build` +[foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 +[DOCUMENTING] foo [..] +[RUNNING] `rustdoc [..]--crate-name foo src/lib.rs [..] +[FINISHED] dev [unoptimized + debuginfo] [..] +").run(); +} diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs new file mode 100644 index 0000000..2d2646f --- /dev/null +++ b/tests/testsuite/profiles.rs @@ -0,0 +1,744 @@ +//! Tests for profiles. + +use cargo_test_support::project; +use cargo_test_support::registry::Package; +use std::env; + +#[cargo_test] +fn profile_overrides() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [profile.dev] + opt-level = 1 + debug = false + rpath = true + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] test v0.0.0 ([CWD]) +[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]\ + -C opt-level=1[..]\ + -C debug-assertions=on \ + -C metadata=[..] \ + -C rpath \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[FINISHED] dev [optimized] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn opt_level_override_0() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [profile.dev] + opt-level = 0 + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] test v0.0.0 ([CWD]) +[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]\ + -C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[FINISHED] [..] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn debug_override_1() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.0.0" + authors = [] + + [profile.dev] + debug = 1 + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] test v0.0.0 ([CWD]) +[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]\ + -C debuginfo=1 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[FINISHED] [..] target(s) in [..] +", + ) + .run(); +} + +fn check_opt_level_override(profile_level: &str, rustc_level: &str) { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [profile.dev] + opt-level = {level} + "#, + level = profile_level + ), + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build -v") + .with_stderr(&format!( + "\ +[COMPILING] test v0.0.0 ([CWD]) +[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \ + --emit=[..]link \ + -C opt-level={level}[..]\ + -C debuginfo=2 \ + -C debug-assertions=on \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[FINISHED] [..] target(s) in [..] +", + level = rustc_level + )) + .run(); +} + +#[cargo_test] +fn opt_level_overrides() { + for &(profile_level, rustc_level) in &[ + ("1", "1"), + ("2", "2"), + ("3", "3"), + ("\"s\"", "s"), + ("\"z\"", "z"), + ] { + check_opt_level_override(profile_level, rustc_level) + } +} + +#[cargo_test] +fn top_level_overrides_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [profile.release] + opt-level = 1 + debug = true + + [dependencies.foo] + path = "foo" + "#, + ) + .file("src/lib.rs", "") + .file( + "foo/Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.0.0" + authors = [] + + [profile.release] + opt-level = 0 + debug = false + + [lib] + name = "foo" + crate_type = ["dylib", "rlib"] + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + p.cargo("build -v --release") + .with_stderr(&format!( + "\ +[COMPILING] foo v0.0.0 ([CWD]/foo) +[RUNNING] `rustc --crate-name foo foo/src/lib.rs [..]\ + --crate-type dylib --crate-type rlib \ + --emit=[..]link \ + -C prefer-dynamic \ + -C opt-level=1[..]\ + -C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [CWD]/target/release/deps \ + -L dependency=[CWD]/target/release/deps` +[COMPILING] test v0.0.0 ([CWD]) +[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \ + --emit=[..]link \ + -C opt-level=1[..]\ + -C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/release/deps \ + --extern foo=[CWD]/target/release/deps/\ + {prefix}foo[..]{suffix} \ + --extern foo=[CWD]/target/release/deps/libfoo.rlib` +[FINISHED] release [optimized + debuginfo] target(s) in [..] +", + prefix = env::consts::DLL_PREFIX, + suffix = env::consts::DLL_SUFFIX + )) + .run(); +} + +#[cargo_test] +fn profile_in_non_root_manifest_triggers_a_warning() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + + [profile.dev] + debug = false + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = ".." + + [profile.dev] + opt-level = 1 + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .cwd("bar") + .with_stderr( + "\ +[WARNING] profiles for the non root package will be ignored, specify profiles at the workspace root: +package: [..] +workspace: [..] +[COMPILING] bar v0.1.0 ([..]) +[RUNNING] `rustc [..]` +[FINISHED] dev [unoptimized] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn profile_in_virtual_manifest_works() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + + [profile.dev] + opt-level = 1 + debug = false + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = ".." + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .cwd("bar") + .with_stderr( + "\ +[COMPILING] bar v0.1.0 ([..]) +[RUNNING] `rustc [..]` +[FINISHED] dev [optimized] target(s) in [..]", + ) + .run(); +} + +#[cargo_test] +fn profile_lto_string_bool_dev() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [profile.dev] + lto = "true" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + `lto` setting of string `\"true\"` for `dev` profile is not a valid setting, \ +must be a boolean (`true`/`false`) or a string (`\"thin\"`/`\"fat\"`/`\"off\"`) or omitted. +", + ) + .run(); +} + +#[cargo_test] +fn profile_panic_test_bench() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [profile.test] + panic = "abort" + + [profile.bench] + panic = "abort" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_stderr_contains( + "\ +[WARNING] `panic` setting is ignored for `bench` profile +[WARNING] `panic` setting is ignored for `test` profile +", + ) + .run(); +} + +#[cargo_test] +fn profile_doc_deprecated() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [profile.doc] + opt-level = 0 + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_stderr_contains("[WARNING] profile `doc` is deprecated and has no effect") + .run(); +} + +#[cargo_test] +fn panic_unwind_does_not_build_twice() { + // Check for a bug where `lib` was built twice, once with panic set and + // once without. Since "unwind" is the default, they are the same and + // should only be built once. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.dev] + panic = "unwind" + "#, + ) + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .file("tests/t1.rs", "") + .build(); + + p.cargo("test -v --tests --no-run") + .with_stderr_unordered( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib [..] +[RUNNING] `rustc --crate-name foo src/lib.rs [..] --test [..] +[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin [..] +[RUNNING] `rustc --crate-name foo src/main.rs [..] --test [..] +[RUNNING] `rustc --crate-name t1 tests/t1.rs [..] +[FINISHED] [..] +[EXECUTABLE] `[..]/target/debug/deps/t1-[..][EXE]` +[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` +[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn debug_0_report() { + // The finished line handles 0 correctly. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.dev] + debug = 0 + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.1.0 [..] +[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C debuginfo=0 [..] +[FINISHED] dev [unoptimized] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn thin_lto_works() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "top" + version = "0.5.0" + authors = [] + + [profile.release] + lto = 'thin' + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --release -v") + .with_stderr( + "\ +[COMPILING] top [..] +[RUNNING] `rustc [..] -C lto=thin [..]` +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn strip_works() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.release] + strip = 'symbols' + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --release -v") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc [..] -C strip=symbols [..]` +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn strip_passes_unknown_option_to_rustc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.release] + strip = 'unknown' + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --release -v") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc [..] -C strip=unknown [..]` +error: incorrect value `unknown` for [..] `strip` [..] was expected +", + ) + .run(); +} + +#[cargo_test] +fn strip_accepts_true_to_strip_symbols() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.release] + strip = true + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --release -v") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc [..] -C strip=symbols [..]` +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn strip_accepts_false_to_disable_strip() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [profile.release] + strip = false + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --release -v") + .with_stderr_does_not_contain("-C strip") + .run(); +} + +#[cargo_test] +fn rustflags_works() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["profile-rustflags"] + + [profile.dev] + rustflags = ["-C", "link-dead-code=yes"] + + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .masquerade_as_nightly_cargo(&["profile-rustflags"]) + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..] -C link-dead-code=yes [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustflags_works_with_env() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["profile-rustflags"] + + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .env("CARGO_PROFILE_DEV_RUSTFLAGS", "-C link-dead-code=yes") + .masquerade_as_nightly_cargo(&["profile-rustflags"]) + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc --crate-name foo [..] -C link-dead-code=yes [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustflags_requires_cargo_feature() { + let p = project() + .file( + "Cargo.toml", + r#" + [profile.dev] + rustflags = ["-C", "link-dead-code=yes"] + + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + .masquerade_as_nightly_cargo(&["profile-rustflags"]) + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[CWD]/Cargo.toml` + +Caused by: + feature `profile-rustflags` is required + + The package requires the Cargo feature called `profile-rustflags`, but that feature is \ + not stabilized in this version of Cargo (1.[..]). + Consider adding `cargo-features = [\"profile-rustflags\"]` to the top of Cargo.toml \ + (above the [package] table) to tell Cargo you are opting in to use this unstable feature. + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-rustflags-option \ + for more information about the status of this feature. +", + ) + .run(); + + Package::new("bar", "1.0.0").publish(); + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = "1.0" + + [profile.dev.package.bar] + rustflags = ["-C", "link-dead-code=yes"] + "#, + ); + p.cargo("check") + .masquerade_as_nightly_cargo(&["profile-rustflags"]) + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + feature `profile-rustflags` is required + + The package requires the Cargo feature called `profile-rustflags`, but that feature is \ + not stabilized in this version of Cargo (1.[..]). + Consider adding `cargo-features = [\"profile-rustflags\"]` to the top of Cargo.toml \ + (above the [package] table) to tell Cargo you are opting in to use this unstable feature. + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-rustflags-option \ + for more information about the status of this feature. +", + ) + .run(); +} diff --git a/tests/testsuite/progress.rs b/tests/testsuite/progress.rs new file mode 100644 index 0000000..20870a3 --- /dev/null +++ b/tests/testsuite/progress.rs @@ -0,0 +1,159 @@ +//! Tests for progress bar. + +use cargo_test_support::project; +use cargo_test_support::registry::Package; + +#[cargo_test] +fn bad_progress_config_unknown_when() { + let p = project() + .file( + ".cargo/config", + r#" + [term] + progress = { when = 'unknown' } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] error in [..].cargo/config: \ +could not load config key `term.progress.when` + +Caused by: + unknown variant `unknown`, expected one of `auto`, `never`, `always` +", + ) + .run(); +} + +#[cargo_test] +fn bad_progress_config_missing_width() { + let p = project() + .file( + ".cargo/config", + r#" + [term] + progress = { when = 'always' } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] \"always\" progress requires a `width` key +", + ) + .run(); +} + +#[cargo_test] +fn bad_progress_config_missing_when() { + let p = project() + .file( + ".cargo/config", + r#" + [term] + progress = { width = 1000 } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: missing field `when` +", + ) + .run(); +} + +#[cargo_test] +fn always_shows_progress() { + const N: usize = 3; + let mut deps = String::new(); + for i in 1..=N { + Package::new(&format!("dep{}", i), "1.0.0").publish(); + deps.push_str(&format!("dep{} = \"1.0\"\n", i)); + } + + let p = project() + .file( + ".cargo/config", + r#" + [term] + progress = { when = 'always', width = 100 } + "#, + ) + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + {} + "#, + deps + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr_contains("[DOWNLOADING] [..] crates [..]") + .with_stderr_contains("[..][DOWNLOADED] 3 crates ([..]) in [..]") + .with_stderr_contains("[BUILDING] [..] [..]/4: [..]") + .run(); +} + +#[cargo_test] +fn never_progress() { + const N: usize = 3; + let mut deps = String::new(); + for i in 1..=N { + Package::new(&format!("dep{}", i), "1.0.0").publish(); + deps.push_str(&format!("dep{} = \"1.0\"\n", i)); + } + + let p = project() + .file( + ".cargo/config", + r#" + [term] + progress = { when = 'never' } + "#, + ) + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + {} + "#, + deps + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr_does_not_contain("[DOWNLOADING] [..] crates [..]") + .with_stderr_does_not_contain("[..][DOWNLOADED] 3 crates ([..]) in [..]") + .with_stderr_does_not_contain("[BUILDING] [..] [..]/4: [..]") + .run(); +} diff --git a/tests/testsuite/pub_priv.rs b/tests/testsuite/pub_priv.rs new file mode 100644 index 0000000..83c6a49 --- /dev/null +++ b/tests/testsuite/pub_priv.rs @@ -0,0 +1,199 @@ +//! Tests for public/private dependencies. + +use cargo_test_support::project; +use cargo_test_support::registry::Package; + +#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] +fn exported_priv_warning() { + Package::new("priv_dep", "0.1.0") + .file("src/lib.rs", "pub struct FromPriv;") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["public-dependency"] + + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + priv_dep = "0.1.0" + "#, + ) + .file( + "src/lib.rs", + " + extern crate priv_dep; + pub fn use_priv(_: priv_dep::FromPriv) {} + ", + ) + .build(); + + p.cargo("check --message-format=short") + .masquerade_as_nightly_cargo(&["public-dependency"]) + .with_stderr_contains( + "\ +src/lib.rs:3:13: warning: type `[..]FromPriv` from private dependency 'priv_dep' in public interface +", + ) + .run() +} + +#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")] +fn exported_pub_dep() { + Package::new("pub_dep", "0.1.0") + .file("src/lib.rs", "pub struct FromPub;") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["public-dependency"] + + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + pub_dep = {version = "0.1.0", public = true} + "#, + ) + .file( + "src/lib.rs", + " + extern crate pub_dep; + pub fn use_pub(_: pub_dep::FromPub) {} + ", + ) + .build(); + + p.cargo("check --message-format=short") + .masquerade_as_nightly_cargo(&["public-dependency"]) + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] pub_dep v0.1.0 ([..]) +[CHECKING] pub_dep v0.1.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run() +} + +#[cargo_test] +pub fn requires_nightly_cargo() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["public-dependency"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check --message-format=short") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + the cargo feature `public-dependency` requires a nightly version of Cargo, but this is the `stable` channel + See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. + See https://doc.rust-lang.org/[..]cargo/reference/unstable.html#public-dependency for more information about using this feature. +" + ) + .run() +} + +#[cargo_test] +fn requires_feature() { + Package::new("pub_dep", "0.1.0") + .file("src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + pub_dep = { version = "0.1.0", public = true } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check --message-format=short") + .masquerade_as_nightly_cargo(&["public-dependency"]) + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + feature `public-dependency` is required + + The package requires the Cargo feature called `public-dependency`, \ + but that feature is not stabilized in this version of Cargo (1.[..]). + Consider adding `cargo-features = [\"public-dependency\"]` to the top of Cargo.toml \ + (above the [package] table) to tell Cargo you are opting in to use this unstable feature. + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#public-dependency \ + for more information about the status of this feature. +", + ) + .run() +} + +#[cargo_test] +fn pub_dev_dependency() { + Package::new("pub_dep", "0.1.0") + .file("src/lib.rs", "pub struct FromPub;") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["public-dependency"] + + [package] + name = "foo" + version = "0.0.1" + + [dev-dependencies] + pub_dep = {version = "0.1.0", public = true} + "#, + ) + .file( + "src/lib.rs", + " + extern crate pub_dep; + pub fn use_pub(_: pub_dep::FromPub) {} + ", + ) + .build(); + + p.cargo("check --message-format=short") + .masquerade_as_nightly_cargo(&["public-dependency"]) + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + 'public' specifier can only be used on regular dependencies, not Development dependencies +", + ) + .run() +} diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs new file mode 100644 index 0000000..e3f8690 --- /dev/null +++ b/tests/testsuite/publish.rs @@ -0,0 +1,2704 @@ +//! Tests for the `cargo publish` command. + +use cargo_test_support::git::{self, repo}; +use cargo_test_support::paths; +use cargo_test_support::registry::{self, Package, RegistryBuilder, Response}; +use cargo_test_support::{basic_manifest, no_such_file_err_msg, project, publish}; +use std::fs; +use std::sync::{Arc, Mutex}; + +const CLEAN_FOO_JSON: &str = r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [], + "description": "foo", + "documentation": "foo", + "features": {}, + "homepage": "foo", + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": "foo", + "vers": "0.0.1" + } +"#; + +fn validate_upload_foo() { + publish::validate_upload( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [], + "description": "foo", + "documentation": null, + "features": {}, + "homepage": null, + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": null, + "vers": "0.0.1" + } + "#, + "foo-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + ); +} + +fn validate_upload_li() { + publish::validate_upload( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [], + "description": "li", + "documentation": null, + "features": {}, + "homepage": null, + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "li", + "readme": null, + "readme_file": null, + "repository": null, + "vers": "0.0.1" + } + "#, + "li-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + ); +} + +#[cargo_test] +fn simple() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] crates.io index +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] foo v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", + ) + .run(); + + validate_upload_foo(); +} + +// Check that the `token` key works at the root instead of under a +// `[registry]` table. +#[cargo_test] +fn simple_publish_with_http() { + let _reg = registry::RegistryBuilder::new() + .http_api() + .token(registry::Token::Plaintext("sekrit".to_string())) + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --no-verify --token sekrit --registry dummy-registry") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] foo v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] `dummy-registry` index +", + ) + .run(); +} + +#[cargo_test] +fn simple_publish_with_asymmetric() { + let _reg = registry::RegistryBuilder::new() + .http_api() + .http_index() + .alternative_named("dummy-registry") + .token(registry::Token::rfc_key()) + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --no-verify -Zregistry-auth --registry dummy-registry") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] foo v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] `dummy-registry` index +", + ) + .run(); +} + +#[cargo_test] +fn old_token_location() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + let credentials = paths::home().join(".cargo/credentials.toml"); + fs::remove_file(&credentials).unwrap(); + + // Verify can't publish without a token. + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_contains( + "[ERROR] no token found, \ + please run `cargo login`", + ) + .run(); + + fs::write(&credentials, format!(r#"token = "{}""#, registry.token())).unwrap(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] crates.io index +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] foo v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", + ) + .run(); + + // Skip `validate_upload_foo` as we just cared we got far enough for verify the token behavior. + // Other tests will verify the endpoint gets the right payload. +} + +#[cargo_test] +fn simple_with_index() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --no-verify") + .arg("--token") + .arg(registry.token()) + .arg("--index") + .arg(registry.index_url().as_str()) + .with_stderr( + "\ +[..] +[..] +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", + ) + .run(); + + // Skip `validate_upload_foo` as we just cared we got far enough for verify the VCS behavior. + // Other tests will verify the endpoint gets the right payload. +} + +#[cargo_test] +fn git_deps() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [dependencies.foo] + git = "git://path/to/nowhere" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish -v --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +[ERROR] all dependencies must have a version specified when publishing. +dependency `foo` does not specify a version +Note: The published dependency will use the version from crates.io, +the `git` specification will be removed from the dependency declaration. +", + ) + .run(); +} + +#[cargo_test] +fn path_dependency_no_version() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +[ERROR] all dependencies must have a version specified when publishing. +dependency `bar` does not specify a version +Note: The published dependency will use the version from crates.io, +the `path` specification will be removed from the dependency declaration. +", + ) + .run(); +} + +#[cargo_test] +fn unpublishable_crate() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + publish = false + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --index") + .arg(registry.index_url().as_str()) + .with_status(101) + .with_stderr( + "\ +[ERROR] `foo` cannot be published. +`package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing. +", + ) + .run(); +} + +#[cargo_test] +fn dont_publish_dirty() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project().file("bar", "").build(); + + let _ = git::repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + "\ +[UPDATING] crates.io index +error: 1 files in the working directory contain changes that were not yet \ +committed into git: + +bar + +to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag +", + ) + .run(); +} + +#[cargo_test] +fn publish_clean() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project().build(); + + let _ = repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[..] +[..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", + ) + .run(); + + // Skip `validate_upload_foo_clean` as we just cared we got far enough for verify the VCS behavior. + // Other tests will verify the endpoint gets the right payload. +} + +#[cargo_test] +fn publish_in_sub_repo() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project().no_manifest().file("baz", "").build(); + + let _ = repo(&paths::root().join("foo")) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .cwd("bar") + .with_stderr( + "\ +[..] +[..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", + ) + .run(); + + // Skip `validate_upload_foo_clean` as we just cared we got far enough for verify the VCS behavior. + // Other tests will verify the endpoint gets the right payload. +} + +#[cargo_test] +fn publish_when_ignored() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project().file("baz", "").build(); + + let _ = repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file(".gitignore", "baz") + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[..] +[..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", + ) + .run(); + + // Skip `validate_upload` as we just cared we got far enough for verify the VCS behavior. + // Other tests will verify the endpoint gets the right payload. +} + +#[cargo_test] +fn ignore_when_crate_ignored() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project().no_manifest().file("bar/baz", "").build(); + + let _ = repo(&paths::root().join("foo")) + .file(".gitignore", "bar") + .nocommit_file( + "bar/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .nocommit_file("bar/src/main.rs", "fn main() {}"); + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .cwd("bar") + .with_stderr( + "\ +[..] +[..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", + ) + .run(); + + // Skip `validate_upload` as we just cared we got far enough for verify the VCS behavior. + // Other tests will verify the endpoint gets the right payload. +} + +#[cargo_test] +fn new_crate_rejected() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project().file("baz", "").build(); + + let _ = repo(&paths::root().join("foo")) + .nocommit_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .nocommit_file("src/main.rs", "fn main() {}"); + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_contains( + "[ERROR] 3 files in the working directory contain \ + changes that were not yet committed into git:", + ) + .run(); +} + +#[cargo_test] +fn dry_run() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --dry-run --index") + .arg(registry.index_url().as_str()) + .with_stderr( + "\ +[UPDATING] `[..]` index +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 ([CWD]) +[WARNING] aborting upload due to dry run +", + ) + .run(); + + // Ensure the API request wasn't actually made + assert!(registry::api_path().join("api/v1/crates").exists()); + assert!(!registry::api_path().join("api/v1/crates/new").exists()); +} + +#[cargo_test] +fn registry_not_in_publish_list() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + publish = [ + "test" + ] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish") + .arg("--registry") + .arg("alternative") + .with_status(101) + .with_stderr( + "\ +[ERROR] `foo` cannot be published. +The registry `alternative` is not listed in the `package.publish` value in Cargo.toml. +", + ) + .run(); +} + +#[cargo_test] +fn publish_empty_list() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + publish = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --registry alternative") + .with_status(101) + .with_stderr( + "\ +[ERROR] `foo` cannot be published. +`package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing. +", + ) + .run(); +} + +#[cargo_test] +fn publish_allowed_registry() { + let _registry = RegistryBuilder::new() + .http_api() + .http_index() + .alternative() + .build(); + + let p = project().build(); + + let _ = repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + publish = ["alternative"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --registry alternative") + .with_stderr( + "\ +[..] +[..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] `alternative` index +", + ) + .run(); + + publish::validate_alt_upload( + CLEAN_FOO_JSON, + "foo-0.0.1.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "src/main.rs", + ".cargo_vcs_info.json", + ], + ); +} + +#[cargo_test] +fn publish_implicitly_to_only_allowed_registry() { + let _registry = RegistryBuilder::new() + .http_api() + .http_index() + .alternative() + .build(); + + let p = project().build(); + + let _ = repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + publish = ["alternative"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish") + .with_stderr( + "\ +[NOTE] Found `alternative` as only allowed registry. Publishing to it automatically. +[UPDATING] `alternative` index +[..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] `alternative` index +", + ) + .run(); + + publish::validate_alt_upload( + CLEAN_FOO_JSON, + "foo-0.0.1.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "src/main.rs", + ".cargo_vcs_info.json", + ], + ); +} + +#[cargo_test] +fn publish_fail_with_no_registry_specified() { + let p = project().build(); + + let _ = repo(&paths::root().join("foo")) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + publish = ["alternative", "test"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish") + .with_status(101) + .with_stderr( + "\ +[ERROR] `foo` cannot be published. +The registry `crates-io` is not listed in the `package.publish` value in Cargo.toml. +", + ) + .run(); +} + +#[cargo_test] +fn block_publish_no_registry() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + publish = [] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --registry alternative") + .with_status(101) + .with_stderr( + "\ +[ERROR] `foo` cannot be published. +`package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing. +", + ) + .run(); +} + +// Explicitly setting `crates-io` in the publish list. +#[cargo_test] +fn publish_with_crates_io_explicit() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + publish = ["crates-io"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --registry alternative") + .with_status(101) + .with_stderr( + "\ +[ERROR] `foo` cannot be published. +The registry `alternative` is not listed in the `package.publish` value in Cargo.toml. +", + ) + .run(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[WARNING] [..] +[..] +[PACKAGING] [..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] crates.io index +", + ) + .run(); +} + +#[cargo_test] +fn publish_with_select_features() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [features] + required = [] + optional = [] + "#, + ) + .file( + "src/main.rs", + "#[cfg(not(feature = \"required\"))] + compile_error!(\"This crate requires `required` feature!\"); + fn main() {}", + ) + .build(); + + p.cargo("publish --features required") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[..] +[..] +[..] +[..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] crates.io index +", + ) + .run(); +} + +#[cargo_test] +fn publish_with_all_features() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [features] + required = [] + optional = [] + "#, + ) + .file( + "src/main.rs", + "#[cfg(not(feature = \"required\"))] + compile_error!(\"This crate requires `required` feature!\"); + fn main() {}", + ) + .build(); + + p.cargo("publish --all-features") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[..] +[..] +[..] +[..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] crates.io index +", + ) + .run(); +} + +#[cargo_test] +fn publish_with_no_default_features() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [features] + default = ["required"] + required = [] + "#, + ) + .file( + "src/main.rs", + "#[cfg(not(feature = \"required\"))] + compile_error!(\"This crate requires `required` feature!\"); + fn main() {}", + ) + .build(); + + p.cargo("publish --no-default-features") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_contains("error: This crate requires `required` feature!") + .run(); +} + +#[cargo_test] +fn publish_with_patch() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + Package::new("bar", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + [dependencies] + bar = "1.0" + [patch.crates-io] + bar = { path = "bar" } + "#, + ) + .file( + "src/main.rs", + "extern crate bar; + fn main() { + bar::newfunc(); + }", + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0")) + .file("bar/src/lib.rs", "pub fn newfunc() {}") + .build(); + + // Check that it works with the patched crate. + p.cargo("build").run(); + + // Check that verify fails with patched crate which has new functionality. + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_contains("[..]newfunc[..]") + .run(); + + // Remove the usage of new functionality and try again. + p.change_file("src/main.rs", "extern crate bar; pub fn main() {}"); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[..] +[..] +[..] +[..] +[UPDATING] crates.io index +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] crates.io index +", + ) + .run(); + + publish::validate_upload( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [ + { + "default_features": true, + "features": [], + "kind": "normal", + "name": "bar", + "optional": false, + "target": null, + "version_req": "^1.0" + } + ], + "description": "foo", + "documentation": null, + "features": {}, + "homepage": null, + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": null, + "vers": "0.0.1" + } + "#, + "foo-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + ); +} + +#[cargo_test] +fn publish_checks_for_token_before_verify() { + let registry = registry::RegistryBuilder::new() + .no_configure_token() + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + // Assert upload token error before the package is verified + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_contains("[ERROR] no token found, please run `cargo login`") + .with_stderr_does_not_contain("[VERIFYING] foo v0.0.1 ([CWD])") + .run(); + + // Assert package verified successfully on dry run + p.cargo("publish --dry-run") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[..] +[..] +[..] +[..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 [..] +[WARNING] aborting upload due to dry run +", + ) + .run(); +} + +#[cargo_test] +fn publish_with_bad_source() { + let p = project() + .file( + ".cargo/config", + r#" + [source.crates-io] + replace-with = 'local-registry' + + [source.local-registry] + local-registry = 'registry' + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish") + .with_status(101) + .with_stderr( + "\ +[ERROR] crates-io is replaced with non-remote-registry source registry `[..]/foo/registry`; +include `--registry crates-io` to use crates.io +", + ) + .run(); + + p.change_file( + ".cargo/config", + r#" + [source.crates-io] + replace-with = "vendored-sources" + + [source.vendored-sources] + directory = "vendor" + "#, + ); + + p.cargo("publish") + .with_status(101) + .with_stderr( + "\ +[ERROR] crates-io is replaced with non-remote-registry source dir [..]/foo/vendor; +include `--registry crates-io` to use crates.io +", + ) + .run(); +} + +// A dependency with both `git` and `version`. +#[cargo_test] +fn publish_git_with_version() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + Package::new("dep1", "1.0.1") + .file("src/lib.rs", "pub fn f() -> i32 {1}") + .publish(); + + let git_project = git::new("dep1", |project| { + project + .file("Cargo.toml", &basic_manifest("dep1", "1.0.0")) + .file("src/lib.rs", "pub fn f() -> i32 {2}") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + edition = "2018" + license = "MIT" + description = "foo" + + [dependencies] + dep1 = {{version = "1.0", git="{}"}} + "#, + git_project.url() + ), + ) + .file( + "src/main.rs", + r#" + pub fn main() { + println!("{}", dep1::f()); + } + "#, + ) + .build(); + + p.cargo("run").with_stdout("2").run(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[..] +[..] +[..] +[..] +[..] +[..] +[UPLOADING] foo v0.1.0 ([CWD]) +[UPDATING] crates.io index +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [ + { + "default_features": true, + "features": [], + "kind": "normal", + "name": "dep1", + "optional": false, + "target": null, + "version_req": "^1.0" + } + ], + "description": "foo", + "documentation": null, + "features": {}, + "homepage": null, + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": null, + "vers": "0.1.0" + } + "#, + "foo-0.1.0.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + &[ + ( + "Cargo.toml", + // Check that only `version` is included in Cargo.toml. + &format!( + "{}\n\ + [package]\n\ + edition = \"2018\"\n\ + name = \"foo\"\n\ + version = \"0.1.0\"\n\ + authors = []\n\ + description = \"foo\"\n\ + license = \"MIT\"\n\ + \n\ + [dependencies.dep1]\n\ + version = \"1.0\"\n\ + ", + cargo::core::package::MANIFEST_PREAMBLE + ), + ), + ( + "Cargo.lock", + // The important check here is that it is 1.0.1 in the registry. + "# This file is automatically @generated by Cargo.\n\ + # It is not intended for manual editing.\n\ + version = 3\n\ + \n\ + [[package]]\n\ + name = \"dep1\"\n\ + version = \"1.0.1\"\n\ + source = \"registry+https://github.com/rust-lang/crates.io-index\"\n\ + checksum = \"[..]\"\n\ + \n\ + [[package]]\n\ + name = \"foo\"\n\ + version = \"0.1.0\"\n\ + dependencies = [\n\ + \x20\"dep1\",\n\ + ]\n\ + ", + ), + ], + ); +} + +#[cargo_test] +fn publish_dev_dep_no_version() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + + [dev-dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.1.0 [..] +[UPDATING] crates.io index +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [], + "description": "foo", + "documentation": "foo", + "features": {}, + "homepage": "foo", + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": "foo", + "vers": "0.1.0" + } + "#, + "foo-0.1.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[( + "Cargo.toml", + &format!( + r#"{} +[package] +name = "foo" +version = "0.1.0" +authors = [] +description = "foo" +homepage = "foo" +documentation = "foo" +license = "MIT" +repository = "foo" + +[dev-dependencies] +"#, + cargo::core::package::MANIFEST_PREAMBLE + ), + )], + ); +} + +#[cargo_test] +fn credentials_ambiguous_filename() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + // Make token in `credentials.toml` incorrect to ensure it is not read. + let credentials_toml = paths::home().join(".cargo/credentials.toml"); + fs::write(credentials_toml, r#"token = "wrong-token""#).unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_contains("[..]Unauthorized message from server[..]") + .run(); + + // Favor `credentials` if exists. + let credentials = paths::home().join(".cargo/credentials"); + fs::write(credentials, r#"token = "sekrit""#).unwrap(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[..] +[WARNING] Both `[..]/credentials` and `[..]/credentials.toml` exist. Using `[..]/credentials` +[..] +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 [..] +[UPDATING] crates.io index +", + ) + .run(); +} + +// --index will not load registry.token to avoid possibly leaking +// crates.io token to another server. +#[cargo_test] +fn index_requires_token() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let credentials = paths::home().join(".cargo/credentials.toml"); + fs::remove_file(&credentials).unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --index") + .arg(registry.index_url().as_str()) + .with_status(101) + .with_stderr( + "\ +[ERROR] command-line argument --index requires --token to be specified +", + ) + .run(); +} + +// publish with source replacement without --registry +#[cargo_test] +fn cratesio_source_replacement() { + registry::init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify") + .with_status(101) + .with_stderr( + "\ +[ERROR] crates-io is replaced with remote registry dummy-registry; +include `--registry dummy-registry` or `--registry crates-io` +", + ) + .run(); +} + +#[cargo_test] +fn publish_with_missing_readme() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + license = "MIT" + description = "foo" + homepage = "https://example.com/" + readme = "foo.md" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr(&format!( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.1.0 [..] +[ERROR] failed to read `readme` file for package `foo v0.1.0 ([ROOT]/foo)` + +Caused by: + failed to read `[ROOT]/foo/foo.md` + +Caused by: + {} +", + no_such_file_err_msg() + )) + .run(); +} + +// Registry returns an API error. +#[cargo_test] +fn api_error_json() { + let _registry = registry::RegistryBuilder::new() + .alternative() + .http_api() + .add_responder("/api/v1/crates/new", |_, _| Response { + body: br#"{"errors": [{"detail": "you must be logged in"}]}"#.to_vec(), + code: 403, + headers: vec![], + }) + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --registry alternative") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.0.1 [..] +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 [..] +[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ + +Caused by: + the remote server responded with an error (status 403 Forbidden): you must be logged in +", + ) + .run(); +} + +// Registry returns an API error with a 200 status code. +#[cargo_test] +fn api_error_200() { + let _registry = registry::RegistryBuilder::new() + .alternative() + .http_api() + .add_responder("/api/v1/crates/new", |_, _| Response { + body: br#"{"errors": [{"detail": "max upload size is 123"}]}"#.to_vec(), + code: 200, + headers: vec![], + }) + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --registry alternative") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.0.1 [..] +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 [..] +[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ + +Caused by: + the remote server responded with an error: max upload size is 123 +", + ) + .run(); +} + +// Registry returns an error code without a JSON message. +#[cargo_test] +fn api_error_code() { + let _registry = registry::RegistryBuilder::new() + .alternative() + .http_api() + .add_responder("/api/v1/crates/new", |_, _| Response { + body: br#"go away"#.to_vec(), + code: 400, + headers: vec![], + }) + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --registry alternative") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.0.1 [..] +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 [..] +[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ + +Caused by: + failed to get a 200 OK response, got 400 + headers: + HTTP/1.1 400 + Content-Length: 7 + + body: + go away +", + ) + .run(); +} + +// Registry has a network error. +#[cargo_test] +fn api_curl_error() { + let _registry = registry::RegistryBuilder::new() + .alternative() + .http_api() + .add_responder("/api/v1/crates/new", |_, _| { + panic!("broke"); + }) + .build(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // This doesn't check for the exact text of the error in the remote + // possibility that cargo is linked with a weird version of libcurl, or + // curl changes the text of the message. Currently the message 52 + // (CURLE_GOT_NOTHING) is: + // Server returned nothing (no headers, no data) (Empty reply from server) + p.cargo("publish --no-verify --registry alternative") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.0.1 [..] +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 [..] +[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ + +Caused by: + [52] [..] +", + ) + .run(); +} + +// Registry returns an invalid response. +#[cargo_test] +fn api_other_error() { + let _registry = registry::RegistryBuilder::new() + .alternative() + .http_api() + .add_responder("/api/v1/crates/new", |_, _| Response { + body: b"\xff".to_vec(), + code: 200, + headers: vec![], + }) + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --registry alternative") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.0.1 [..] +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 [..] +[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ + +Caused by: + invalid response from server + +Caused by: + response body was not valid utf-8 +", + ) + .run(); +} + +#[cargo_test] +fn in_package_workspace() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + [workspace] + members = ["li"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "li/Cargo.toml", + r#" + [package] + name = "li" + version = "0.0.1" + description = "li" + license = "MIT" + "#, + ) + .file("li/src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish -p li --no-verify") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[WARNING] manifest has no documentation, homepage or repository. +See [..] +[PACKAGING] li v0.0.1 ([CWD]/li) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] li v0.0.1 ([CWD]/li) +[UPDATING] crates.io index +", + ) + .run(); + + validate_upload_li(); +} + +#[cargo_test] +fn with_duplicate_spec_in_members() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [workspace] + resolver = "2" + members = ["li","bar"] + default-members = ["li","bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "li/Cargo.toml", + r#" + [package] + name = "li" + version = "0.0.1" + description = "li" + license = "MIT" + "#, + ) + .file("li/src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + description = "bar" + license = "MIT" + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + "error: the `-p` argument must be specified to select a single package to publish", + ) + .run(); +} + +#[cargo_test] +fn in_package_workspace_with_members_with_features_old() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [workspace] + members = ["li"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "li/Cargo.toml", + r#" + [package] + name = "li" + version = "0.0.1" + description = "li" + license = "MIT" + "#, + ) + .file("li/src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish -p li --no-verify") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[WARNING] manifest has no documentation, homepage or repository. +See [..] +[PACKAGING] li v0.0.1 ([CWD]/li) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] li v0.0.1 ([CWD]/li) +[UPDATING] crates.io index +", + ) + .run(); + + validate_upload_li(); +} + +#[cargo_test] +fn in_virtual_workspace() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("foo/src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + "error: the `-p` argument must be specified in the root of a virtual workspace", + ) + .run(); +} + +#[cargo_test] +fn in_virtual_workspace_with_p() { + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo","li"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("foo/src/main.rs", "fn main() {}") + .file( + "li/Cargo.toml", + r#" + [package] + name = "li" + version = "0.0.1" + description = "li" + license = "MIT" + "#, + ) + .file("li/src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish -p li --no-verify") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[WARNING] manifest has no documentation, homepage or repository. +See [..] +[PACKAGING] li v0.0.1 ([CWD]/li) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] li v0.0.1 ([CWD]/li) +[UPDATING] crates.io index +", + ) + .run(); +} + +#[cargo_test] +fn in_package_workspace_not_found() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "li/Cargo.toml", + r#" + [package] + name = "li" + version = "0.0.1" + edition = "2021" + authors = [] + license = "MIT" + description = "li" + "#, + ) + .file("li/src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish -p li --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + "\ +error: package ID specification `li` did not match any packages + +Did you mean `foo`? +", + ) + .run(); +} + +#[cargo_test] +fn in_package_workspace_found_multiple() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + [workspace] + members = ["li","lii"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "li/Cargo.toml", + r#" + [package] + name = "li" + version = "0.0.1" + edition = "2021" + authors = [] + license = "MIT" + description = "li" + "#, + ) + .file("li/src/main.rs", "fn main() {}") + .file( + "lii/Cargo.toml", + r#" + [package] + name = "lii" + version = "0.0.1" + edition = "2021" + authors = [] + license = "MIT" + description = "lii" + "#, + ) + .file("lii/src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish -p li* --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + "\ +error: the `-p` argument must be specified to select a single package to publish +", + ) + .run(); +} + +#[cargo_test] +// https://github.com/rust-lang/cargo/issues/10536 +fn publish_path_dependency_without_workspace() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + edition = "2021" + authors = [] + license = "MIT" + description = "bar" + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish -p bar --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + "\ +error: package ID specification `bar` did not match any packages + +Did you mean `foo`? +", + ) + .run(); +} + +#[cargo_test] +fn http_api_not_noop() { + let registry = registry::RegistryBuilder::new().http_api().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[..] +[..] +[..] +[..] +[VERIFYING] foo v0.0.1 ([CWD]) +[..] +[..] +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", + ) + .run(); + + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "bar" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [dependencies] + foo = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build").run(); +} + +#[cargo_test] +fn wait_for_first_publish() { + // Counter for number of tries before the package is "published" + let arc: Arc> = Arc::new(Mutex::new(0)); + let arc2 = arc.clone(); + + // Registry returns an invalid response. + let registry = registry::RegistryBuilder::new() + .http_index() + .http_api() + .add_responder("/index/de/la/delay", move |req, server| { + let mut lock = arc.lock().unwrap(); + *lock += 1; + if *lock <= 1 { + server.not_found(req) + } else { + server.index(req) + } + }) + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "delay" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(0) + .with_stderr( + "\ +[UPDATING] crates.io index +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] delay v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] delay v0.0.1 ([CWD]) +[UPDATING] crates.io index +[WAITING] on `delay` to propagate to crates.io index (ctrl-c to wait asynchronously) +", + ) + .run(); + + // Verify the responder has been pinged + let lock = arc2.lock().unwrap(); + assert_eq!(*lock, 2); + drop(lock); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + [dependencies] + delay = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build").with_status(0).run(); +} + +/// A separate test is needed for package names with - or _ as they hit +/// the responder twice per cargo invocation. If that ever gets changed +/// this test will need to be changed accordingly. +#[cargo_test] +fn wait_for_first_publish_underscore() { + // Counter for number of tries before the package is "published" + let arc: Arc> = Arc::new(Mutex::new(0)); + let arc2 = arc.clone(); + + // Registry returns an invalid response. + let registry = registry::RegistryBuilder::new() + .http_index() + .http_api() + .add_responder("/index/de/la/delay_with_underscore", move |req, server| { + let mut lock = arc.lock().unwrap(); + *lock += 1; + if *lock <= 1 { + server.not_found(req) + } else { + server.index(req) + } + }) + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "delay_with_underscore" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(0) + .with_stderr( + "\ +[UPDATING] crates.io index +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] delay_with_underscore v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] delay_with_underscore v0.0.1 ([CWD]) +[UPDATING] crates.io index +[WAITING] on `delay_with_underscore` to propagate to crates.io index (ctrl-c to wait asynchronously) +", + ) + .run(); + + // Verify the repsponder has been pinged + let lock = arc2.lock().unwrap(); + assert_eq!(*lock, 2); + drop(lock); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + [dependencies] + delay_with_underscore = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build").with_status(0).run(); +} + +#[cargo_test] +fn wait_for_subsequent_publish() { + // Counter for number of tries before the package is "published" + let arc: Arc> = Arc::new(Mutex::new(0)); + let arc2 = arc.clone(); + let publish_req = Arc::new(Mutex::new(None)); + let publish_req2 = publish_req.clone(); + + let registry = registry::RegistryBuilder::new() + .http_index() + .http_api() + .add_responder("/api/v1/crates/new", move |req, server| { + // Capture the publish request, but defer publishing + *publish_req.lock().unwrap() = Some(req.clone()); + server.ok(req) + }) + .add_responder("/index/de/la/delay", move |req, server| { + let mut lock = arc.lock().unwrap(); + *lock += 1; + if *lock == 3 { + // Run the publish on the 3rd attempt + let rep = server + .check_authorized_publish(&publish_req2.lock().unwrap().as_ref().unwrap()); + assert_eq!(rep.code, 200); + } + server.index(req) + }) + .build(); + + // Publish an earlier version + Package::new("delay", "0.0.1") + .file("src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "delay" + version = "0.0.2" + authors = [] + license = "MIT" + description = "foo" + + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(0) + .with_stderr( + "\ +[UPDATING] crates.io index +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] delay v0.0.2 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] delay v0.0.2 ([CWD]) +[UPDATING] crates.io index +[WAITING] on `delay` to propagate to crates.io index (ctrl-c to wait asynchronously) +", + ) + .run(); + + // Verify the responder has been pinged + let lock = arc2.lock().unwrap(); + assert_eq!(*lock, 3); + drop(lock); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + [dependencies] + delay = "0.0.2" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build").with_status(0).run(); +} + +#[cargo_test] +fn skip_wait_for_publish() { + // Intentionally using local registry so the crate never makes it to the index + let registry = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + " + [publish] + timeout = 0 + ", + ) + .build(); + + p.cargo("publish --no-verify -Zpublish-timeout") + .replace_crates_io(registry.index_url()) + .masquerade_as_nightly_cargo(&["publish-timeout"]) + .with_stderr( + "\ +[UPDATING] crates.io index +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] foo v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] foo v0.0.1 ([CWD]) +", + ) + .run(); +} diff --git a/tests/testsuite/publish_lockfile.rs b/tests/testsuite/publish_lockfile.rs new file mode 100644 index 0000000..35da513 --- /dev/null +++ b/tests/testsuite/publish_lockfile.rs @@ -0,0 +1,592 @@ +//! Tests for including `Cargo.lock` when publishing/packaging. + +use std::fs::File; + +use cargo_test_support::registry::Package; +use cargo_test_support::{ + basic_manifest, cargo_process, git, paths, project, publish::validate_crate_contents, +}; + +fn pl_manifest(name: &str, version: &str, extra: &str) -> String { + format!( + r#" + [package] + name = "{}" + version = "{}" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + + {} + "#, + name, version, extra + ) +} + +#[cargo_test] +fn removed() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["publish-lockfile"] + [package] + name = "foo" + version = "0.1.0" + publish-lockfile = true + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("package") + .masquerade_as_nightly_cargo(&["publish-lockfile"]) + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at [..] + +Caused by: + the cargo feature `publish-lockfile` has been removed in the 1.37 release + + Remove the feature from Cargo.toml to remove this error. + See https://doc.rust-lang.org/[..]cargo/reference/unstable.html#publish-lockfile [..] +", + ) + .run(); +} + +#[cargo_test] +fn package_lockfile() { + let p = project() + .file("Cargo.toml", &pl_manifest("foo", "0.0.1", "")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("package") + .with_stderr( + "\ +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + p.cargo("package -l") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + p.cargo("package").with_stdout("").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"], + &[], + ); +} + +#[cargo_test] +fn package_lockfile_git_repo() { + // Create a Git repository containing a minimal Rust project. + let g = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", &pl_manifest("foo", "0.0.1", "")) + .file("src/main.rs", "fn main() {}") + .build(); + cargo_process("package -l") + .cwd(g.root()) + .with_stdout( + "\ +.cargo_vcs_info.json +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + cargo_process("package -v") + .cwd(g.root()) + .with_stderr( + "\ +[PACKAGING] foo v0.0.1 ([..]) +[ARCHIVING] .cargo_vcs_info.json +[ARCHIVING] Cargo.lock +[ARCHIVING] Cargo.toml +[ARCHIVING] Cargo.toml.orig +[ARCHIVING] src/main.rs +[VERIFYING] foo v0.0.1 ([..]) +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc --crate-name foo src/main.rs [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 5 files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn no_lock_file_with_library() { + let p = project() + .file("Cargo.toml", &pl_manifest("foo", "0.0.1", "")) + .file("src/lib.rs", "") + .build(); + + p.cargo("package").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[], + ); +} + +#[cargo_test] +fn lock_file_and_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + "#, + ) + .file("foo/Cargo.toml", &pl_manifest("foo", "0.0.1", "")) + .file("foo/src/main.rs", "fn main() {}") + .build(); + + p.cargo("package").cwd("foo").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/main.rs", "Cargo.lock"], + &[], + ); +} + +#[cargo_test] +fn note_resolve_changes() { + // `multi` has multiple sources (path and registry). + Package::new("multi", "0.1.0").publish(); + // `updated` is always from registry, but should not change. + Package::new("updated", "1.0.0").publish(); + // `patched` is [patch]ed. + Package::new("patched", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + &pl_manifest( + "foo", + "0.0.1", + r#" + [dependencies] + multi = { path = "multi", version = "0.1" } + updated = "1.0" + patched = "1.0" + + [patch.crates-io] + patched = { path = "patched" } + "#, + ), + ) + .file("src/main.rs", "fn main() {}") + .file("multi/Cargo.toml", &basic_manifest("multi", "0.1.0")) + .file("multi/src/lib.rs", "") + .file("patched/Cargo.toml", &basic_manifest("patched", "1.0.0")) + .file("patched/src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile").run(); + + // Make sure this does not change or warn. + Package::new("updated", "1.0.1").publish(); + + p.cargo("package --no-verify -v --allow-dirty") + .with_stderr_unordered( + "\ +[PACKAGING] foo v0.0.1 ([..]) +[ARCHIVING] Cargo.lock +[ARCHIVING] Cargo.toml +[ARCHIVING] Cargo.toml.orig +[ARCHIVING] src/main.rs +[UPDATING] `[..]` index +[NOTE] package `multi v0.1.0` added to the packaged Cargo.lock file, was originally sourced from `[..]/foo/multi` +[NOTE] package `patched v1.0.0` added to the packaged Cargo.lock file, was originally sourced from `[..]/foo/patched` +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn outdated_lock_version_change_does_not_warn() { + // If the version of the package being packaged changes, but Cargo.lock is + // not updated, don't bother warning about it. + let p = project() + .file("Cargo.toml", &pl_manifest("foo", "0.1.0", "")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("generate-lockfile").run(); + + p.change_file("Cargo.toml", &pl_manifest("foo", "0.2.0", "")); + + p.cargo("package --no-verify") + .with_stderr( + "\ +[PACKAGING] foo v0.2.0 ([..]) +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn no_warn_workspace_extras() { + // Other entries in workspace lock file should be ignored. + Package::new("dep1", "1.0.0").publish(); + Package::new("dep2", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + &pl_manifest( + "a", + "0.1.0", + r#" + [dependencies] + dep1 = "1.0" + "#, + ), + ) + .file("a/src/main.rs", "fn main() {}") + .file( + "b/Cargo.toml", + &pl_manifest( + "b", + "0.1.0", + r#" + [dependencies] + dep2 = "1.0" + "#, + ), + ) + .file("b/src/main.rs", "fn main() {}") + .build(); + p.cargo("generate-lockfile").run(); + p.cargo("package --no-verify") + .cwd("a") + .with_stderr( + "\ +[PACKAGING] a v0.1.0 ([..]) +[UPDATING] `[..]` index +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn warn_package_with_yanked() { + Package::new("bar", "0.1.0").publish(); + let p = project() + .file( + "Cargo.toml", + &pl_manifest( + "foo", + "0.0.1", + r#" + [dependencies] + bar = "0.1" + "#, + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("generate-lockfile").run(); + Package::new("bar", "0.1.0").yanked(true).publish(); + // Make sure it sticks with the locked (yanked) version. + Package::new("bar", "0.1.1").publish(); + p.cargo("package --no-verify") + .with_stderr( + "\ +[PACKAGING] foo v0.0.1 ([..]) +[UPDATING] `[..]` index +[WARNING] package `bar v0.1.0` in Cargo.lock is yanked in registry \ + `crates-io`, consider updating to a version that is not yanked +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn warn_install_with_yanked() { + Package::new("bar", "0.1.0").yanked(true).publish(); + Package::new("bar", "0.1.1").publish(); + Package::new("foo", "0.1.0") + .dep("bar", "0.1") + .file("src/main.rs", "fn main() {}") + .file( + "Cargo.lock", + r#" +[[package]] +name = "bar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foo" +version = "0.1.0" +dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + "#, + ) + .publish(); + + cargo_process("install --locked foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.1.0 (registry `[..]`) +[INSTALLING] foo v0.1.0 +[WARNING] package `bar v0.1.0` in Cargo.lock is yanked in registry \ + `crates-io`, consider running without --locked +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 (registry `[..]`) +[COMPILING] bar v0.1.0 +[COMPILING] foo v0.1.0 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [..]/.cargo/bin/foo[EXE] +[INSTALLED] package `foo v0.1.0` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); + + // Try again without --locked, make sure it uses 0.1.1 and does not warn. + cargo_process("install --force foo") + .with_stderr( + "\ +[UPDATING] `[..]` index +[INSTALLING] foo v0.1.0 +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.1 (registry `[..]`) +[COMPILING] bar v0.1.1 +[COMPILING] foo v0.1.0 +[FINISHED] release [optimized] target(s) in [..] +[REPLACING] [..]/.cargo/bin/foo[EXE] +[REPLACED] package `foo v0.1.0` with `foo v0.1.0` (executable `foo[EXE]`) +[WARNING] be sure to add [..] +", + ) + .run(); +} + +#[cargo_test] +fn ignore_lockfile() { + // With an explicit `include` list, but Cargo.lock in .gitignore, don't + // complain about `Cargo.lock` being ignored. Note that it is still + // included in the packaged regardless. + let p = git::new("foo", |p| { + p.file( + "Cargo.toml", + &pl_manifest( + "foo", + "0.0.1", + r#" + include = [ + "src/main.rs" + ] + "#, + ), + ) + .file("src/main.rs", "fn main() {}") + .file(".gitignore", "Cargo.lock") + }); + p.cargo("package -l") + .with_stdout( + "\ +.cargo_vcs_info.json +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + p.cargo("generate-lockfile").run(); + p.cargo("package -v") + .with_stderr( + "\ +[PACKAGING] foo v0.0.1 ([..]) +[ARCHIVING] .cargo_vcs_info.json +[ARCHIVING] Cargo.lock +[ARCHIVING] Cargo.toml +[ARCHIVING] Cargo.toml.orig +[ARCHIVING] src/main.rs +[VERIFYING] foo v0.0.1 ([..]) +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc --crate-name foo src/main.rs [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 5 files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn ignore_lockfile_inner() { + // Ignore `Cargo.lock` if in .gitignore in a git subdirectory. + let p = git::new("foo", |p| { + p.no_manifest() + .file("bar/Cargo.toml", &pl_manifest("bar", "0.0.1", "")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/.gitignore", "Cargo.lock") + }); + p.cargo("generate-lockfile").cwd("bar").run(); + p.cargo("package -v --no-verify") + .cwd("bar") + .with_stderr( + "\ +[PACKAGING] bar v0.0.1 ([..]) +[ARCHIVING] .cargo_vcs_info.json +[ARCHIVING] .gitignore +[ARCHIVING] Cargo.lock +[ARCHIVING] Cargo.toml +[ARCHIVING] Cargo.toml.orig +[ARCHIVING] src/main.rs +[PACKAGED] 6 files, [..] ([..] compressed) +", + ) + .run(); +} + +#[cargo_test] +fn use_workspace_root_lockfile() { + // Issue #11148 + // Workspace members should use `Cargo.lock` at workspace root + + Package::new("serde", "0.2.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [dependencies] + serde = "0.2" + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + license = "MIT" + description = "bar" + workspace = ".." + + [dependencies] + serde = "0.2" + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + // Create `Cargo.lock` in the workspace root. + p.cargo("generate-lockfile").run(); + + // Now, add a newer version of `serde`. + Package::new("serde", "0.2.1").publish(); + + // Expect: package `bar` uses `serde v0.2.0` as required by workspace `Cargo.lock`. + p.cargo("package --workspace") + .with_stderr( + "\ +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] bar v0.0.1 ([CWD]/bar) +[UPDATING] `dummy-registry` index +[VERIFYING] bar v0.0.1 ([CWD]/bar) +[DOWNLOADING] crates ... +[DOWNLOADED] serde v0.2.0 ([..]) +[COMPILING] serde v0.2.0 +[COMPILING] bar v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 4 files, [..] +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] foo v0.0.1 ([CWD]) +[VERIFYING] foo v0.0.1 ([CWD]) +[COMPILING] serde v0.2.0 +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 4 files, [..] +", + ) + .run(); + + let package_path = p.root().join("target/package/foo-0.0.1.crate"); + assert!(package_path.is_file()); + let f = File::open(&package_path).unwrap(); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + &[], + ); + + let package_path = p.root().join("target/package/bar-0.0.1.crate"); + assert!(package_path.is_file()); + let f = File::open(&package_path).unwrap(); + validate_crate_contents( + f, + "bar-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + &[], + ); +} diff --git a/tests/testsuite/read_manifest.rs b/tests/testsuite/read_manifest.rs new file mode 100644 index 0000000..b5e9f05 --- /dev/null +++ b/tests/testsuite/read_manifest.rs @@ -0,0 +1,206 @@ +//! Tests for the `cargo read-manifest` command. + +use cargo_test_support::{basic_bin_manifest, main_file, project}; + +fn manifest_output(readme_value: &str) -> String { + format!( + r#" +{{ + "authors": [ + "wycats@example.com" + ], + "categories": [], + "default_run": null, + "name":"foo", + "readme": {}, + "homepage": null, + "documentation": null, + "repository": null, + "rust_version": null, + "version":"0.5.0", + "id":"foo[..]0.5.0[..](path+file://[..]/foo)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "description": null, + "edition": "2015", + "source":null, + "dependencies":[], + "targets":[{{ + "kind":["bin"], + "crate_types":["bin"], + "doc": true, + "doctest": false, + "test": true, + "edition": "2015", + "name":"foo", + "src_path":"[..]/foo/src/foo.rs" + }}], + "features":{{}}, + "manifest_path":"[..]Cargo.toml", + "metadata": null, + "publish": null +}}"#, + readme_value + ) +} + +fn manifest_output_no_readme() -> String { + manifest_output("null") +} + +pub fn basic_bin_manifest_with_readme(name: &str, readme_filename: &str) -> String { + format!( + r#" + [package] + + name = "{}" + version = "0.5.0" + authors = ["wycats@example.com"] + readme = {} + + [[bin]] + + name = "{}" + "#, + name, readme_filename, name + ) +} + +#[cargo_test] +fn cargo_read_manifest_path_to_cargo_toml_relative() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("read-manifest --manifest-path foo/Cargo.toml") + .cwd(p.root().parent().unwrap()) + .with_json(&manifest_output_no_readme()) + .run(); +} + +#[cargo_test] +fn cargo_read_manifest_path_to_cargo_toml_absolute() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("read-manifest --manifest-path") + .arg(p.root().join("Cargo.toml")) + .cwd(p.root().parent().unwrap()) + .with_json(&manifest_output_no_readme()) + .run(); +} + +#[cargo_test] +fn cargo_read_manifest_path_to_cargo_toml_parent_relative() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("read-manifest --manifest-path foo") + .cwd(p.root().parent().unwrap()) + .with_status(101) + .with_stderr( + "[ERROR] the manifest-path must be \ + a path to a Cargo.toml file", + ) + .run(); +} + +#[cargo_test] +fn cargo_read_manifest_path_to_cargo_toml_parent_absolute() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("read-manifest --manifest-path") + .arg(p.root()) + .cwd(p.root().parent().unwrap()) + .with_status(101) + .with_stderr( + "[ERROR] the manifest-path must be \ + a path to a Cargo.toml file", + ) + .run(); +} + +#[cargo_test] +fn cargo_read_manifest_cwd() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("read-manifest") + .with_json(&manifest_output_no_readme()) + .run(); +} + +#[cargo_test] +fn cargo_read_manifest_with_specified_readme() { + let p = project() + .file( + "Cargo.toml", + &basic_bin_manifest_with_readme("foo", r#""SomeReadme.txt""#), + ) + .file("SomeReadme.txt", "Sample Project") + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("read-manifest") + .with_json(&manifest_output(&format!(r#""{}""#, "SomeReadme.txt"))) + .run(); +} + +#[cargo_test] +fn cargo_read_manifest_default_readme() { + let readme_filenames = ["README.md", "README.txt", "README"]; + + for readme in readme_filenames.iter() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file(readme, "Sample project") + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("read-manifest") + .with_json(&manifest_output(&format!(r#""{}""#, readme))) + .run(); + } +} + +#[cargo_test] +fn cargo_read_manifest_suppress_default_readme() { + let p = project() + .file( + "Cargo.toml", + &basic_bin_manifest_with_readme("foo", "false"), + ) + .file("README.txt", "Sample project") + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("read-manifest") + .with_json(&manifest_output_no_readme()) + .run(); +} + +// If a file named README.md exists, and `readme = true`, the value `README.md` should be defaulted in. +#[cargo_test] +fn cargo_read_manifest_defaults_readme_if_true() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest_with_readme("foo", "true")) + .file("README.md", "Sample project") + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("read-manifest") + .with_json(&manifest_output(r#""README.md""#)) + .run(); +} diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs new file mode 100644 index 0000000..626436a --- /dev/null +++ b/tests/testsuite/registry.rs @@ -0,0 +1,2913 @@ +//! Tests for normal registry dependencies. + +use cargo::core::SourceId; +use cargo_test_support::cargo_process; +use cargo_test_support::paths::{self, CargoPathExt}; +use cargo_test_support::registry::{ + self, registry_path, Dependency, Package, RegistryBuilder, TestRegistry, +}; +use cargo_test_support::{basic_manifest, project}; +use cargo_test_support::{git, install::cargo_home, t}; +use cargo_util::paths::remove_dir_all; +use std::fs::{self, File}; +use std::path::Path; +use std::sync::Mutex; + +fn setup_http() -> TestRegistry { + RegistryBuilder::new().http_index().build() +} + +#[cargo_test] +fn test_server_stops() { + let server = setup_http(); + server.join(); // ensure the server fully shuts down +} + +#[cargo_test] +fn simple_http() { + let _server = setup_http(); + simple(); +} + +#[cargo_test] +fn simple_git() { + simple(); +} + +fn simple() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) +[CHECKING] bar v0.0.1 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + + p.cargo("clean").run(); + + assert!(paths::home().join(".cargo/registry/CACHEDIR.TAG").is_file()); + + // Don't download a second time + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.0.1 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn deps_http() { + let _server = setup_http(); + deps(); +} + +#[cargo_test] +fn deps_git() { + deps(); +} + +fn deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("baz", "0.0.1").publish(); + Package::new("bar", "0.0.1").dep("baz", "*").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] [..] v0.0.1 (registry `dummy-registry`) +[DOWNLOADED] [..] v0.0.1 (registry `dummy-registry`) +[CHECKING] baz v0.0.1 +[CHECKING] bar v0.0.1 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + + assert!(paths::home().join(".cargo/registry/CACHEDIR.TAG").is_file()); +} + +#[cargo_test] +fn nonexistent_http() { + let _server = setup_http(); + nonexistent(); +} + +#[cargo_test] +fn nonexistent_git() { + nonexistent(); +} + +fn nonexistent() { + Package::new("init", "0.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + nonexistent = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +error: no matching package named `nonexistent` found +location searched: registry [..] +required by package `foo v0.0.1 ([..])` +", + ) + .run(); +} + +#[cargo_test] +fn wrong_case_http() { + let _server = setup_http(); + wrong_case(); +} + +#[cargo_test] +fn wrong_case_git() { + wrong_case(); +} + +fn wrong_case() { + Package::new("init", "0.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + Init = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + // #5678 to make this work + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +error: no matching package found +searched package name: `Init` +perhaps you meant: init +location searched: registry [..] +required by package `foo v0.0.1 ([..])` +", + ) + .run(); +} + +#[cargo_test] +fn mis_hyphenated_http() { + let _server = setup_http(); + mis_hyphenated(); +} + +#[cargo_test] +fn mis_hyphenated_git() { + mis_hyphenated(); +} + +fn mis_hyphenated() { + Package::new("mis-hyphenated", "0.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + mis_hyphenated = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + // #2775 to make this work + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +error: no matching package found +searched package name: `mis_hyphenated` +perhaps you meant: mis-hyphenated +location searched: registry [..] +required by package `foo v0.0.1 ([..])` +", + ) + .run(); +} + +#[cargo_test] +fn wrong_version_http() { + let _server = setup_http(); + wrong_version(); +} + +#[cargo_test] +fn wrong_version_git() { + wrong_version(); +} + +fn wrong_version() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + foo = ">= 1.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("foo", "0.0.1").publish(); + Package::new("foo", "0.0.2").publish(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains( + "\ +error: failed to select a version for the requirement `foo = \">=1.0.0\"` +candidate versions found which didn't match: 0.0.2, 0.0.1 +location searched: `[..]` index (which is replacing registry `[..]`) +required by package `foo v0.0.1 ([..])` +", + ) + .run(); + + Package::new("foo", "0.0.3").publish(); + Package::new("foo", "0.0.4").publish(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains( + "\ +error: failed to select a version for the requirement `foo = \">=1.0.0\"` +candidate versions found which didn't match: 0.0.4, 0.0.3, 0.0.2, ... +location searched: `[..]` index (which is replacing registry `[..]`) +required by package `foo v0.0.1 ([..])` +", + ) + .run(); +} + +#[cargo_test] +fn bad_cksum_http() { + let _server = setup_http(); + bad_cksum(); +} + +#[cargo_test] +fn bad_cksum_git() { + bad_cksum(); +} + +fn bad_cksum() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bad-cksum = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + let pkg = Package::new("bad-cksum", "0.0.1"); + pkg.publish(); + t!(File::create(&pkg.archive_dst())); + + p.cargo("check -v") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +[DOWNLOADING] crates ... +[DOWNLOADED] bad-cksum [..] +[ERROR] failed to download replaced source registry `crates-io` + +Caused by: + failed to verify the checksum of `bad-cksum v0.0.1 (registry `dummy-registry`)` +", + ) + .run(); +} + +#[cargo_test] +fn update_registry_http() { + let _server = setup_http(); + update_registry(); +} + +#[cargo_test] +fn update_registry_git() { + update_registry(); +} + +fn update_registry() { + Package::new("init", "0.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + notyet = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains( + "\ +error: no matching package named `notyet` found +location searched: registry `[..]` +required by package `foo v0.0.1 ([..])` +", + ) + .run(); + + Package::new("notyet", "0.0.1").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] notyet v0.0.1 (registry `dummy-registry`) +[CHECKING] notyet v0.0.1 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn package_with_path_deps_http() { + let _server = setup_http(); + package_with_path_deps(); +} + +#[cargo_test] +fn package_with_path_deps_git() { + package_with_path_deps(); +} + +fn package_with_path_deps() { + Package::new("init", "0.0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + repository = "bar" + + [dependencies.notyet] + version = "0.0.1" + path = "notyet" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("notyet/Cargo.toml", &basic_manifest("notyet", "0.0.1")) + .file("notyet/src/lib.rs", "") + .build(); + + p.cargo("package") + .with_status(101) + .with_stderr_contains( + "\ +[PACKAGING] foo [..] +[UPDATING] [..] +[ERROR] failed to prepare local package for uploading + +Caused by: + no matching package named `notyet` found + location searched: registry `crates-io` + required by package `foo v0.0.1 [..]` +", + ) + .run(); + + Package::new("notyet", "0.0.1").publish(); + + p.cargo("package") + .with_stderr( + "\ +[PACKAGING] foo v0.0.1 ([CWD]) +[UPDATING] `[..]` index +[VERIFYING] foo v0.0.1 ([CWD]) +[DOWNLOADING] crates ... +[DOWNLOADED] notyet v0.0.1 (registry `dummy-registry`) +[COMPILING] notyet v0.0.1 +[COMPILING] foo v0.0.1 ([CWD][..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[PACKAGED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn lockfile_locks_http() { + let _server = setup_http(); + lockfile_locks(); +} + +#[cargo_test] +fn lockfile_locks_git() { + lockfile_locks(); +} + +fn lockfile_locks() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) +[CHECKING] bar v0.0.1 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + + p.root().move_into_the_past(); + Package::new("bar", "0.0.2").publish(); + + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn lockfile_locks_transitively_http() { + let _server = setup_http(); + lockfile_locks_transitively(); +} + +#[cargo_test] +fn lockfile_locks_transitively_git() { + lockfile_locks_transitively(); +} + +fn lockfile_locks_transitively() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("baz", "0.0.1").publish(); + Package::new("bar", "0.0.1").dep("baz", "*").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] [..] v0.0.1 (registry `dummy-registry`) +[DOWNLOADED] [..] v0.0.1 (registry `dummy-registry`) +[CHECKING] baz v0.0.1 +[CHECKING] bar v0.0.1 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + + p.root().move_into_the_past(); + Package::new("baz", "0.0.2").publish(); + Package::new("bar", "0.0.2").dep("baz", "*").publish(); + + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn yanks_are_not_used_http() { + let _server = setup_http(); + yanks_are_not_used(); +} + +#[cargo_test] +fn yanks_are_not_used_git() { + yanks_are_not_used(); +} + +fn yanks_are_not_used() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("baz", "0.0.1").publish(); + Package::new("baz", "0.0.2").yanked(true).publish(); + Package::new("bar", "0.0.1").dep("baz", "*").publish(); + Package::new("bar", "0.0.2") + .dep("baz", "*") + .yanked(true) + .publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] [..] v0.0.1 (registry `dummy-registry`) +[DOWNLOADED] [..] v0.0.1 (registry `dummy-registry`) +[CHECKING] baz v0.0.1 +[CHECKING] bar v0.0.1 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn relying_on_a_yank_is_bad_http() { + let _server = setup_http(); + relying_on_a_yank_is_bad(); +} + +#[cargo_test] +fn relying_on_a_yank_is_bad_git() { + relying_on_a_yank_is_bad(); +} + +fn relying_on_a_yank_is_bad() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("baz", "0.0.1").publish(); + Package::new("baz", "0.0.2").yanked(true).publish(); + Package::new("bar", "0.0.1").dep("baz", "=0.0.2").publish(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains( + "\ +error: failed to select a version for the requirement `baz = \"=0.0.2\"` +candidate versions found which didn't match: 0.0.1 +location searched: `[..]` index (which is replacing registry `[..]`) +required by package `bar v0.0.1` + ... which satisfies dependency `bar = \"*\"` of package `foo [..]` +", + ) + .run(); +} + +#[cargo_test] +fn yanks_in_lockfiles_are_ok_http() { + let _server = setup_http(); + yanks_in_lockfiles_are_ok(); +} + +#[cargo_test] +fn yanks_in_lockfiles_are_ok_git() { + yanks_in_lockfiles_are_ok(); +} + +fn yanks_in_lockfiles_are_ok() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").publish(); + + p.cargo("check").run(); + + registry_path().join("3").rm_rf(); + + Package::new("bar", "0.0.1").yanked(true).publish(); + + p.cargo("check").with_stdout("").run(); + + p.cargo("update") + .with_status(101) + .with_stderr_contains( + "\ +error: no matching package named `bar` found +location searched: registry [..] +required by package `foo v0.0.1 ([..])` +", + ) + .run(); +} + +#[cargo_test] +fn yanks_in_lockfiles_are_ok_for_other_update_http() { + let _server = setup_http(); + yanks_in_lockfiles_are_ok_for_other_update(); +} + +#[cargo_test] +fn yanks_in_lockfiles_are_ok_for_other_update_git() { + yanks_in_lockfiles_are_ok_for_other_update(); +} + +fn yanks_in_lockfiles_are_ok_for_other_update() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + baz = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").publish(); + Package::new("baz", "0.0.1").publish(); + + p.cargo("check").run(); + + registry_path().join("3").rm_rf(); + + Package::new("bar", "0.0.1").yanked(true).publish(); + Package::new("baz", "0.0.1").publish(); + + p.cargo("check").with_stdout("").run(); + + Package::new("baz", "0.0.2").publish(); + + p.cargo("update") + .with_status(101) + .with_stderr_contains( + "\ +error: no matching package named `bar` found +location searched: registry [..] +required by package `foo v0.0.1 ([..])` +", + ) + .run(); + + p.cargo("update -p baz") + .with_stderr_contains( + "\ +[UPDATING] `[..]` index +[UPDATING] baz v0.0.1 -> v0.0.2 +", + ) + .run(); +} + +#[cargo_test] +fn yanks_in_lockfiles_are_ok_with_new_dep_http() { + let _server = setup_http(); + yanks_in_lockfiles_are_ok_with_new_dep(); +} + +#[cargo_test] +fn yanks_in_lockfiles_are_ok_with_new_dep_git() { + yanks_in_lockfiles_are_ok_with_new_dep(); +} + +fn yanks_in_lockfiles_are_ok_with_new_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").publish(); + + p.cargo("check").run(); + + registry_path().join("3").rm_rf(); + + Package::new("bar", "0.0.1").yanked(true).publish(); + Package::new("baz", "0.0.1").publish(); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + baz = "*" + "#, + ); + + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn update_with_lockfile_if_packages_missing_http() { + let _server = setup_http(); + update_with_lockfile_if_packages_missing(); +} + +#[cargo_test] +fn update_with_lockfile_if_packages_missing_git() { + update_with_lockfile_if_packages_missing(); +} + +fn update_with_lockfile_if_packages_missing() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").publish(); + p.cargo("check").run(); + p.root().move_into_the_past(); + + paths::home().join(".cargo/registry").rm_rf(); + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn update_lockfile_http() { + let _server = setup_http(); + update_lockfile(); +} + +#[cargo_test] +fn update_lockfile_git() { + update_lockfile(); +} + +fn update_lockfile() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + println!("0.0.1"); + Package::new("bar", "0.0.1").publish(); + p.cargo("check").run(); + + Package::new("bar", "0.0.2").publish(); + Package::new("bar", "0.0.3").publish(); + paths::home().join(".cargo/registry").rm_rf(); + println!("0.0.2 update"); + p.cargo("update -p bar --precise 0.0.2") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] bar v0.0.1 -> v0.0.2 +", + ) + .run(); + + println!("0.0.2 build"); + p.cargo("check") + .with_stderr( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] [..] v0.0.2 (registry `dummy-registry`) +[CHECKING] bar v0.0.2 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + + println!("0.0.3 update"); + p.cargo("update -p bar") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] bar v0.0.2 -> v0.0.3 +", + ) + .run(); + + println!("0.0.3 build"); + p.cargo("check") + .with_stderr( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] [..] v0.0.3 (registry `dummy-registry`) +[CHECKING] bar v0.0.3 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + + println!("new dependencies update"); + Package::new("bar", "0.0.4").dep("spam", "0.2.5").publish(); + Package::new("spam", "0.2.5").publish(); + p.cargo("update -p bar") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] bar v0.0.3 -> v0.0.4 +[ADDING] spam v0.2.5 +", + ) + .run(); + + println!("new dependencies update"); + Package::new("bar", "0.0.5").publish(); + p.cargo("update -p bar") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] bar v0.0.4 -> v0.0.5 +[REMOVING] spam v0.2.5 +", + ) + .run(); +} + +#[cargo_test] +fn dev_dependency_not_used_http() { + let _server = setup_http(); + dev_dependency_not_used(); +} + +#[cargo_test] +fn dev_dependency_not_used_git() { + dev_dependency_not_used(); +} + +fn dev_dependency_not_used() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("baz", "0.0.1").publish(); + Package::new("bar", "0.0.1").dev_dep("baz", "*").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] [..] v0.0.1 (registry `dummy-registry`) +[CHECKING] bar v0.0.1 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn bad_license_file_http() { + let registry = setup_http(); + bad_license_file(®istry); +} + +#[cargo_test] +fn bad_license_file_git() { + let registry = registry::init(); + bad_license_file(®istry); +} + +fn bad_license_file(registry: &TestRegistry) { + Package::new("foo", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license-file = "foo" + description = "bar" + repository = "baz" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("publish -v") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_contains("[ERROR] the license file `foo` does not exist") + .run(); +} + +#[cargo_test] +fn updating_a_dep_http() { + let _server = setup_http(); + updating_a_dep(); +} + +#[cargo_test] +fn updating_a_dep_git() { + updating_a_dep(); +} + +fn updating_a_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.a] + path = "a" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + Package::new("bar", "0.0.1").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) +[CHECKING] bar v0.0.1 +[CHECKING] a v0.0.1 ([CWD]/a) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + assert!(paths::home().join(".cargo/registry/CACHEDIR.TAG").is_file()); + + // Now delete the CACHEDIR.TAG file: this is the situation we'll be in after + // upgrading from a version of Cargo that doesn't mark this directory, to one that + // does. It should be recreated. + fs::remove_file(paths::home().join(".cargo/registry/CACHEDIR.TAG")) + .expect("remove CACHEDIR.TAG"); + + p.change_file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + "#, + ); + Package::new("bar", "0.1.0").publish(); + + println!("second"); + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) +[CHECKING] bar v0.1.0 +[CHECKING] a v0.0.1 ([CWD]/a) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + + assert!( + paths::home().join(".cargo/registry/CACHEDIR.TAG").is_file(), + "CACHEDIR.TAG recreated in existing registry" + ); +} + +#[cargo_test] +fn git_and_registry_dep_http() { + let _server = setup_http(); + git_and_registry_dep(); +} + +#[cargo_test] +fn git_and_registry_dep_git() { + git_and_registry_dep(); +} + +fn git_and_registry_dep() { + let b = git::repo(&paths::root().join("b")) + .file( + "Cargo.toml", + r#" + [package] + name = "b" + version = "0.0.1" + authors = [] + + [dependencies] + a = "0.0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = "0.0.1" + + [dependencies.b] + git = '{}' + "#, + b.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("a", "0.0.1").publish(); + + p.root().move_into_the_past(); + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] a v0.0.1 (registry `dummy-registry`) +[CHECKING] a v0.0.1 +[CHECKING] b v0.0.1 ([..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); + p.root().move_into_the_past(); + + println!("second"); + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn update_publish_then_update_http() { + let _server = setup_http(); + update_publish_then_update(); +} + +#[cargo_test] +fn update_publish_then_update_git() { + update_publish_then_update(); +} + +fn update_publish_then_update() { + // First generate a Cargo.lock and a clone of the registry index at the + // "head" of the current registry. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + a = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + Package::new("a", "0.1.0").publish(); + p.cargo("build").run(); + + // Next, publish a new package and back up the copy of the registry we just + // created. + Package::new("a", "0.1.1").publish(); + let registry = paths::home().join(".cargo/registry"); + let backup = paths::root().join("registry-backup"); + t!(fs::rename(®istry, &backup)); + + // Generate a Cargo.lock with the newer version, and then move the old copy + // of the registry back into place. + let p2 = project() + .at("foo2") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + a = "0.1.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + p2.cargo("build").run(); + registry.rm_rf(); + t!(fs::rename(&backup, ®istry)); + t!(fs::rename( + p2.root().join("Cargo.lock"), + p.root().join("Cargo.lock") + )); + + // Finally, build the first project again (with our newer Cargo.lock) which + // should force an update of the old registry, download the new crate, and + // then build everything again. + p.cargo("build") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] a v0.1.1 (registry `dummy-registry`) +[COMPILING] a v0.1.1 +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn fetch_downloads_http() { + let _server = setup_http(); + fetch_downloads(); +} + +#[cargo_test] +fn fetch_downloads_git() { + fetch_downloads(); +} + +fn fetch_downloads() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + a = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("a", "0.1.0").publish(); + + p.cargo("fetch") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] a v0.1.0 (registry [..]) +", + ) + .run(); +} + +#[cargo_test] +fn update_transitive_dependency_http() { + let _server = setup_http(); + update_transitive_dependency(); +} + +#[cargo_test] +fn update_transitive_dependency_git() { + update_transitive_dependency(); +} + +fn update_transitive_dependency() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + a = "0.1.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("a", "0.1.0").dep("b", "*").publish(); + Package::new("b", "0.1.0").publish(); + + p.cargo("fetch").run(); + + Package::new("b", "0.1.1").publish(); + + p.cargo("update -pb") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] b v0.1.0 -> v0.1.1 +", + ) + .run(); + + p.cargo("check") + .with_stderr( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] b v0.1.1 (registry `dummy-registry`) +[CHECKING] b v0.1.1 +[CHECKING] a v0.1.0 +[CHECKING] foo v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn update_backtracking_ok_http() { + let _server = setup_http(); + update_backtracking_ok(); +} + +#[cargo_test] +fn update_backtracking_ok_git() { + update_backtracking_ok(); +} + +fn update_backtracking_ok() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + webdriver = "0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("webdriver", "0.1.0") + .dep("hyper", "0.6") + .publish(); + Package::new("hyper", "0.6.5") + .dep("openssl", "0.1") + .dep("cookie", "0.1") + .publish(); + Package::new("cookie", "0.1.0") + .dep("openssl", "0.1") + .publish(); + Package::new("openssl", "0.1.0").publish(); + + p.cargo("generate-lockfile").run(); + + Package::new("openssl", "0.1.1").publish(); + Package::new("hyper", "0.6.6") + .dep("openssl", "0.1.1") + .dep("cookie", "0.1.0") + .publish(); + + p.cargo("update -p hyper") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] hyper v0.6.5 -> v0.6.6 +[UPDATING] openssl v0.1.0 -> v0.1.1 +", + ) + .run(); +} + +#[cargo_test] +fn update_multiple_packages_http() { + let _server = setup_http(); + update_multiple_packages(); +} + +#[cargo_test] +fn update_multiple_packages_git() { + update_multiple_packages(); +} + +fn update_multiple_packages() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + a = "*" + b = "*" + c = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("a", "0.1.0").publish(); + Package::new("b", "0.1.0").publish(); + Package::new("c", "0.1.0").publish(); + + p.cargo("fetch").run(); + + Package::new("a", "0.1.1").publish(); + Package::new("b", "0.1.1").publish(); + Package::new("c", "0.1.1").publish(); + + p.cargo("update -pa -pb") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] a v0.1.0 -> v0.1.1 +[UPDATING] b v0.1.0 -> v0.1.1 +", + ) + .run(); + + p.cargo("update -pb -pc") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] c v0.1.0 -> v0.1.1 +", + ) + .run(); + + p.cargo("check") + .with_stderr_contains("[DOWNLOADED] a v0.1.1 (registry `dummy-registry`)") + .with_stderr_contains("[DOWNLOADED] b v0.1.1 (registry `dummy-registry`)") + .with_stderr_contains("[DOWNLOADED] c v0.1.1 (registry `dummy-registry`)") + .with_stderr_contains("[CHECKING] a v0.1.1") + .with_stderr_contains("[CHECKING] b v0.1.1") + .with_stderr_contains("[CHECKING] c v0.1.1") + .with_stderr_contains("[CHECKING] foo v0.5.0 ([..])") + .run(); +} + +#[cargo_test] +fn bundled_crate_in_registry_http() { + let _server = setup_http(); + bundled_crate_in_registry(); +} + +#[cargo_test] +fn bundled_crate_in_registry_git() { + bundled_crate_in_registry(); +} + +fn bundled_crate_in_registry() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + bar = "0.1" + baz = "0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.1.0").publish(); + Package::new("baz", "0.1.0") + .dep("bar", "0.1.0") + .file( + "Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "bar", version = "0.1.0" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "") + .publish(); + + p.cargo("run").run(); +} + +#[cargo_test] +fn update_same_prefix_oh_my_how_was_this_a_bug_http() { + let _server = setup_http(); + update_same_prefix_oh_my_how_was_this_a_bug(); +} + +#[cargo_test] +fn update_same_prefix_oh_my_how_was_this_a_bug_git() { + update_same_prefix_oh_my_how_was_this_a_bug(); +} + +fn update_same_prefix_oh_my_how_was_this_a_bug() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "ugh" + version = "0.5.0" + authors = [] + + [dependencies] + foo = "0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("foobar", "0.2.0").publish(); + Package::new("foo", "0.1.0") + .dep("foobar", "0.2.0") + .publish(); + + p.cargo("generate-lockfile").run(); + p.cargo("update -pfoobar --precise=0.2.0").run(); +} + +#[cargo_test] +fn use_semver_http() { + let _server = setup_http(); + use_semver(); +} + +#[cargo_test] +fn use_semver_git() { + use_semver(); +} + +fn use_semver() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + foo = "1.2.3-alpha.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("foo", "1.2.3-alpha.0").publish(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn use_semver_package_incorrectly_http() { + let _server = setup_http(); + use_semver_package_incorrectly(); +} + +#[cargo_test] +fn use_semver_package_incorrectly_git() { + use_semver_package_incorrectly(); +} + +fn use_semver_package_incorrectly() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.1-alpha.0" + authors = [] + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + authors = [] + + [dependencies] + a = { version = "^0.1", path = "../a" } + "#, + ) + .file("a/src/main.rs", "fn main() {}") + .file("b/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: no matching package found +searched package name: `a` +prerelease package needs to be specified explicitly +a = { version = \"0.1.1-alpha.0\" } +location searched: [..] +required by package `b v0.1.0 ([..])` +", + ) + .run(); +} + +#[cargo_test] +fn only_download_relevant_http() { + let _server = setup_http(); + only_download_relevant(); +} + +#[cargo_test] +fn only_download_relevant_git() { + only_download_relevant(); +} + +fn only_download_relevant() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [target.foo.dependencies] + foo = "*" + [dev-dependencies] + bar = "*" + [dependencies] + baz = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("foo", "0.1.0").publish(); + Package::new("bar", "0.1.0").publish(); + Package::new("baz", "0.1.0").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] baz v0.1.0 ([..]) +[CHECKING] baz v0.1.0 +[CHECKING] bar v0.5.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn resolve_and_backtracking_http() { + let _server = setup_http(); + resolve_and_backtracking(); +} + +#[cargo_test] +fn resolve_and_backtracking_git() { + resolve_and_backtracking(); +} + +fn resolve_and_backtracking() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + foo = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("foo", "0.1.1") + .feature_dep("bar", "0.1", &["a", "b"]) + .publish(); + Package::new("foo", "0.1.0").publish(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn upstream_warnings_on_extra_verbose_http() { + let _server = setup_http(); + upstream_warnings_on_extra_verbose(); +} + +#[cargo_test] +fn upstream_warnings_on_extra_verbose_git() { + upstream_warnings_on_extra_verbose(); +} + +fn upstream_warnings_on_extra_verbose() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + foo = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("foo", "0.1.0") + .file("src/lib.rs", "fn unused() {}") + .publish(); + + p.cargo("check -vv") + .with_stderr_contains("[WARNING] [..]unused[..]") + .run(); +} + +#[cargo_test] +fn disallow_network_http() { + let _server = setup_http(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + foo = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check --frozen") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] failed to get `foo` as a dependency of package `bar v0.5.0 ([..])` + +Caused by: + failed to query replaced source registry `crates-io` + +Caused by: + attempting to make an HTTP request, but --frozen was specified +", + ) + .run(); +} + +#[cargo_test] +fn disallow_network_git() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + foo = "*" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check --frozen") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to get `foo` as a dependency of package `bar v0.5.0 [..]` + +Caused by: + failed to load source for dependency `foo` + +Caused by: + Unable to update registry [..] + +Caused by: + attempting to make an HTTP request, but --frozen was specified +", + ) + .run(); +} + +#[cargo_test] +fn add_dep_dont_update_registry_http() { + let _server = setup_http(); + add_dep_dont_update_registry(); +} + +#[cargo_test] +fn add_dep_dont_update_registry_git() { + add_dep_dont_update_registry(); +} + +fn add_dep_dont_update_registry() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + baz = { path = "baz" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.5.0" + authors = [] + + [dependencies] + remote = "0.3" + "#, + ) + .file("baz/src/lib.rs", "") + .build(); + + Package::new("remote", "0.3.4").publish(); + + p.cargo("check").run(); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + baz = { path = "baz" } + remote = "0.3" + "#, + ); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.5.0 ([..]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn bump_version_dont_update_registry_http() { + let _server = setup_http(); + bump_version_dont_update_registry(); +} + +#[cargo_test] +fn bump_version_dont_update_registry_git() { + bump_version_dont_update_registry(); +} + +fn bump_version_dont_update_registry() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + baz = { path = "baz" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.5.0" + authors = [] + + [dependencies] + remote = "0.3" + "#, + ) + .file("baz/src/lib.rs", "") + .build(); + + Package::new("remote", "0.3.4").publish(); + + p.cargo("check").run(); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.6.0" + authors = [] + + [dependencies] + baz = { path = "baz" } + "#, + ); + + p.cargo("check") + .with_stderr( + "\ +[CHECKING] bar v0.6.0 ([..]) +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn toml_lies_but_index_is_truth_http() { + let _server = setup_http(); + toml_lies_but_index_is_truth(); +} + +#[cargo_test] +fn toml_lies_but_index_is_truth_git() { + toml_lies_but_index_is_truth(); +} + +fn toml_lies_but_index_is_truth() { + Package::new("foo", "0.2.0").publish(); + Package::new("bar", "0.3.0") + .dep("foo", "0.2.0") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.3.0" + authors = [] + + [dependencies] + foo = "0.1.0" + "#, + ) + .file("src/lib.rs", "extern crate foo;") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = [] + + [dependencies] + bar = "0.3" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v").run(); +} + +#[cargo_test] +fn vv_prints_warnings_http() { + let _server = setup_http(); + vv_prints_warnings(); +} + +#[cargo_test] +fn vv_prints_warnings_git() { + vv_prints_warnings(); +} + +fn vv_prints_warnings() { + Package::new("foo", "0.2.0") + .file( + "src/lib.rs", + "#![deny(warnings)] fn foo() {} // unused function", + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "fo" + version = "0.5.0" + authors = [] + + [dependencies] + foo = "0.2" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -vv").run(); +} + +#[cargo_test] +fn bad_and_or_malicious_packages_rejected_http() { + let _server = setup_http(); + bad_and_or_malicious_packages_rejected(); +} + +#[cargo_test] +fn bad_and_or_malicious_packages_rejected_git() { + bad_and_or_malicious_packages_rejected(); +} + +fn bad_and_or_malicious_packages_rejected() { + Package::new("foo", "0.2.0") + .extra_file("foo-0.1.0/src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "fo" + version = "0.5.0" + authors = [] + + [dependencies] + foo = "0.2" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -vv") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] [..] +error: failed to download [..] + +Caused by: + failed to unpack [..] + +Caused by: + [..] contains a file at \"foo-0.1.0/src/lib.rs\" which isn't under \"foo-0.2.0\" +", + ) + .run(); +} + +#[cargo_test] +fn git_init_templatedir_missing_http() { + let _server = setup_http(); + git_init_templatedir_missing(); +} + +#[cargo_test] +fn git_init_templatedir_missing_git() { + git_init_templatedir_missing(); +} + +fn git_init_templatedir_missing() { + Package::new("foo", "0.2.0").dep("bar", "*").publish(); + Package::new("bar", "0.2.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "fo" + version = "0.5.0" + authors = [] + + [dependencies] + foo = "0.2" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check").run(); + + remove_dir_all(paths::home().join(".cargo/registry")).unwrap(); + fs::write( + paths::home().join(".gitconfig"), + r#" + [init] + templatedir = nowhere + "#, + ) + .unwrap(); + + p.cargo("check").run(); + p.cargo("check").run(); +} + +#[cargo_test] +fn rename_deps_and_features_http() { + let _server = setup_http(); + rename_deps_and_features(); +} + +#[cargo_test] +fn rename_deps_and_features_git() { + rename_deps_and_features(); +} + +fn rename_deps_and_features() { + Package::new("foo", "0.1.0") + .file("src/lib.rs", "pub fn f1() {}") + .publish(); + Package::new("foo", "0.2.0") + .file("src/lib.rs", "pub fn f2() {}") + .publish(); + Package::new("bar", "0.2.0") + .add_dep( + Dependency::new("foo01", "0.1.0") + .package("foo") + .optional(true), + ) + .add_dep(Dependency::new("foo02", "0.2.0").package("foo")) + .feature("another", &["foo01"]) + .file( + "src/lib.rs", + r#" + extern crate foo02; + #[cfg(feature = "foo01")] + extern crate foo01; + + pub fn foo() { + foo02::f2(); + #[cfg(feature = "foo01")] + foo01::f1(); + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + + [dependencies] + bar = "0.2" + "#, + ) + .file( + "src/main.rs", + " + extern crate bar; + fn main() { bar::foo(); } + ", + ) + .build(); + + p.cargo("check").run(); + p.cargo("check --features bar/foo01").run(); + p.cargo("check --features bar/another").run(); +} + +#[cargo_test] +fn ignore_invalid_json_lines_http() { + let _server = setup_http(); + ignore_invalid_json_lines(); +} + +#[cargo_test] +fn ignore_invalid_json_lines_git() { + ignore_invalid_json_lines(); +} + +fn ignore_invalid_json_lines() { + Package::new("foo", "0.1.0").publish(); + Package::new("foo", "0.1.1").invalid_json(true).publish(); + Package::new("foo", "0.2.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + + [dependencies] + foo = '0.1.0' + foo02 = { version = '0.2.0', package = 'foo' } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn readonly_registry_still_works_http() { + let _server = setup_http(); + readonly_registry_still_works(); +} + +#[cargo_test] +fn readonly_registry_still_works_git() { + readonly_registry_still_works(); +} + +fn readonly_registry_still_works() { + Package::new("foo", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + authors = [] + + [dependencies] + foo = '0.1.0' + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile").run(); + p.cargo("fetch --locked").run(); + chmod_readonly(&paths::home(), true); + p.cargo("check").run(); + // make sure we un-readonly the files afterwards so "cargo clean" can remove them (#6934) + chmod_readonly(&paths::home(), false); + + fn chmod_readonly(path: &Path, readonly: bool) { + for entry in t!(path.read_dir()) { + let entry = t!(entry); + let path = entry.path(); + if t!(entry.file_type()).is_dir() { + chmod_readonly(&path, readonly); + } else { + set_readonly(&path, readonly); + } + } + set_readonly(path, readonly); + } + + fn set_readonly(path: &Path, readonly: bool) { + let mut perms = t!(path.metadata()).permissions(); + perms.set_readonly(readonly); + t!(fs::set_permissions(path, perms)); + } +} + +#[cargo_test] +fn registry_index_rejected_http() { + let _server = setup_http(); + registry_index_rejected(); +} + +#[cargo_test] +fn registry_index_rejected_git() { + registry_index_rejected(); +} + +fn registry_index_rejected() { + Package::new("dep", "0.1.0").publish(); + + let p = project() + .file( + ".cargo/config", + r#" + [registry] + index = "https://example.com/" + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + the `registry.index` config value is no longer supported + Use `[source]` replacement to alter the default index for crates.io. +", + ) + .run(); + + p.cargo("login") + .with_status(101) + .with_stderr( + "\ +[ERROR] the `registry.index` config value is no longer supported +Use `[source]` replacement to alter the default index for crates.io. +", + ) + .run(); +} + +#[cargo_test] +fn package_lock_inside_package_is_overwritten() { + let registry = registry::init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1") + .file("src/lib.rs", "") + .file(".cargo-ok", "") + .publish(); + + p.cargo("check").run(); + + let id = SourceId::for_registry(registry.index_url()).unwrap(); + let hash = cargo::util::hex::short_hash(&id); + let ok = cargo_home() + .join("registry") + .join("src") + .join(format!("-{}", hash)) + .join("bar-0.0.1") + .join(".cargo-ok"); + + assert_eq!(ok.metadata().unwrap().len(), 2); +} + +#[cargo_test] +fn package_lock_as_a_symlink_inside_package_is_overwritten() { + let registry = registry::init(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1") + .file("src/lib.rs", "pub fn f() {}") + .symlink(".cargo-ok", "src/lib.rs") + .publish(); + + p.cargo("check").run(); + + let id = SourceId::for_registry(registry.index_url()).unwrap(); + let hash = cargo::util::hex::short_hash(&id); + let pkg_root = cargo_home() + .join("registry") + .join("src") + .join(format!("-{}", hash)) + .join("bar-0.0.1"); + let ok = pkg_root.join(".cargo-ok"); + let librs = pkg_root.join("src/lib.rs"); + + // Is correctly overwritten and doesn't affect the file linked to + assert_eq!(ok.metadata().unwrap().len(), 2); + assert_eq!(fs::read_to_string(librs).unwrap(), "pub fn f() {}"); +} + +#[cargo_test] +fn ignores_unknown_index_version_http() { + let _server = setup_http(); + ignores_unknown_index_version(); +} + +#[cargo_test] +fn ignores_unknown_index_version_git() { + ignores_unknown_index_version(); +} + +fn ignores_unknown_index_version() { + // If the version field is not understood, it is ignored. + Package::new("bar", "1.0.0").publish(); + Package::new("bar", "1.0.1").schema_version(9999).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "foo v0.1.0 [..]\n\ + └── bar v1.0.0\n\ + ", + ) + .run(); +} + +#[cargo_test] +fn protocol() { + cargo_process("install bar") + .with_status(101) + .env("CARGO_REGISTRIES_CRATES_IO_PROTOCOL", "invalid") + .with_stderr("[ERROR] unsupported registry protocol `invalid` (defined in environment variable `CARGO_REGISTRIES_CRATES_IO_PROTOCOL`)") + .run() +} + +#[cargo_test] +fn http_requires_trailing_slash() { + cargo_process("install bar --index sparse+https://invalid.crates.io/test") + .with_status(101) + .with_stderr("[ERROR] sparse registry url must end in a slash `/`: sparse+https://invalid.crates.io/test") + .run() +} + +// Limit the test to debug builds so that `__CARGO_TEST_MAX_UNPACK_SIZE` will take affect. +#[cfg(debug_assertions)] +#[cargo_test] +fn reach_max_unpack_size() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + // Size of bar.crate is around 180 bytes. + Package::new("bar", "0.0.1").publish(); + + p.cargo("check") + .env("__CARGO_TEST_MAX_UNPACK_SIZE", "8") // hit 8 bytes limit and boom! + .env("__CARGO_TEST_MAX_UNPACK_RATIO", "0") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) +[ERROR] failed to download replaced source registry `crates-io` + +Caused by: + failed to unpack package `bar v0.0.1 (registry `dummy-registry`)` + +Caused by: + failed to iterate over archive + +Caused by: + maximum limit reached when reading +", + ) + .run(); + + // Restore to the default ratio and it should compile. + p.cargo("check") + .env("__CARGO_TEST_MAX_UNPACK_SIZE", "8") + .with_stderr( + "\ +[CHECKING] bar v0.0.1 +[CHECKING] foo v0.0.1 ([..]) +[FINISHED] dev [..] +", + ) + .run(); +} + +#[cargo_test] +fn sparse_retry() { + let fail_count = Mutex::new(0); + let _registry = RegistryBuilder::new() + .http_index() + .add_responder("/index/3/b/bar", move |req, server| { + let mut fail_count = fail_count.lock().unwrap(); + if *fail_count < 2 { + *fail_count += 1; + server.internal_server_error(req) + } else { + server.index(req) + } + }) + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +warning: spurious network error (2 tries remaining): failed to get successful HTTP response from `[..]`, got 500 +body: +internal server error +warning: spurious network error (1 tries remaining): failed to get successful HTTP response from `[..]`, got 500 +body: +internal server error +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`) +[CHECKING] bar v0.0.1 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn deleted_entry() { + // Checks the behavior when a package is removed from the index. + // This is done occasionally on crates.io to handle things like + // copyright takedowns. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // First, test removing a single version, but leaving an older version. + Package::new("bar", "0.1.0").publish(); + let bar_path = Path::new("3/b/bar"); + let bar_reg_path = registry_path().join(&bar_path); + let old_index = fs::read_to_string(&bar_reg_path).unwrap(); + Package::new("bar", "0.1.1").publish(); + p.cargo("tree") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.1 (registry `dummy-registry`) +", + ) + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +└── bar v0.1.1 +", + ) + .run(); + + // Remove 0.1.1 + fs::remove_file(paths::root().join("dl/bar/0.1.1/download")).unwrap(); + let repo = git2::Repository::open(registry_path()).unwrap(); + let mut index = repo.index().unwrap(); + fs::write(&bar_reg_path, &old_index).unwrap(); + index.add_path(&bar_path).unwrap(); + index.write().unwrap(); + git::commit(&repo); + + // With `Cargo.lock` unchanged, it shouldn't have an impact. + p.cargo("tree") + .with_stderr("") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +└── bar v0.1.1 +", + ) + .run(); + + // Regenerating Cargo.lock should switch to old version. + fs::remove_file(p.root().join("Cargo.lock")).unwrap(); + p.cargo("tree") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) +", + ) + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +└── bar v0.1.0 +", + ) + .run(); + + // Remove the package entirely. + fs::remove_file(paths::root().join("dl/bar/0.1.0/download")).unwrap(); + let mut index = repo.index().unwrap(); + index.remove(&bar_path, 0).unwrap(); + index.write().unwrap(); + git::commit(&repo); + fs::remove_file(&bar_reg_path).unwrap(); + + // With `Cargo.lock` unchanged, it shouldn't have an impact. + p.cargo("tree") + .with_stderr("") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +└── bar v0.1.0 +", + ) + .run(); + + // Regenerating Cargo.lock should fail. + fs::remove_file(p.root().join("Cargo.lock")).unwrap(); + p.cargo("tree") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +error: no matching package named `bar` found +location searched: registry `crates-io` +required by package `foo v0.1.0 ([ROOT]/foo)` +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn corrupted_ok_overwritten() { + // Checks what happens if .cargo-ok gets truncated, such as if the file is + // created, but the flush/close is interrupted. + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("fetch") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) +", + ) + .run(); + let ok = glob::glob( + paths::home() + .join(".cargo/registry/src/*/bar-1.0.0/.cargo-ok") + .to_str() + .unwrap(), + ) + .unwrap() + .next() + .unwrap() + .unwrap(); + // Simulate cargo being interrupted, or filesystem corruption. + fs::write(&ok, "").unwrap(); + assert_eq!(fs::read_to_string(&ok).unwrap(), ""); + p.cargo("fetch").with_stderr("").run(); + assert_eq!(fs::read_to_string(&ok).unwrap(), "ok"); +} diff --git a/tests/testsuite/registry_auth.rs b/tests/testsuite/registry_auth.rs new file mode 100644 index 0000000..3989dc9 --- /dev/null +++ b/tests/testsuite/registry_auth.rs @@ -0,0 +1,519 @@ +//! Tests for normal registry dependencies. + +use cargo_test_support::registry::{Package, RegistryBuilder}; +use cargo_test_support::{project, Execs, Project}; + +fn cargo(p: &Project, s: &str) -> Execs { + let mut e = p.cargo(s); + e.masquerade_as_nightly_cargo(&["registry-auth"]) + .arg("-Zregistry-auth"); + e +} + +fn make_project() -> Project { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "0.0.1" + registry = "alternative" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + Package::new("bar", "0.0.1").alternative(true).publish(); + p +} + +static SUCCESS_OUTPUT: &'static str = "\ +[UPDATING] `alternative` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 (registry `alternative`) +[COMPILING] bar v0.0.1 (registry `alternative`) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +"; + +#[cargo_test] +fn requires_nightly() { + let _registry = RegistryBuilder::new() + .alternative() + .auth_required() + .http_api() + .build(); + + let p = make_project(); + p.cargo("check") + .with_status(101) + .with_stderr( + r#"[UPDATING] `alternative` index +[DOWNLOADING] crates ... +error: failed to download from `[..]/dl/bar/0.0.1/download` + +Caused by: + failed to get successful HTTP response from `[..]`, got 401 + body: + Unauthorized message from server. +"#, + ) + .run(); +} + +#[cargo_test] +fn simple() { + let _registry = RegistryBuilder::new() + .alternative() + .auth_required() + .http_index() + .build(); + + let p = make_project(); + cargo(&p, "build").with_stderr(SUCCESS_OUTPUT).run(); +} + +#[cargo_test] +fn simple_with_asymmetric() { + let _registry = RegistryBuilder::new() + .alternative() + .auth_required() + .http_index() + .token(cargo_test_support::registry::Token::rfc_key()) + .build(); + + let p = make_project(); + cargo(&p, "build").with_stderr(SUCCESS_OUTPUT).run(); +} + +#[cargo_test] +fn environment_config() { + let registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_registry() + .no_configure_token() + .http_index() + .build(); + let p = make_project(); + cargo(&p, "build") + .env( + "CARGO_REGISTRIES_ALTERNATIVE_INDEX", + registry.index_url().as_str(), + ) + .env("CARGO_REGISTRIES_ALTERNATIVE_TOKEN", registry.token()) + .with_stderr(SUCCESS_OUTPUT) + .run(); +} + +#[cargo_test] +fn environment_token() { + let registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_token() + .http_index() + .build(); + + let p = make_project(); + cargo(&p, "build") + .env("CARGO_REGISTRIES_ALTERNATIVE_TOKEN", registry.token()) + .with_stderr(SUCCESS_OUTPUT) + .run(); +} + +#[cargo_test] +fn environment_token_with_asymmetric() { + let registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_token() + .http_index() + .token(cargo_test_support::registry::Token::Keys( + "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36" + .to_string(), + None, + )) + .build(); + + let p = make_project(); + cargo(&p, "build") + .env("CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY", registry.key()) + .with_stderr(SUCCESS_OUTPUT) + .run(); +} + +#[cargo_test] +fn warn_both_asymmetric_and_token() { + let _server = RegistryBuilder::new() + .alternative() + .no_configure_token() + .build(); + let p = project() + .file( + ".cargo/config", + r#" + [registries.alternative] + token = "sekrit" + secret-key = "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36" + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + description = "foo" + authors = [] + license = "MIT" + homepage = "https://example.com/" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --registry alternative") + .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"]) + .arg("-Zregistry-auth") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] both `token` and `secret-key` were specified in the config for registry `alternative`. +Only one of these values may be set, remove one or the other to proceed. +", + ) + .run(); +} + +#[cargo_test] +fn warn_both_asymmetric_and_credential_process() { + let _server = RegistryBuilder::new() + .alternative() + .no_configure_token() + .build(); + let p = project() + .file( + ".cargo/config", + r#" + [registries.alternative] + credential-process = "false" + secret-key = "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36" + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + description = "foo" + authors = [] + license = "MIT" + homepage = "https://example.com/" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --registry alternative") + .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"]) + .arg("-Zcredential-process") + .arg("-Zregistry-auth") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] both `credential-process` and `secret-key` were specified in the config for registry `alternative`. +Only one of these values may be set, remove one or the other to proceed. +", + ) + .run(); +} + +#[cargo_test] +fn bad_environment_token_with_asymmetric_subject() { + let registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_token() + .http_index() + .token(cargo_test_support::registry::Token::Keys( + "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36" + .to_string(), + None, + )) + .build(); + + let p = make_project(); + cargo(&p, "build") + .env("CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY", registry.key()) + .env( + "CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY_SUBJECT", + "incorrect", + ) + .with_stderr_contains( + " token rejected for `alternative`, please run `cargo login --registry alternative`", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn bad_environment_token_with_asymmetric_incorrect_subject() { + let registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_token() + .http_index() + .token(cargo_test_support::registry::Token::rfc_key()) + .build(); + + let p = make_project(); + cargo(&p, "build") + .env("CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY", registry.key()) + .env( + "CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY_SUBJECT", + "incorrect", + ) + .with_stderr_contains( + " token rejected for `alternative`, please run `cargo login --registry alternative`", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn bad_environment_token_with_incorrect_asymmetric() { + let _registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_token() + .http_index() + .token(cargo_test_support::registry::Token::Keys( + "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36" + .to_string(), + None, + )) + .build(); + + let p = make_project(); + cargo(&p, "build") + .env( + "CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY", + "k3.secret.9Vxr5hVlI_g_orBZN54vPz20bmB4O76wB_MVqUSuJJJqHFLwP8kdn_RY5g6J6pQG", + ) + .with_stderr_contains( + " token rejected for `alternative`, please run `cargo login --registry alternative`", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn missing_token() { + let _registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_token() + .http_index() + .build(); + + let p = make_project(); + cargo(&p, "build") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `alternative` index +[ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([..])` + +Caused by: + no token found for `alternative`, please run `cargo login --registry alternative` + or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN", + ) + .run(); +} + +#[cargo_test] +fn missing_token_git() { + let _registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_token() + .build(); + + let p = make_project(); + cargo(&p, "build") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `alternative` index +[ERROR] failed to download `bar v0.0.1 (registry `alternative`)` + +Caused by: + unable to get packages from source + +Caused by: + no token found for `alternative`, please run `cargo login --registry alternative` + or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN", + ) + .run(); +} + +#[cargo_test] +fn incorrect_token() { + let _registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_token() + .http_index() + .build(); + + let p = make_project(); + cargo(&p, "build") + .env("CARGO_REGISTRIES_ALTERNATIVE_TOKEN", "incorrect") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `alternative` index +[ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([..])` + +Caused by: + token rejected for `alternative`, please run `cargo login --registry alternative` + or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN + +Caused by: + failed to get successful HTTP response from `http://[..]/index/config.json`, got 401 + body: + Unauthorized message from server.", + ) + .run(); +} + +#[cargo_test] +fn incorrect_token_git() { + let _registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_token() + .http_api() + .build(); + + let p = make_project(); + cargo(&p, "build") + .env("CARGO_REGISTRIES_ALTERNATIVE_TOKEN", "incorrect") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `alternative` index +[DOWNLOADING] crates ... +[ERROR] failed to download from `http://[..]/dl/bar/0.0.1/download` + +Caused by: + failed to get successful HTTP response from `http://[..]/dl/bar/0.0.1/download`, got 401 + body: + Unauthorized message from server.", + ) + .run(); +} + +#[cargo_test] +fn anonymous_alt_registry() { + // An alternative registry that requires auth, but is not in the config. + let registry = RegistryBuilder::new() + .alternative() + .auth_required() + .no_configure_token() + .no_configure_registry() + .http_index() + .build(); + + let p = make_project(); + cargo(&p, &format!("install --index {} bar", registry.index_url())) + .with_status(101) + .with_stderr( + "\ +[UPDATING] `[..]` index +[ERROR] no token found for `[..]` +consider setting up an alternate registry in Cargo's configuration +as described by https://doc.rust-lang.org/cargo/reference/registries.html + +[registries] +my-registry = { index = \"[..]\" } + +", + ) + .run(); +} + +#[cargo_test] +fn login() { + let _registry = RegistryBuilder::new() + .alternative() + .no_configure_token() + .auth_required() + .http_index() + .build(); + + let p = make_project(); + cargo(&p, "login --registry alternative") + .with_stdout("please paste the token found on https://test-registry-login/me below") + .with_stdin("sekrit") + .run(); +} + +#[cargo_test] +fn login_existing_token() { + let _registry = RegistryBuilder::new() + .alternative() + .auth_required() + .http_index() + .build(); + + let p = make_project(); + cargo(&p, "login --registry alternative") + .with_stdout("please paste the token found on file://[..]/me below") + .with_stdin("sekrit") + .run(); +} + +#[cargo_test] +fn duplicate_index() { + let server = RegistryBuilder::new() + .alternative() + .no_configure_token() + .auth_required() + .build(); + let p = make_project(); + + // Two alternative registries with the same index. + cargo(&p, "build") + .env( + "CARGO_REGISTRIES_ALTERNATIVE1_INDEX", + server.index_url().as_str(), + ) + .env( + "CARGO_REGISTRIES_ALTERNATIVE2_INDEX", + server.index_url().as_str(), + ) + .with_status(101) + .with_stderr( + "\ +[UPDATING] `alternative` index +[ERROR] failed to download `bar v0.0.1 (registry `alternative`)` + +Caused by: + unable to get packages from source + +Caused by: + multiple registries are configured with the same index url \ + 'registry+file://[..]/alternative-registry': alternative1, alternative2 +", + ) + .run(); +} diff --git a/tests/testsuite/rename_deps.rs b/tests/testsuite/rename_deps.rs new file mode 100644 index 0000000..f274404 --- /dev/null +++ b/tests/testsuite/rename_deps.rs @@ -0,0 +1,391 @@ +//! Tests for renaming dependencies. + +use cargo_test_support::git; +use cargo_test_support::paths; +use cargo_test_support::registry::{self, Package}; +use cargo_test_support::{basic_manifest, project}; + +#[cargo_test] +fn rename_dependency() { + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.2.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { version = "0.1.0" } + baz = { version = "0.2.0", package = "bar" } + "#, + ) + .file("src/lib.rs", "extern crate bar; extern crate baz;") + .build(); + + p.cargo("build").run(); +} + +#[cargo_test] +fn rename_with_different_names() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + baz = { path = "bar", package = "bar" } + "#, + ) + .file("src/lib.rs", "extern crate baz;") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + name = "random_name" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("build").run(); +} + +#[cargo_test] +fn lots_of_names() { + registry::alt_init(); + Package::new("foo", "0.1.0") + .file("src/lib.rs", "pub fn foo1() {}") + .publish(); + Package::new("foo", "0.2.0") + .file("src/lib.rs", "pub fn foo() {}") + .publish(); + Package::new("foo", "0.1.0") + .file("src/lib.rs", "pub fn foo2() {}") + .alternative(true) + .publish(); + + let g = git::repo(&paths::root().join("another")) + .file("Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("src/lib.rs", "pub fn foo3() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "test" + version = "0.1.0" + authors = [] + + [dependencies] + foo = "0.2" + foo1 = {{ version = "0.1", package = "foo" }} + foo2 = {{ version = "0.1", registry = "alternative", package = "foo" }} + foo3 = {{ git = '{}', package = "foo" }} + foo4 = {{ path = "foo", package = "foo" }} + "#, + g.url() + ), + ) + .file( + "src/lib.rs", + " + extern crate foo; + extern crate foo1; + extern crate foo2; + extern crate foo3; + extern crate foo4; + + pub fn foo() { + foo::foo(); + foo1::foo1(); + foo2::foo2(); + foo3::foo3(); + foo4::foo4(); + } + ", + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "pub fn foo4() {}") + .build(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn rename_and_patch() { + Package::new("foo", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { version = "0.1", package = "foo" } + + [patch.crates-io] + foo = { path = "foo" } + "#, + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::foo(); }", + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn rename_twice() { + Package::new("foo", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { version = "0.1", package = "foo" } + [build-dependencies] + foo = { version = "0.1" } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v") + .with_status(101) + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] foo v0.1.0 (registry [..]) +error: the crate `test v0.1.0 ([CWD])` depends on crate `foo v0.1.0` multiple times with different names +", + ) + .run(); +} + +#[cargo_test] +fn rename_affects_fingerprint() { + Package::new("foo", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.1.0" + authors = [] + + [dependencies] + foo = { version = "0.1", package = "foo" } + "#, + ) + .file("src/lib.rs", "extern crate foo;") + .build(); + + p.cargo("build -v").run(); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { version = "0.1", package = "foo" } + "#, + ); + + p.cargo("build -v") + .with_status(101) + .with_stderr_contains("[..]can't find crate for `foo`") + .run(); +} + +#[cargo_test] +fn can_run_doc_tests() { + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.2.0").publish(); + + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = { version = "0.1.0" } + baz = { version = "0.2.0", package = "bar" } + "#, + ) + .file( + "src/lib.rs", + " + extern crate bar; + extern crate baz; + ", + ) + .build(); + + foo.cargo("test -v") + .with_stderr_contains( + "\ +[DOCTEST] foo +[RUNNING] `rustdoc [..]--test [..]src/lib.rs \ + [..] \ + --extern bar=[CWD]/target/debug/deps/libbar-[..].rlib \ + --extern baz=[CWD]/target/debug/deps/libbar-[..].rlib \ + [..]` +", + ) + .run(); +} + +#[cargo_test] +fn features_still_work() { + Package::new("foo", "0.1.0").publish(); + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.1.0" + authors = [] + + [dependencies] + p1 = { path = 'a', features = ['b'] } + p2 = { path = 'b' } + "#, + ) + .file("src/lib.rs", "") + .file( + "a/Cargo.toml", + r#" + [package] + name = "p1" + version = "0.1.0" + authors = [] + + [dependencies] + b = { version = "0.1", package = "foo", optional = true } + "#, + ) + .file("a/src/lib.rs", "extern crate b;") + .file( + "b/Cargo.toml", + r#" + [package] + name = "p2" + version = "0.1.0" + authors = [] + + [dependencies] + b = { version = "0.1", package = "bar", optional = true } + + [features] + default = ['b'] + "#, + ) + .file("b/src/lib.rs", "extern crate b;") + .build(); + + p.cargo("build -v").run(); +} + +#[cargo_test] +fn features_not_working() { + Package::new("foo", "0.1.0").publish(); + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.1.0" + authors = [] + + [dependencies] + a = { path = 'a', package = 'p1', optional = true } + + [features] + default = ['p1'] + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("p1", "0.1.0")) + .build(); + + p.cargo("build -v") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + feature `default` includes `p1` which is neither a dependency nor another feature +", + ) + .run(); +} + +#[cargo_test] +fn rename_with_dash() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "qwerty" + version = "0.1.0" + + [dependencies] + foo-bar = { path = 'a', package = 'a' } + "#, + ) + .file("src/lib.rs", "extern crate foo_bar;") + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build").run(); +} diff --git a/tests/testsuite/replace.rs b/tests/testsuite/replace.rs new file mode 100644 index 0000000..c11c493 --- /dev/null +++ b/tests/testsuite/replace.rs @@ -0,0 +1,1300 @@ +//! Tests for `[replace]` table source replacement. + +use cargo_test_support::git; +use cargo_test_support::paths; +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_manifest, project}; + +#[cargo_test] +fn override_simple() { + Package::new("bar", "0.1.0").publish(); + + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = {{ git = '{}' }} + "#, + bar.url() + ), + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[UPDATING] git repository `[..]` +[CHECKING] bar v0.1.0 (file://[..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn override_with_features() { + Package::new("bar", "0.1.0").publish(); + + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = {{ git = '{}', features = ["some_feature"] }} + "#, + bar.url() + ), + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] index +[UPDATING] git repository `[..]` +[WARNING] replacement for `bar` uses the features mechanism. default-features and features \ +will not take effect because the replacement dependency does not support this mechanism +[CHECKING] bar v0.1.0 (file://[..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn override_with_setting_default_features() { + Package::new("bar", "0.1.0").publish(); + + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = {{ git = '{}', default-features = false, features = ["none_default_feature"] }} + "#, + bar.url() + ), + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] index +[UPDATING] git repository `[..]` +[WARNING] replacement for `bar` uses the features mechanism. default-features and features \ +will not take effect because the replacement dependency does not support this mechanism +[CHECKING] bar v0.1.0 (file://[..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn missing_version() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + bar = { git = 'https://example.com' } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + replacements must specify a version to replace, but `[..]bar` does not +", + ) + .run(); +} + +#[cargo_test] +fn invalid_semver_version() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + + [replace] + "bar:*" = { git = 'https://example.com' } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + replacements must specify a valid semver version to replace, but `bar:*` does not +", + ) + .run(); +} + +#[cargo_test] +fn different_version() { + Package::new("bar", "0.2.0").publish(); + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = "0.2.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + replacements cannot specify a version requirement, but found one for [..] +", + ) + .run(); +} + +#[cargo_test] +fn transitive() { + Package::new("bar", "0.1.0").publish(); + Package::new("baz", "0.2.0") + .dep("bar", "0.1.0") + .file("src/lib.rs", "extern crate bar; fn baz() { bar::bar(); }") + .publish(); + + let foo = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + baz = "0.2.0" + + [replace] + "bar:0.1.0" = {{ git = '{}' }} + "#, + foo.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[UPDATING] git repository `[..]` +[DOWNLOADING] crates ... +[DOWNLOADED] baz v0.2.0 (registry [..]) +[CHECKING] bar v0.1.0 (file://[..]) +[CHECKING] baz v0.2.0 +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn persists_across_rebuilds() { + Package::new("bar", "0.1.0").publish(); + + let foo = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = {{ git = '{}' }} + "#, + foo.url() + ), + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[UPDATING] git repository `file://[..]` +[CHECKING] bar v0.1.0 (file://[..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn replace_registry_with_path() { + Package::new("bar", "0.1.0").publish(); + + let _ = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = { path = "../bar" } + "#, + ) + .file( + "src/lib.rs", + "extern crate bar; pub fn foo() { bar::bar(); }", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[CHECKING] bar v0.1.0 ([ROOT][..]/bar) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn use_a_spec_to_select() { + Package::new("baz", "0.1.1") + .file("src/lib.rs", "pub fn baz1() {}") + .publish(); + Package::new("baz", "0.2.0").publish(); + Package::new("bar", "0.1.1") + .dep("baz", "0.2") + .file( + "src/lib.rs", + "extern crate baz; pub fn bar() { baz::baz3(); }", + ) + .publish(); + + let foo = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("baz", "0.2.0")) + .file("src/lib.rs", "pub fn baz3() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + baz = "0.1" + + [replace] + "baz:0.2.0" = {{ git = '{}' }} + "#, + foo.url() + ), + ) + .file( + "src/lib.rs", + " + extern crate bar; + extern crate baz; + + pub fn local() { + baz::baz1(); + bar::bar(); + } + ", + ) + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[UPDATING] git repository `[..]` +[DOWNLOADING] crates ... +[DOWNLOADED] [..] +[DOWNLOADED] [..] +[CHECKING] [..] +[CHECKING] [..] +[CHECKING] [..] +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn override_adds_some_deps() { + Package::new("baz", "0.1.1").publish(); + Package::new("bar", "0.1.0").publish(); + + let foo = git::repo(&paths::root().join("override")) + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + baz = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + + [replace] + "bar:0.1.0" = {{ git = '{}' }} + "#, + foo.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +[UPDATING] git repository `[..]` +[DOWNLOADING] crates ... +[DOWNLOADED] baz v0.1.1 (registry [..]) +[CHECKING] baz v0.1.1 +[CHECKING] bar v0.1.0 ([..]) +[CHECKING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("check").with_stdout("").run(); + + Package::new("baz", "0.1.2").publish(); + p.cargo("update -p") + .arg(&format!("{}#bar", foo.url())) + .with_stderr( + "\ +[UPDATING] git repository `file://[..]` +[UPDATING] `dummy-registry` index +", + ) + .run(); + p.cargo("update -p https://github.com/rust-lang/crates.io-index#bar") + .with_stderr( + "\ +[UPDATING] `dummy-registry` index +", + ) + .run(); + + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn locked_means_locked_yes_no_seriously_i_mean_locked() { + // this in theory exercises #2041 + Package::new("baz", "0.1.0").publish(); + Package::new("baz", "0.2.0").publish(); + Package::new("bar", "0.1.0").publish(); + + let foo = git::repo(&paths::root().join("override")) + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + baz = "*" + "#, + ) + .file("src/lib.rs", "") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + baz = "0.1" + + [replace] + "bar:0.1.0" = {{ git = '{}' }} + "#, + foo.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + p.cargo("check").with_stdout("").run(); + p.cargo("check").with_stdout("").run(); +} + +#[cargo_test] +fn override_wrong_name() { + Package::new("baz", "0.1.0").publish(); + + let foo = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + baz = "0.1" + + [replace] + "baz:0.1.0" = {{ git = '{}' }} + "#, + foo.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +[UPDATING] git repository [..] +[ERROR] failed to get `baz` as a dependency of package `foo v0.0.1 ([..])` + +Caused by: + no matching package for override `[..]baz@0.1.0` found + location searched: file://[..] + version required: =0.1.0 +", + ) + .run(); +} + +#[cargo_test] +fn override_with_nothing() { + Package::new("bar", "0.1.0").publish(); + + let foo = git::repo(&paths::root().join("override")) + .file("src/lib.rs", "") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + + [replace] + "bar:0.1.0" = {{ git = '{}' }} + "#, + foo.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +[UPDATING] git repository [..] +[ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([..])` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update file://[..] + +Caused by: + Could not find Cargo.toml in `[..]` +", + ) + .run(); +} + +#[cargo_test] +fn override_wrong_version() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [replace] + "bar:0.1.0" = { git = 'https://example.com', version = '0.2.0' } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]` + +Caused by: + replacements cannot specify a version requirement, but found one for `[..]bar@0.1.0` +", + ) + .run(); +} + +#[cargo_test] +fn multiple_specs() { + Package::new("bar", "0.1.0").publish(); + + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = {{ git = '{0}' }} + + [replace."https://github.com/rust-lang/crates.io-index#bar:0.1.0"] + git = '{0}' + "#, + bar.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +[UPDATING] git repository [..] +[ERROR] failed to get `bar` as a dependency of package `foo v0.0.1 ([..])` + +Caused by: + overlapping replacement specifications found: + + * [..] + * [..] + + both specifications match: bar v0.1.0 +", + ) + .run(); +} + +#[cargo_test] +fn test_override_dep() { + Package::new("bar", "0.1.0").publish(); + + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = {{ git = '{0}' }} + "#, + bar.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("test -p bar") + .with_status(101) + .with_stderr_contains( + "\ +error: There are multiple `bar` packages in your project, and the [..] +Please re-run this command with [..] + [..]#bar@0.1.0 + [..]#bar@0.1.0 +", + ) + .run(); +} + +#[cargo_test] +fn update() { + Package::new("bar", "0.1.0").publish(); + + let bar = git::repo(&paths::root().join("override")) + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn bar() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = {{ git = '{0}' }} + "#, + bar.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile").run(); + p.cargo("update") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] git repository `[..]` +", + ) + .run(); +} + +// foo -> near -> far +// near is overridden with itself +#[cargo_test] +fn no_override_self() { + let deps = git::repo(&paths::root().join("override")) + .file("far/Cargo.toml", &basic_manifest("far", "0.1.0")) + .file("far/src/lib.rs", "") + .file( + "near/Cargo.toml", + r#" + [package] + name = "near" + version = "0.1.0" + authors = [] + + [dependencies] + far = { path = "../far" } + "#, + ) + .file("near/src/lib.rs", "#![no_std] pub extern crate far;") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + near = {{ git = '{0}' }} + + [replace] + "near:0.1.0" = {{ git = '{0}' }} + "#, + deps.url() + ), + ) + .file("src/lib.rs", "#![no_std] pub extern crate near;") + .build(); + + p.cargo("check --verbose").run(); +} + +#[cargo_test] +fn override_an_override() { + Package::new("chrono", "0.2.0") + .dep("serde", "< 0.9") + .publish(); + Package::new("serde", "0.7.0") + .file("src/lib.rs", "pub fn serde07() {}") + .publish(); + Package::new("serde", "0.8.0") + .file("src/lib.rs", "pub fn serde08() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + chrono = "0.2" + serde = "0.8" + + [replace] + "chrono:0.2.0" = { path = "chrono" } + "serde:0.8.0" = { path = "serde" } + "#, + ) + .file( + "Cargo.lock", + r#" + [[package]] + name = "foo" + version = "0.0.1" + dependencies = [ + "chrono 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "chrono" + version = "0.2.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + replace = "chrono 0.2.0" + + [[package]] + name = "chrono" + version = "0.2.0" + dependencies = [ + "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "serde" + version = "0.7.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + + [[package]] + name = "serde" + version = "0.8.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + replace = "serde 0.8.0" + + [[package]] + name = "serde" + version = "0.8.0" + "#, + ) + .file( + "src/lib.rs", + " + extern crate chrono; + extern crate serde; + + pub fn foo() { + chrono::chrono(); + serde::serde08_override(); + } + ", + ) + .file( + "chrono/Cargo.toml", + r#" + [package] + name = "chrono" + version = "0.2.0" + authors = [] + + [dependencies] + serde = "< 0.9" + "#, + ) + .file( + "chrono/src/lib.rs", + " + extern crate serde; + pub fn chrono() { + serde::serde07(); + } + ", + ) + .file("serde/Cargo.toml", &basic_manifest("serde", "0.8.0")) + .file("serde/src/lib.rs", "pub fn serde08_override() {}") + .build(); + + p.cargo("check -v").run(); +} + +#[cargo_test] +fn overriding_nonexistent_no_spurious() { + Package::new("bar", "0.1.0").dep("baz", "0.1").publish(); + Package::new("baz", "0.1.0").publish(); + + let bar = git::repo(&paths::root().join("override")) + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + baz = { path = "baz" } + "#, + ) + .file("src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = {{ git = '{url}' }} + "baz:0.1.0" = {{ git = '{url}' }} + "#, + url = bar.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); + p.cargo("check") + .with_stderr( + "\ +[WARNING] package replacement is not used: [..]baz@0.1.0 +[FINISHED] [..] +", + ) + .with_stdout("") + .run(); +} + +#[cargo_test] +fn no_warnings_when_replace_is_used_in_another_workspace_member() { + Package::new("bar", "0.1.0").publish(); + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [ "first_crate", "second_crate"] + + [replace] + "bar:0.1.0" = { path = "local_bar" } + "#, + ) + .file( + "first_crate/Cargo.toml", + r#" + [package] + name = "first_crate" + version = "0.1.0" + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("first_crate/src/lib.rs", "") + .file( + "second_crate/Cargo.toml", + &basic_manifest("second_crate", "0.1.0"), + ) + .file("second_crate/src/lib.rs", "") + .file("local_bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("local_bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .cwd("first_crate") + .with_stdout("") + .with_stderr( + "\ +[UPDATING] `[..]` index +[CHECKING] bar v0.1.0 ([..]) +[CHECKING] first_crate v0.1.0 ([..]) +[FINISHED] [..]", + ) + .run(); + + p.cargo("check") + .cwd("second_crate") + .with_stdout("") + .with_stderr( + "\ +[CHECKING] second_crate v0.1.0 ([..]) +[FINISHED] [..]", + ) + .run(); +} + +#[cargo_test] +fn replace_to_path_dep() { + Package::new("bar", "0.1.0").dep("baz", "0.1").publish(); + Package::new("baz", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1.0" + + [replace] + "bar:0.1.0" = { path = "bar" } + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + baz = { path = "baz" } + "#, + ) + .file( + "bar/src/lib.rs", + "extern crate baz; pub fn bar() { baz::baz(); }", + ) + .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("bar/baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn override_with_default_feature() { + Package::new("another", "0.1.0").publish(); + Package::new("another", "0.1.1").dep("bar", "0.1").publish(); + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar", default-features = false } + another = "0.1" + another2 = { path = "another2" } + + [replace] + 'bar:0.1.0' = { path = "bar" } + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() { bar::bar(); }") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [features] + default = [] + "#, + ) + .file( + "bar/src/lib.rs", + r#" + #[cfg(feature = "default")] + pub fn bar() {} + "#, + ) + .file( + "another2/Cargo.toml", + r#" + [package] + name = "another2" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { version = "0.1", default-features = false } + "#, + ) + .file("another2/src/lib.rs", "") + .build(); + + p.cargo("run").run(); +} + +#[cargo_test] +fn override_plus_dep() { + Package::new("bar", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + + [replace] + 'bar:0.1.0' = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + foo = { path = ".." } + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("error: cyclic package dependency: [..]") + .run(); +} diff --git a/tests/testsuite/required_features.rs b/tests/testsuite/required_features.rs new file mode 100644 index 0000000..ac6c9d2 --- /dev/null +++ b/tests/testsuite/required_features.rs @@ -0,0 +1,1452 @@ +//! Tests for targets with `required-features`. + +use cargo_test_support::install::{ + assert_has_installed_exe, assert_has_not_installed_exe, cargo_home, +}; +use cargo_test_support::is_nightly; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::project; + +#[cargo_test] +fn build_bin_default_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + + [[bin]] + name = "foo" + required-features = ["a"] + "#, + ) + .file( + "src/main.rs", + r#" + extern crate foo; + + #[cfg(feature = "a")] + fn test() { + foo::foo(); + } + + fn main() {} + "#, + ) + .file("src/lib.rs", r#"#[cfg(feature = "a")] pub fn foo() {}"#) + .build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + + p.cargo("build --no-default-features").run(); + + p.cargo("build --bin=foo").run(); + assert!(p.bin("foo").is_file()); + + p.cargo("build --bin=foo --no-default-features") + .with_status(101) + .with_stderr( + "\ +error: target `foo` in package `foo` requires the features: `a` +Consider enabling them by passing, e.g., `--features=\"a\"` +", + ) + .run(); +} + +#[cargo_test] +fn build_bin_arg_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[bin]] + name = "foo" + required-features = ["a"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --features a").run(); + assert!(p.bin("foo").is_file()); +} + +#[cargo_test] +fn build_bin_multiple_required_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a", "b"] + a = [] + b = ["a"] + c = [] + + [[bin]] + name = "foo_1" + path = "src/foo_1.rs" + required-features = ["b", "c"] + + [[bin]] + name = "foo_2" + path = "src/foo_2.rs" + required-features = ["a"] + "#, + ) + .file("src/foo_1.rs", "fn main() {}") + .file("src/foo_2.rs", "fn main() {}") + .build(); + + p.cargo("build").run(); + + assert!(!p.bin("foo_1").is_file()); + assert!(p.bin("foo_2").is_file()); + + p.cargo("build --features c").run(); + + assert!(p.bin("foo_1").is_file()); + assert!(p.bin("foo_2").is_file()); + + p.cargo("build --no-default-features").run(); +} + +#[cargo_test] +fn build_example_default_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + + [[example]] + name = "foo" + required-features = ["a"] + "#, + ) + .file("examples/foo.rs", "fn main() {}") + .build(); + + p.cargo("build --example=foo").run(); + assert!(p.bin("examples/foo").is_file()); + + p.cargo("build --example=foo --no-default-features") + .with_status(101) + .with_stderr( + "\ +error: target `foo` in package `foo` requires the features: `a` +Consider enabling them by passing, e.g., `--features=\"a\"` +", + ) + .run(); +} + +#[cargo_test] +fn build_example_arg_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[example]] + name = "foo" + required-features = ["a"] + "#, + ) + .file("examples/foo.rs", "fn main() {}") + .build(); + + p.cargo("build --example=foo --features a").run(); + assert!(p.bin("examples/foo").is_file()); +} + +#[cargo_test] +fn build_example_multiple_required_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a", "b"] + a = [] + b = ["a"] + c = [] + + [[example]] + name = "foo_1" + required-features = ["b", "c"] + + [[example]] + name = "foo_2" + required-features = ["a"] + "#, + ) + .file("examples/foo_1.rs", "fn main() {}") + .file("examples/foo_2.rs", "fn main() {}") + .build(); + + p.cargo("build --example=foo_1") + .with_status(101) + .with_stderr( + "\ +error: target `foo_1` in package `foo` requires the features: `b`, `c` +Consider enabling them by passing, e.g., `--features=\"b c\"` +", + ) + .run(); + p.cargo("build --example=foo_2").run(); + + assert!(!p.bin("examples/foo_1").is_file()); + assert!(p.bin("examples/foo_2").is_file()); + + p.cargo("build --example=foo_1 --features c").run(); + p.cargo("build --example=foo_2 --features c").run(); + + assert!(p.bin("examples/foo_1").is_file()); + assert!(p.bin("examples/foo_2").is_file()); + + p.cargo("build --example=foo_1 --no-default-features") + .with_status(101) + .with_stderr( + "\ +error: target `foo_1` in package `foo` requires the features: `b`, `c` +Consider enabling them by passing, e.g., `--features=\"b c\"` +", + ) + .run(); + p.cargo("build --example=foo_2 --no-default-features") + .with_status(101) + .with_stderr( + "\ +error: target `foo_2` in package `foo` requires the features: `a` +Consider enabling them by passing, e.g., `--features=\"a\"` +", + ) + .run(); +} + +#[cargo_test] +fn test_default_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + + [[test]] + name = "foo" + required-features = ["a"] + "#, + ) + .file("tests/foo.rs", "#[test]\nfn test() {}") + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test test ... ok") + .run(); + + p.cargo("test --no-default-features") + .with_stderr("[FINISHED] test [unoptimized + debuginfo] target(s) in [..]") + .with_stdout("") + .run(); + + p.cargo("test --test=foo") + .with_stderr( + "\ +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test test ... ok") + .run(); + + p.cargo("test --test=foo --no-default-features") + .with_status(101) + .with_stderr( + "\ +error: target `foo` in package `foo` requires the features: `a` +Consider enabling them by passing, e.g., `--features=\"a\"` +", + ) + .run(); +} + +#[cargo_test] +fn test_arg_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[test]] + name = "foo" + required-features = ["a"] + "#, + ) + .file("tests/foo.rs", "#[test]\nfn test() {}") + .build(); + + p.cargo("test --features a") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test test ... ok") + .run(); +} + +#[cargo_test] +fn test_multiple_required_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a", "b"] + a = [] + b = ["a"] + c = [] + + [[test]] + name = "foo_1" + required-features = ["b", "c"] + + [[test]] + name = "foo_2" + required-features = ["a"] + "#, + ) + .file("tests/foo_1.rs", "#[test]\nfn test() {}") + .file("tests/foo_2.rs", "#[test]\nfn test() {}") + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo_2-[..][EXE])", + ) + .with_stdout_contains("test test ... ok") + .run(); + + p.cargo("test --features c") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo_1-[..][EXE]) +[RUNNING] [..] (target/debug/deps/foo_2-[..][EXE])", + ) + .with_stdout_contains_n("test test ... ok", 2) + .run(); + + p.cargo("test --no-default-features") + .with_stderr("[FINISHED] test [unoptimized + debuginfo] target(s) in [..]") + .with_stdout("") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_default_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + + [[bench]] + name = "foo" + required-features = ["a"] + "#, + ) + .file( + "benches/foo.rs", + r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + } + "#, + ) + .build(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test bench ... bench: [..]") + .run(); + + p.cargo("bench --no-default-features") + .with_stderr("[FINISHED] bench [optimized] target(s) in [..]".to_string()) + .with_stdout("") + .run(); + + p.cargo("bench --bench=foo") + .with_stderr( + "\ +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test bench ... bench: [..]") + .run(); + + p.cargo("bench --bench=foo --no-default-features") + .with_status(101) + .with_stderr( + "\ +error: target `foo` in package `foo` requires the features: `a` +Consider enabling them by passing, e.g., `--features=\"a\"` +", + ) + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_arg_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[bench]] + name = "foo" + required-features = ["a"] + "#, + ) + .file( + "benches/foo.rs", + r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + } + "#, + ) + .build(); + + p.cargo("bench --features a") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test bench ... bench: [..]") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn bench_multiple_required_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a", "b"] + a = [] + b = ["a"] + c = [] + + [[bench]] + name = "foo_1" + required-features = ["b", "c"] + + [[bench]] + name = "foo_2" + required-features = ["a"] + "#, + ) + .file( + "benches/foo_1.rs", + r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + } + "#, + ) + .file( + "benches/foo_2.rs", + r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + } + "#, + ) + .build(); + + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo_2-[..][EXE])", + ) + .with_stdout_contains("test bench ... bench: [..]") + .run(); + + p.cargo("bench --features c") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo_1-[..][EXE]) +[RUNNING] [..] (target/release/deps/foo_2-[..][EXE])", + ) + .with_stdout_contains_n("test bench ... bench: [..]", 2) + .run(); + + p.cargo("bench --no-default-features") + .with_stderr("[FINISHED] bench [optimized] target(s) in [..]") + .with_stdout("") + .run(); +} + +#[cargo_test] +fn install_default_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + + [[bin]] + name = "foo" + required-features = ["a"] + + [[example]] + name = "foo" + required-features = ["a"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("examples/foo.rs", "fn main() {}") + .build(); + + p.cargo("install --path .").run(); + assert_has_installed_exe(cargo_home(), "foo"); + p.cargo("uninstall foo").run(); + + p.cargo("install --path . --no-default-features") + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[WARNING] none of the package's binaries are available for install using the selected features + bin \"foo\" requires the features: `a` + example \"foo\" requires the features: `a` +Consider enabling some of the needed features by passing, e.g., `--features=\"a\"` +", + ) + .run(); + assert_has_not_installed_exe(cargo_home(), "foo"); + + p.cargo("install --path . --bin=foo").run(); + assert_has_installed_exe(cargo_home(), "foo"); + p.cargo("uninstall foo").run(); + + p.cargo("install --path . --bin=foo --no-default-features") + .with_status(101) + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 ([..]) +[ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be found at \ + `[..]target` + +Caused by: + target `foo` in package `foo` requires the features: `a` + Consider enabling them by passing, e.g., `--features=\"a\"` +", + ) + .run(); + assert_has_not_installed_exe(cargo_home(), "foo"); + + p.cargo("install --path . --example=foo").run(); + assert_has_installed_exe(cargo_home(), "foo"); + p.cargo("uninstall foo").run(); + + p.cargo("install --path . --example=foo --no-default-features") + .with_status(101) + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 ([..]) +[ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be found at \ + `[..]target` + +Caused by: + target `foo` in package `foo` requires the features: `a` + Consider enabling them by passing, e.g., `--features=\"a\"` +", + ) + .run(); + assert_has_not_installed_exe(cargo_home(), "foo"); +} + +#[cargo_test] +fn install_arg_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[bin]] + name = "foo" + required-features = ["a"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("install --features a").run(); + assert_has_installed_exe(cargo_home(), "foo"); + p.cargo("uninstall foo").run(); +} + +#[cargo_test] +fn install_multiple_required_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a", "b"] + a = [] + b = ["a"] + c = [] + + [[bin]] + name = "foo_1" + path = "src/foo_1.rs" + required-features = ["b", "c"] + + [[bin]] + name = "foo_2" + path = "src/foo_2.rs" + required-features = ["a"] + + [[example]] + name = "foo_3" + path = "src/foo_3.rs" + required-features = ["b", "c"] + + [[example]] + name = "foo_4" + path = "src/foo_4.rs" + required-features = ["a"] + "#, + ) + .file("src/foo_1.rs", "fn main() {}") + .file("src/foo_2.rs", "fn main() {}") + .file("src/foo_3.rs", "fn main() {}") + .file("src/foo_4.rs", "fn main() {}") + .build(); + + p.cargo("install --path .").run(); + assert_has_not_installed_exe(cargo_home(), "foo_1"); + assert_has_installed_exe(cargo_home(), "foo_2"); + assert_has_not_installed_exe(cargo_home(), "foo_3"); + assert_has_not_installed_exe(cargo_home(), "foo_4"); + p.cargo("uninstall foo").run(); + + p.cargo("install --path . --bins --examples").run(); + assert_has_not_installed_exe(cargo_home(), "foo_1"); + assert_has_installed_exe(cargo_home(), "foo_2"); + assert_has_not_installed_exe(cargo_home(), "foo_3"); + assert_has_installed_exe(cargo_home(), "foo_4"); + p.cargo("uninstall foo").run(); + + p.cargo("install --path . --features c").run(); + assert_has_installed_exe(cargo_home(), "foo_1"); + assert_has_installed_exe(cargo_home(), "foo_2"); + assert_has_not_installed_exe(cargo_home(), "foo_3"); + assert_has_not_installed_exe(cargo_home(), "foo_4"); + p.cargo("uninstall foo").run(); + + p.cargo("install --path . --features c --bins --examples") + .run(); + assert_has_installed_exe(cargo_home(), "foo_1"); + assert_has_installed_exe(cargo_home(), "foo_2"); + assert_has_installed_exe(cargo_home(), "foo_3"); + assert_has_installed_exe(cargo_home(), "foo_4"); + p.cargo("uninstall foo").run(); + + p.cargo("install --path . --no-default-features") + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[WARNING] none of the package's binaries are available for install using the selected features + bin \"foo_1\" requires the features: `b`, `c` + bin \"foo_2\" requires the features: `a` + example \"foo_3\" requires the features: `b`, `c` + example \"foo_4\" requires the features: `a` +Consider enabling some of the needed features by passing, e.g., `--features=\"b c\"` +", + ) + .run(); + p.cargo("install --path . --no-default-features --bins") + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 ([..]) +[WARNING] Target filter `bins` specified, but no targets matched. This is a no-op +[FINISHED] release [optimized] target(s) in [..] +[WARNING] none of the package's binaries are available for install using the selected features + bin \"foo_1\" requires the features: `b`, `c` + bin \"foo_2\" requires the features: `a` + example \"foo_3\" requires the features: `b`, `c` + example \"foo_4\" requires the features: `a` +Consider enabling some of the needed features by passing, e.g., `--features=\"b c\"` +", + ) + .run(); + p.cargo("install --path . --no-default-features --examples") + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 ([..]) +[WARNING] Target filter `examples` specified, but no targets matched. This is a no-op +[FINISHED] release [optimized] target(s) in [..] +[WARNING] none of the package's binaries are available for install using the selected features + bin \"foo_1\" requires the features: `b`, `c` + bin \"foo_2\" requires the features: `a` + example \"foo_3\" requires the features: `b`, `c` + example \"foo_4\" requires the features: `a` +Consider enabling some of the needed features by passing, e.g., `--features=\"b c\"` +", + ) + .run(); + p.cargo("install --path . --no-default-features --bins --examples") + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 ([..]) +[WARNING] Target filters `bins`, `examples` specified, but no targets matched. This is a no-op +[FINISHED] release [optimized] target(s) in [..] +[WARNING] none of the package's binaries are available for install using the selected features + bin \"foo_1\" requires the features: `b`, `c` + bin \"foo_2\" requires the features: `a` + example \"foo_3\" requires the features: `b`, `c` + example \"foo_4\" requires the features: `a` +Consider enabling some of the needed features by passing, e.g., `--features=\"b c\"` +", + ) + .run(); + assert_has_not_installed_exe(cargo_home(), "foo_1"); + assert_has_not_installed_exe(cargo_home(), "foo_2"); + assert_has_not_installed_exe(cargo_home(), "foo_3"); + assert_has_not_installed_exe(cargo_home(), "foo_4"); +} + +#[cargo_test] +fn dep_feature_in_toml() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar", features = ["a"] } + + [[bin]] + name = "foo" + required-features = ["bar/a"] + + [[example]] + name = "foo" + required-features = ["bar/a"] + + [[test]] + name = "foo" + required-features = ["bar/a"] + + [[bench]] + name = "foo" + required-features = ["bar/a"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("examples/foo.rs", "fn main() {}") + .file("tests/foo.rs", "#[test]\nfn test() {}") + .file( + "benches/foo.rs", + r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + a = [] + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + // bin + p.cargo("build --bin=foo").run(); + assert!(p.bin("foo").is_file()); + + // example + p.cargo("build --example=foo").run(); + assert!(p.bin("examples/foo").is_file()); + + // test + p.cargo("test --test=foo") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test test ... ok") + .run(); + + // bench + if is_nightly() { + p.cargo("bench --bench=foo") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test bench ... bench: [..]") + .run(); + } + + // install + p.cargo("install").run(); + assert_has_installed_exe(cargo_home(), "foo"); + p.cargo("uninstall foo").run(); +} + +#[cargo_test] +fn dep_feature_in_cmd_line() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar" } + + [[bin]] + name = "foo" + required-features = ["bar/a"] + + [[example]] + name = "foo" + required-features = ["bar/a"] + + [[test]] + name = "foo" + required-features = ["bar/a"] + + [[bench]] + name = "foo" + required-features = ["bar/a"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("examples/foo.rs", "fn main() {}") + .file( + "tests/foo.rs", + r#" + #[test] + fn bin_is_built() { + let s = format!("target/debug/foo{}", std::env::consts::EXE_SUFFIX); + let p = std::path::Path::new(&s); + assert!(p.exists(), "foo does not exist"); + } + "#, + ) + .file( + "benches/foo.rs", + r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + a = [] + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + // This is a no-op + p.cargo("build").with_stderr("[FINISHED] dev [..]").run(); + assert!(!p.bin("foo").is_file()); + + // bin + p.cargo("build --bin=foo") + .with_status(101) + .with_stderr( + "\ +error: target `foo` in package `foo` requires the features: `bar/a` +Consider enabling them by passing, e.g., `--features=\"bar/a\"` +", + ) + .run(); + + p.cargo("build --bin=foo --features bar/a").run(); + assert!(p.bin("foo").is_file()); + + // example + p.cargo("build --example=foo") + .with_status(101) + .with_stderr( + "\ +error: target `foo` in package `foo` requires the features: `bar/a` +Consider enabling them by passing, e.g., `--features=\"bar/a\"` +", + ) + .run(); + + p.cargo("build --example=foo --features bar/a").run(); + assert!(p.bin("examples/foo").is_file()); + + // test + // This is a no-op, since no tests are enabled + p.cargo("test") + .with_stderr("[FINISHED] test [unoptimized + debuginfo] target(s) in [..]") + .with_stdout("") + .run(); + + // Delete the target directory so this can check if the main.rs gets built. + p.build_dir().rm_rf(); + p.cargo("test --test=foo --features bar/a") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test bin_is_built ... ok") + .run(); + + // bench + if is_nightly() { + p.cargo("bench") + .with_stderr("[FINISHED] bench [optimized] target(s) in [..]") + .with_stdout("") + .run(); + + p.cargo("bench --bench=foo --features bar/a") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test bench ... bench: [..]") + .run(); + } + + // install + p.cargo("install --path .") + .with_stderr( + "\ +[INSTALLING] foo v0.0.1 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[WARNING] none of the package's binaries are available for install using the selected features + bin \"foo\" requires the features: `bar/a` + example \"foo\" requires the features: `bar/a` +Consider enabling some of the needed features by passing, e.g., `--features=\"bar/a\"` +", + ) + .run(); + assert_has_not_installed_exe(cargo_home(), "foo"); + + p.cargo("install --features bar/a").run(); + assert_has_installed_exe(cargo_home(), "foo"); + p.cargo("uninstall foo").run(); +} + +#[cargo_test] +fn test_skips_compiling_bin_with_missing_required_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + a = [] + + [[bin]] + name = "bin_foo" + path = "src/bin/foo.rs" + required-features = ["a"] + "#, + ) + .file("src/bin/foo.rs", "extern crate bar; fn main() {}") + .file("tests/foo.rs", "") + .file("benches/foo.rs", "") + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("running 0 tests") + .run(); + + p.cargo("test --features a -j 1") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +error[E0463]: can't find crate for `bar`", + ) + .run(); + + if is_nightly() { + p.cargo("bench") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] [..] (target/release/deps/foo-[..][EXE])", + ) + .with_stdout_contains("running 0 tests") + .run(); + + p.cargo("bench --features a -j 1") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +error[E0463]: can't find crate for `bar`", + ) + .run(); + } +} + +#[cargo_test] +fn run_default() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = [] + a = [] + + [[bin]] + name = "foo" + required-features = ["a"] + "#, + ) + .file("src/lib.rs", "") + .file("src/main.rs", "extern crate foo; fn main() {}") + .build(); + + p.cargo("run") + .with_status(101) + .with_stderr( + "\ +error: target `foo` in package `foo` requires the features: `a` +Consider enabling them by passing, e.g., `--features=\"a\"` +", + ) + .run(); + + p.cargo("run --features a").run(); +} + +#[cargo_test] +fn run_default_multiple_required_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + default = ["a"] + a = [] + b = [] + + [[bin]] + name = "foo1" + path = "src/foo1.rs" + required-features = ["a"] + + [[bin]] + name = "foo3" + path = "src/foo3.rs" + required-features = ["b"] + + [[bin]] + name = "foo2" + path = "src/foo2.rs" + required-features = ["b"] + "#, + ) + .file("src/lib.rs", "") + .file("src/foo1.rs", "extern crate foo; fn main() {}") + .file("src/foo3.rs", "extern crate foo; fn main() {}") + .file("src/foo2.rs", "extern crate foo; fn main() {}") + .build(); + + p.cargo("run") + .with_status(101) + .with_stderr( + "\ +error: `cargo run` could not determine which binary to run[..] +available binaries: foo1, foo2, foo3", + ) + .run(); +} + +#[cargo_test] +fn renamed_required_features() { + // Test that required-features uses renamed package feature names. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [[bin]] + name = "x" + required-features = ["a1/f1"] + + [dependencies] + a1 = {path="a1", package="a"} + a2 = {path="a2", package="a"} + "#, + ) + .file( + "src/bin/x.rs", + r#" + fn main() { + a1::f(); + a2::f(); + } + "#, + ) + .file( + "a1/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [features] + f1 = [] + "#, + ) + .file( + "a1/src/lib.rs", + r#" + pub fn f() { + if cfg!(feature="f1") { + println!("a1 f1"); + } + } + "#, + ) + .file( + "a2/Cargo.toml", + r#" + [package] + name = "a" + version = "0.2.0" + + [features] + f2 = [] + "#, + ) + .file( + "a2/src/lib.rs", + r#" + pub fn f() { + if cfg!(feature="f2") { + println!("a2 f2"); + } + } + "#, + ) + .build(); + + p.cargo("run") + .with_status(101) + .with_stderr( + "\ +[ERROR] target `x` in package `foo` requires the features: `a1/f1` +Consider enabling them by passing, e.g., `--features=\"a1/f1\"` +", + ) + .run(); + + p.cargo("build --features a1/f1").run(); + p.rename_run("x", "x_with_f1").with_stdout("a1 f1").run(); + + p.cargo("build --features a1/f1,a2/f2").run(); + p.rename_run("x", "x_with_f1_f2") + .with_stdout("a1 f1\na2 f2") + .run(); +} + +#[cargo_test] +fn truncated_install_warning_message() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + + [features] + feature1 = [] + feature2 = [] + feature3 = [] + feature4 = [] + feature5 = [] + + [[bin]] + name = "foo1" + required-features = ["feature1", "feature2", "feature3"] + + [[bin]] + name = "foo2" + required-features = ["feature2"] + + [[bin]] + name = "foo3" + required-features = ["feature3"] + + [[bin]] + name = "foo4" + required-features = ["feature4", "feature1"] + + [[bin]] + name = "foo5" + required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] + + [[bin]] + name = "foo6" + required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] + + [[bin]] + name = "foo7" + required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] + + [[bin]] + name = "foo8" + required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] + + [[bin]] + name = "foo9" + required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] + + [[bin]] + name = "foo10" + required-features = ["feature1", "feature2", "feature3", "feature4", "feature5"] + + [[example]] + name = "example1" + required-features = ["feature1", "feature2"] + "#, + ) + .file("src/bin/foo1.rs", "fn main() {}") + .file("src/bin/foo2.rs", "fn main() {}") + .file("src/bin/foo3.rs", "fn main() {}") + .file("src/bin/foo4.rs", "fn main() {}") + .file("src/bin/foo5.rs", "fn main() {}") + .file("src/bin/foo6.rs", "fn main() {}") + .file("src/bin/foo7.rs", "fn main() {}") + .file("src/bin/foo8.rs", "fn main() {}") + .file("src/bin/foo9.rs", "fn main() {}") + .file("src/bin/foo10.rs", "fn main() {}") + .file("examples/example1.rs", "fn main() {}") + .build(); + + p.cargo("install --path .").with_stderr("\ +[INSTALLING] foo v0.1.0 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[WARNING] none of the package's binaries are available for install using the selected features + bin \"foo1\" requires the features: `feature1`, `feature2`, `feature3` + bin \"foo2\" requires the features: `feature2` + bin \"foo3\" requires the features: `feature3` + bin \"foo4\" requires the features: `feature4`, `feature1` + bin \"foo5\" requires the features: `feature1`, `feature2`, `feature3`, `feature4`, `feature5` + bin \"foo6\" requires the features: `feature1`, `feature2`, `feature3`, `feature4`, `feature5` + bin \"foo7\" requires the features: `feature1`, `feature2`, `feature3`, `feature4`, `feature5` +4 more targets also requires features not enabled. See them in the Cargo.toml file. +Consider enabling some of the needed features by passing, e.g., `--features=\"feature1 feature2 feature3\"`").run(); +} diff --git a/tests/testsuite/run.rs b/tests/testsuite/run.rs new file mode 100644 index 0000000..c29a6a3 --- /dev/null +++ b/tests/testsuite/run.rs @@ -0,0 +1,1510 @@ +//! Tests for the `cargo run` command. + +use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, project, Project}; +use cargo_util::paths::dylib_path_envvar; + +#[cargo_test] +fn simple() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]`", + ) + .with_stdout("hello") + .run(); + assert!(p.bin("foo").is_file()); +} + +#[cargo_test] +fn quiet_arg() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("run -q").with_stderr("").with_stdout("hello").run(); + + p.cargo("run --quiet") + .with_stderr("") + .with_stdout("hello") + .run(); +} + +#[cargo_test] +fn quiet_arg_and_verbose_arg() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("run -q -v") + .with_status(101) + .with_stderr("[ERROR] cannot set both --verbose and --quiet") + .run(); +} + +#[cargo_test] +fn quiet_arg_and_verbose_config() { + let p = project() + .file( + ".cargo/config", + r#" + [term] + verbose = true + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("run -q").with_stderr("").with_stdout("hello").run(); +} + +#[cargo_test] +fn verbose_arg_and_quiet_config() { + let p = project() + .file( + ".cargo/config", + r#" + [term] + quiet = true + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("run -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]`", + ) + .with_stdout("hello") + .run(); +} + +#[cargo_test] +fn quiet_config_alone() { + let p = project() + .file( + ".cargo/config", + r#" + [term] + quiet = true + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("run").with_stderr("").with_stdout("hello").run(); +} + +#[cargo_test] +fn verbose_config_alone() { + let p = project() + .file( + ".cargo/config", + r#" + [term] + verbose = true + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]`", + ) + .with_stdout("hello") + .run(); +} + +#[cargo_test] +fn quiet_config_and_verbose_config() { + let p = project() + .file( + ".cargo/config", + r#" + [term] + verbose = true + quiet = true + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("run") + .with_status(101) + .with_stderr("[ERROR] cannot set both `term.verbose` and `term.quiet`") + .run(); +} + +#[cargo_test] +fn simple_with_args() { + let p = project() + .file( + "src/main.rs", + r#" + fn main() { + assert_eq!(std::env::args().nth(1).unwrap(), "hello"); + assert_eq!(std::env::args().nth(2).unwrap(), "world"); + } + "#, + ) + .build(); + + p.cargo("run hello world").run(); +} + +#[cfg(unix)] +#[cargo_test] +fn simple_with_non_utf8_args() { + use std::os::unix::ffi::OsStrExt; + + let p = project() + .file( + "src/main.rs", + r#" + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + + fn main() { + assert_eq!(std::env::args_os().nth(1).unwrap(), OsStr::from_bytes(b"hello")); + assert_eq!(std::env::args_os().nth(2).unwrap(), OsStr::from_bytes(b"ab\xffcd")); + } + "#, + ) + .build(); + + p.cargo("run") + .arg("hello") + .arg(std::ffi::OsStr::from_bytes(b"ab\xFFcd")) + .run(); +} + +#[cargo_test] +fn exit_code() { + let p = project() + .file("src/main.rs", "fn main() { std::process::exit(2); }") + .build(); + + let mut output = String::from( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target[..]` +", + ); + if !cfg!(unix) { + output.push_str( + "[ERROR] process didn't exit successfully: `target[..]foo[..]` (exit [..]: 2)", + ); + } + p.cargo("run").with_status(2).with_stderr(output).run(); +} + +#[cargo_test] +fn exit_code_verbose() { + let p = project() + .file("src/main.rs", "fn main() { std::process::exit(2); }") + .build(); + + let mut output = String::from( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target[..]` +", + ); + if !cfg!(unix) { + output.push_str( + "[ERROR] process didn't exit successfully: `target[..]foo[..]` (exit [..]: 2)", + ); + } + + p.cargo("run -v").with_status(2).with_stderr(output).run(); +} + +#[cargo_test] +fn no_main_file() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("run") + .with_status(101) + .with_stderr( + "[ERROR] a bin target must be available \ + for `cargo run`\n", + ) + .run(); +} + +#[cargo_test] +fn too_many_bins() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", "") + .file("src/bin/b.rs", "") + .build(); + + // Using [..] here because the order is not stable + p.cargo("run") + .with_status(101) + .with_stderr( + "[ERROR] `cargo run` could not determine which binary to run. \ + Use the `--bin` option to specify a binary, or the \ + `default-run` manifest key.\ + \navailable binaries: [..]\n", + ) + .run(); +} + +#[cargo_test] +fn specify_name() { + let p = project() + .file("src/lib.rs", "") + .file( + "src/bin/a.rs", + r#" + #[allow(unused_extern_crates)] + extern crate foo; + fn main() { println!("hello a.rs"); } + "#, + ) + .file( + "src/bin/b.rs", + r#" + #[allow(unused_extern_crates)] + extern crate foo; + fn main() { println!("hello b.rs"); } + "#, + ) + .build(); + + p.cargo("run --bin a -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc [..] src/lib.rs [..]` +[RUNNING] `rustc [..] src/bin/a.rs [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/a[EXE]`", + ) + .with_stdout("hello a.rs") + .run(); + + p.cargo("run --bin b -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] src/bin/b.rs [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/b[EXE]`", + ) + .with_stdout("hello b.rs") + .run(); +} + +#[cargo_test] +fn specify_default_run() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + default-run = "a" + "#, + ) + .file("src/lib.rs", "") + .file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#) + .file("src/bin/b.rs", r#"fn main() { println!("hello B"); }"#) + .build(); + + p.cargo("run").with_stdout("hello A").run(); + p.cargo("run --bin a").with_stdout("hello A").run(); + p.cargo("run --bin b").with_stdout("hello B").run(); +} + +#[cargo_test] +fn bogus_default_run() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + default-run = "b" + "#, + ) + .file("src/lib.rs", "") + .file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#) + .build(); + + p.cargo("run") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + default-run target `b` not found + + Did you mean `a`? +", + ) + .run(); +} + +#[cargo_test] +fn run_example() { + let p = project() + .file("src/lib.rs", "") + .file("examples/a.rs", r#"fn main() { println!("example"); }"#) + .file("src/bin/a.rs", r#"fn main() { println!("bin"); }"#) + .build(); + + p.cargo("run --example a") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/examples/a[EXE]`", + ) + .with_stdout("example") + .run(); +} + +#[cargo_test] +fn run_library_example() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + [[example]] + name = "bar" + crate_type = ["lib"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/bar.rs", "fn foo() {}") + .build(); + + p.cargo("run --example bar") + .with_status(101) + .with_stderr("[ERROR] example target `bar` is a library and cannot be executed") + .run(); +} + +#[cargo_test] +fn run_bin_example() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + [[example]] + name = "bar" + crate_type = ["bin"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/bar.rs", r#"fn main() { println!("example"); }"#) + .build(); + + p.cargo("run --example bar") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/examples/bar[EXE]`", + ) + .with_stdout("example") + .run(); +} + +fn autodiscover_examples_project(rust_edition: &str, autoexamples: Option) -> Project { + let autoexamples = match autoexamples { + None => "".to_string(), + Some(bool) => format!("autoexamples = {}", bool), + }; + project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + edition = "{rust_edition}" + {autoexamples} + + [features] + magic = [] + + [[example]] + name = "do_magic" + required-features = ["magic"] + "#, + rust_edition = rust_edition, + autoexamples = autoexamples + ), + ) + .file("examples/a.rs", r#"fn main() { println!("example"); }"#) + .file( + "examples/do_magic.rs", + r#" + fn main() { println!("magic example"); } + "#, + ) + .build() +} + +#[cargo_test] +fn run_example_autodiscover_2015() { + let p = autodiscover_examples_project("2015", None); + p.cargo("run --example a") + .with_status(101) + .with_stderr( + "warning: \ +An explicit [[example]] section is specified in Cargo.toml which currently +disables Cargo from automatically inferring other example targets. +This inference behavior will change in the Rust 2018 edition and the following +files will be included as a example target: + +* [..]a.rs + +This is likely to break cargo build or cargo test as these files may not be +ready to be compiled as a example target today. You can future-proof yourself +and disable this warning by adding `autoexamples = false` to your [package] +section. You may also move the files to a location where Cargo would not +automatically infer them to be a target, such as in subfolders. + +For more information on this warning you can consult +https://github.com/rust-lang/cargo/issues/5330 +error: no example target named `a`. +Available example targets: + do_magic + +", + ) + .run(); +} + +#[cargo_test] +fn run_example_autodiscover_2015_with_autoexamples_enabled() { + let p = autodiscover_examples_project("2015", Some(true)); + p.cargo("run --example a") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/examples/a[EXE]`", + ) + .with_stdout("example") + .run(); +} + +#[cargo_test] +fn run_example_autodiscover_2015_with_autoexamples_disabled() { + let p = autodiscover_examples_project("2015", Some(false)); + p.cargo("run --example a") + .with_status(101) + .with_stderr( + "\ +error: no example target named `a`. +Available example targets: + do_magic + +", + ) + .run(); +} + +#[cargo_test] +fn run_example_autodiscover_2018() { + let p = autodiscover_examples_project("2018", None); + p.cargo("run --example a") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/examples/a[EXE]`", + ) + .with_stdout("example") + .run(); +} + +#[cargo_test] +fn autobins_disables() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + autobins = false + "#, + ) + .file("src/lib.rs", "pub mod bin;") + .file("src/bin/mod.rs", "// empty") + .build(); + + p.cargo("run") + .with_status(101) + .with_stderr("[ERROR] a bin target must be available for `cargo run`") + .run(); +} + +#[cargo_test] +#[ignore = "temporarily disabled for beta due to clap update"] +fn run_bins() { + let p = project() + .file("src/lib.rs", "") + .file("examples/a.rs", r#"fn main() { println!("example"); }"#) + .file("src/bin/a.rs", r#"fn main() { println!("bin"); }"#) + .build(); + + p.cargo("run --bins") + .with_status(1) + .with_stderr_contains( + "\ +error: unexpected argument '--bins' found + + note: argument '--bin' exists", + ) + .run(); +} + +#[cargo_test] +fn run_with_filename() { + let p = project() + .file("src/lib.rs", "") + .file( + "src/bin/a.rs", + r#" + extern crate foo; + fn main() { println!("hello a.rs"); } + "#, + ) + .file("examples/a.rs", r#"fn main() { println!("example"); }"#) + .build(); + + p.cargo("run --bin bin.rs") + .with_status(101) + .with_stderr( + "\ +[ERROR] no bin target named `bin.rs`. +Available bin targets: + a + +", + ) + .run(); + + p.cargo("run --bin a.rs") + .with_status(101) + .with_stderr( + "\ +[ERROR] no bin target named `a.rs` + +Did you mean `a`?", + ) + .run(); + + p.cargo("run --example example.rs") + .with_status(101) + .with_stderr( + "\ +[ERROR] no example target named `example.rs`. +Available example targets: + a + +", + ) + .run(); + + p.cargo("run --example a.rs") + .with_status(101) + .with_stderr( + "\ +[ERROR] no example target named `a.rs` + +Did you mean `a`?", + ) + .run(); +} + +#[cargo_test] +fn either_name_or_example() { + let p = project() + .file("src/bin/a.rs", r#"fn main() { println!("hello a.rs"); }"#) + .file("examples/b.rs", r#"fn main() { println!("hello b.rs"); }"#) + .build(); + + p.cargo("run --bin a --example b") + .with_status(101) + .with_stderr( + "[ERROR] `cargo run` can run at most one \ + executable, but multiple were \ + specified", + ) + .run(); +} + +#[cargo_test] +fn one_bin_multiple_examples() { + let p = project() + .file("src/lib.rs", "") + .file( + "src/bin/main.rs", + r#"fn main() { println!("hello main.rs"); }"#, + ) + .file("examples/a.rs", r#"fn main() { println!("hello a.rs"); }"#) + .file("examples/b.rs", r#"fn main() { println!("hello b.rs"); }"#) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/main[EXE]`", + ) + .with_stdout("hello main.rs") + .run(); +} + +#[cargo_test] +fn example_with_release_flag() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + version = "*" + path = "bar" + "#, + ) + .file( + "examples/a.rs", + r#" + extern crate bar; + + fn main() { + if cfg!(debug_assertions) { + println!("slow1") + } else { + println!("fast1") + } + bar::baz(); + } + "#, + ) + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file( + "bar/src/bar.rs", + r#" + pub fn baz() { + if cfg!(debug_assertions) { + println!("slow2") + } else { + println!("fast2") + } + } + "#, + ) + .build(); + + p.cargo("run -v --release --example a") + .with_stderr( + "\ +[COMPILING] bar v0.5.0 ([CWD]/bar) +[RUNNING] `rustc --crate-name bar bar/src/bar.rs [..]--crate-type lib \ + --emit=[..]link \ + -C opt-level=3[..]\ + -C metadata=[..] \ + --out-dir [CWD]/target/release/deps \ + -L dependency=[CWD]/target/release/deps` +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name a examples/a.rs [..]--crate-type bin \ + --emit=[..]link \ + -C opt-level=3[..]\ + -C metadata=[..] \ + --out-dir [CWD]/target/release/examples \ + -L dependency=[CWD]/target/release/deps \ + --extern bar=[CWD]/target/release/deps/libbar-[..].rlib` +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] `target/release/examples/a[EXE]` +", + ) + .with_stdout( + "\ +fast1 +fast2", + ) + .run(); + + p.cargo("run -v --example a") + .with_stderr( + "\ +[COMPILING] bar v0.5.0 ([CWD]/bar) +[RUNNING] `rustc --crate-name bar bar/src/bar.rs [..]--crate-type lib \ + --emit=[..]link[..]\ + -C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [CWD]/target/debug/deps \ + -L dependency=[CWD]/target/debug/deps` +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name a examples/a.rs [..]--crate-type bin \ + --emit=[..]link[..]\ + -C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [CWD]/target/debug/examples \ + -L dependency=[CWD]/target/debug/deps \ + --extern bar=[CWD]/target/debug/deps/libbar-[..].rlib` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/examples/a[EXE]` +", + ) + .with_stdout( + "\ +slow1 +slow2", + ) + .run(); +} + +#[cargo_test] +fn run_dylib_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#, + ) + .file( + "src/main.rs", + r#"extern crate bar; fn main() { bar::bar(); }"#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + name = "bar" + crate-type = ["dylib"] + "#, + ) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("run hello world").run(); +} + +#[cargo_test] +fn run_with_bin_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar" + "#, + ) + .file("bar/src/main.rs", r#"fn main() { println!("bar"); }"#) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[WARNING] foo v0.0.1 ([CWD]) ignoring invalid dependency `bar` which is missing a lib target +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]`", + ) + .with_stdout("hello") + .run(); +} + +#[cargo_test] +fn run_with_bin_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies.bar1] + path = "bar1" + [dependencies.bar2] + path = "bar2" + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file( + "bar1/Cargo.toml", + r#" + [package] + name = "bar1" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar1" + "#, + ) + .file("bar1/src/main.rs", r#"fn main() { println!("bar1"); }"#) + .file( + "bar2/Cargo.toml", + r#" + [package] + name = "bar2" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar2" + "#, + ) + .file("bar2/src/main.rs", r#"fn main() { println!("bar2"); }"#) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[WARNING] foo v0.0.1 ([CWD]) ignoring invalid dependency `bar1` which is missing a lib target +[WARNING] foo v0.0.1 ([CWD]) ignoring invalid dependency `bar2` which is missing a lib target +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]`", + ) + .with_stdout("hello") + .run(); +} + +#[cargo_test] +fn run_with_bin_dep_in_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo1", "foo2"] + "#, + ) + .file( + "foo1/Cargo.toml", + r#" + [package] + name = "foo1" + version = "0.0.1" + + [dependencies.bar1] + path = "bar1" + "#, + ) + .file("foo1/src/main.rs", r#"fn main() { println!("hello"); }"#) + .file( + "foo1/bar1/Cargo.toml", + r#" + [package] + name = "bar1" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar1" + "#, + ) + .file( + "foo1/bar1/src/main.rs", + r#"fn main() { println!("bar1"); }"#, + ) + .file( + "foo2/Cargo.toml", + r#" + [package] + name = "foo2" + version = "0.0.1" + + [dependencies.bar2] + path = "bar2" + "#, + ) + .file("foo2/src/main.rs", r#"fn main() { println!("hello"); }"#) + .file( + "foo2/bar2/Cargo.toml", + r#" + [package] + name = "bar2" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar2" + "#, + ) + .file( + "foo2/bar2/src/main.rs", + r#"fn main() { println!("bar2"); }"#, + ) + .build(); + + p.cargo("run") + .with_status(101) + .with_stderr( + "\ +[ERROR] `cargo run` could not determine which binary to run[..] +available binaries: bar1, bar2, foo1, foo2", + ) + .run(); + + p.cargo("run --bin foo1") + .with_stderr( + "\ +[WARNING] foo1 v0.0.1 ([CWD]/foo1) ignoring invalid dependency `bar1` which is missing a lib target +[WARNING] foo2 v0.0.1 ([CWD]/foo2) ignoring invalid dependency `bar2` which is missing a lib target +[COMPILING] foo1 v0.0.1 ([CWD]/foo1) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo1[EXE]`", + ) + .with_stdout("hello") + .run(); +} + +#[cargo_test] +fn release_works() { + let p = project() + .file( + "src/main.rs", + r#" + fn main() { if cfg!(debug_assertions) { panic!() } } + "#, + ) + .build(); + + p.cargo("run --release") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] `target/release/foo[EXE]` +", + ) + .run(); + assert!(p.release_bin("foo").is_file()); +} + +#[cargo_test] +fn release_short_works() { + let p = project() + .file( + "src/main.rs", + r#" + fn main() { if cfg!(debug_assertions) { panic!() } } + "#, + ) + .build(); + + p.cargo("run -r") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] `target/release/foo[EXE]` +", + ) + .run(); + assert!(p.release_bin("foo").is_file()); +} + +#[cargo_test] +fn run_bin_different_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar" + "#, + ) + .file("src/bar.rs", "fn main() {}") + .build(); + + p.cargo("run").run(); +} + +#[cargo_test] +fn dashes_are_forwarded() { + let p = project() + .file( + "src/bin/bar.rs", + r#" + fn main() { + let s: Vec = std::env::args().collect(); + assert_eq!(s[1], "--"); + assert_eq!(s[2], "a"); + assert_eq!(s[3], "--"); + assert_eq!(s[4], "b"); + } + "#, + ) + .build(); + + p.cargo("run -- -- a -- b").run(); +} + +#[cargo_test] +fn run_from_executable_folder() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + let cwd = p.root().join("target").join("debug"); + p.cargo("build").run(); + + p.cargo("run") + .cwd(cwd) + .with_stderr( + "[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n\ + [RUNNING] `./foo[EXE]`", + ) + .with_stdout("hello") + .run(); +} + +#[cargo_test] +fn run_with_library_paths() { + let p = project(); + + // Only link search directories within the target output directory are + // propagated through to dylib_path_envvar() (see #3366). + let mut dir1 = p.target_debug_dir(); + dir1.push("foo\\backslash"); + + let mut dir2 = p.target_debug_dir(); + dir2.push("dir=containing=equal=signs"); + + let p = p + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + &format!( + r##" + fn main() {{ + println!(r#"cargo:rustc-link-search=native={}"#); + println!(r#"cargo:rustc-link-search={}"#); + }} + "##, + dir1.display(), + dir2.display() + ), + ) + .file( + "src/main.rs", + &format!( + r##" + fn main() {{ + let search_path = std::env::var_os("{}").unwrap(); + let paths = std::env::split_paths(&search_path).collect::>(); + println!("{{:#?}}", paths); + assert!(paths.contains(&r#"{}"#.into())); + assert!(paths.contains(&r#"{}"#.into())); + }} + "##, + dylib_path_envvar(), + dir1.display(), + dir2.display() + ), + ) + .build(); + + p.cargo("run").run(); +} + +#[cargo_test] +fn library_paths_sorted_alphabetically() { + let p = project(); + + let mut dir1 = p.target_debug_dir(); + dir1.push("zzzzzzz"); + + let mut dir2 = p.target_debug_dir(); + dir2.push("BBBBBBB"); + + let mut dir3 = p.target_debug_dir(); + dir3.push("aaaaaaa"); + + let p = p + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file( + "build.rs", + &format!( + r##" + fn main() {{ + println!(r#"cargo:rustc-link-search=native={}"#); + println!(r#"cargo:rustc-link-search=native={}"#); + println!(r#"cargo:rustc-link-search=native={}"#); + }} + "##, + dir1.display(), + dir2.display(), + dir3.display() + ), + ) + .file( + "src/main.rs", + &format!( + r##" + fn main() {{ + let search_path = std::env::var_os("{}").unwrap(); + let paths = std::env::split_paths(&search_path).collect::>(); + // ASCII case-sensitive sort + assert_eq!("BBBBBBB", paths[0].file_name().unwrap().to_string_lossy()); + assert_eq!("aaaaaaa", paths[1].file_name().unwrap().to_string_lossy()); + assert_eq!("zzzzzzz", paths[2].file_name().unwrap().to_string_lossy()); + }} + "##, + dylib_path_envvar() + ), + ) + .build(); + + p.cargo("run").run(); +} + +#[cargo_test] +fn fail_no_extra_verbose() { + let p = project() + .file("src/main.rs", "fn main() { std::process::exit(1); }") + .build(); + + p.cargo("run -q") + .with_status(1) + .with_stdout("") + .with_stderr("") + .run(); +} + +#[cargo_test] +fn run_multiple_packages() { + let p = project() + .no_manifest() + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [workspace] + + [dependencies] + d1 = { path = "d1" } + d2 = { path = "d2" } + d3 = { path = "../d3" } # outside of the workspace + + [[bin]] + name = "foo" + "#, + ) + .file("foo/src/foo.rs", "fn main() { println!(\"foo\"); }") + .file("foo/d1/Cargo.toml", &basic_bin_manifest("d1")) + .file("foo/d1/src/lib.rs", "") + .file("foo/d1/src/main.rs", "fn main() { println!(\"d1\"); }") + .file("foo/d2/Cargo.toml", &basic_bin_manifest("d2")) + .file("foo/d2/src/main.rs", "fn main() { println!(\"d2\"); }") + .file("d3/Cargo.toml", &basic_bin_manifest("d3")) + .file("d3/src/main.rs", "fn main() { println!(\"d2\"); }") + .build(); + + let cargo = || { + let mut process_builder = p.cargo("run"); + process_builder.cwd("foo"); + process_builder + }; + + cargo().arg("-p").arg("d1").with_stdout("d1").run(); + + cargo() + .arg("-p") + .arg("d2") + .arg("--bin") + .arg("d2") + .with_stdout("d2") + .run(); + + cargo().with_stdout("foo").run(); + + cargo() + .arg("-p") + .arg("d1") + .arg("-p") + .arg("d2") + .with_status(1) + .with_stderr_contains( + "error: the argument '--package []' cannot be used multiple times", + ) + .run(); + + cargo() + .arg("-p") + .arg("d3") + .with_status(101) + .with_stderr_contains("[ERROR] package(s) `d3` not found in workspace [..]") + .run(); + + cargo() + .arg("-p") + .arg("d*") + .with_status(101) + .with_stderr_contains( + "[ERROR] `cargo run` does not support glob pattern `d*` on package selection", + ) + .run(); +} + +#[cargo_test] +fn explicit_bin_with_args() { + let p = project() + .file( + "src/main.rs", + r#" + fn main() { + assert_eq!(std::env::args().nth(1).unwrap(), "hello"); + assert_eq!(std::env::args().nth(2).unwrap(), "world"); + } + "#, + ) + .build(); + + p.cargo("run --bin foo hello world").run(); +} + +#[cargo_test] +fn run_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file("a/Cargo.toml", &basic_bin_manifest("a")) + .file("a/src/main.rs", r#"fn main() {println!("run-a");}"#) + .file("b/Cargo.toml", &basic_bin_manifest("b")) + .file("b/src/main.rs", r#"fn main() {println!("run-b");}"#) + .build(); + + p.cargo("run") + .with_status(101) + .with_stderr( + "\ +[ERROR] `cargo run` could not determine which binary to run[..] +available binaries: a, b", + ) + .run(); + p.cargo("run --bin a").with_stdout("run-a").run(); +} + +#[cargo_test] +fn default_run_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.0.1" + default-run = "a" + "#, + ) + .file("a/src/main.rs", r#"fn main() {println!("run-a");}"#) + .file("b/Cargo.toml", &basic_bin_manifest("b")) + .file("b/src/main.rs", r#"fn main() {println!("run-b");}"#) + .build(); + + p.cargo("run").with_stdout("run-a").run(); +} + +#[cargo_test] +#[cfg(target_os = "macos")] +fn run_link_system_path_macos() { + use cargo_test_support::paths::{self, CargoPathExt}; + use std::fs; + // Check that the default system library path is honored. + // First, build a shared library that will be accessed from + // DYLD_FALLBACK_LIBRARY_PATH. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + [lib] + crate-type = ["cdylib"] + "#, + ) + .file( + "src/lib.rs", + "#[no_mangle] pub extern fn something_shared() {}", + ) + .build(); + p.cargo("build").run(); + + // This is convoluted. Since this test can't modify things in /usr, + // this needs to dance around to check that things work. + // + // The default DYLD_FALLBACK_LIBRARY_PATH is: + // $(HOME)/lib:/usr/local/lib:/lib:/usr/lib + // + // This will make use of ~/lib in the path, but the default cc link + // path is /usr/lib:/usr/local/lib. So first need to build in one + // location, and then move it to ~/lib. + // + // 1. Build with rustc-link-search pointing to libfoo so the initial + // binary can be linked. + // 2. Move the library to ~/lib + // 3. Run `cargo run` to make sure it can still find the library in + // ~/lib. + // + // This should be equivalent to having the library in /usr/local/lib. + let p2 = project() + .at("bar") + .file("Cargo.toml", &basic_bin_manifest("bar")) + .file( + "src/main.rs", + r#" + extern { + fn something_shared(); + } + fn main() { + unsafe { something_shared(); } + } + "#, + ) + .file( + "build.rs", + &format!( + r#" + fn main() {{ + println!("cargo:rustc-link-lib=foo"); + println!("cargo:rustc-link-search={}"); + }} + "#, + p.target_debug_dir().display() + ), + ) + .build(); + p2.cargo("build").run(); + p2.cargo("test").run(); + + let libdir = paths::home().join("lib"); + fs::create_dir(&libdir).unwrap(); + fs::rename( + p.target_debug_dir().join("libfoo.dylib"), + libdir.join("libfoo.dylib"), + ) + .unwrap(); + p.root().rm_rf(); + const VAR: &str = "DYLD_FALLBACK_LIBRARY_PATH"; + // Reset DYLD_FALLBACK_LIBRARY_PATH so that we don't inherit anything that + // was set by the cargo that invoked the test. + p2.cargo("run").env_remove(VAR).run(); + p2.cargo("test").env_remove(VAR).run(); + // Ensure this still works when DYLD_FALLBACK_LIBRARY_PATH has + // a value set. + p2.cargo("run").env(VAR, &libdir).run(); + p2.cargo("test").env(VAR, &libdir).run(); +} diff --git a/tests/testsuite/rust_version.rs b/tests/testsuite/rust_version.rs new file mode 100644 index 0000000..91711cf --- /dev/null +++ b/tests/testsuite/rust_version.rs @@ -0,0 +1,194 @@ +//! Tests for targets with `rust-version`. + +use cargo_test_support::{project, registry::Package}; + +#[cargo_test] +fn rust_version_satisfied() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "1.1.1" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check").run(); + p.cargo("check --ignore-rust-version").run(); +} + +#[cargo_test] +fn rust_version_bad_caret() { + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "^1.43" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build() + .cargo("check") + .with_status(101) + .with_stderr( + "error: failed to parse manifest at `[..]`\n\n\ + Caused by:\n `rust-version` must be a value like \"1.32\"", + ) + .run(); +} + +#[cargo_test] +fn rust_version_bad_pre_release() { + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "1.43-beta.1" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build() + .cargo("check") + .with_status(101) + .with_stderr( + "error: failed to parse manifest at `[..]`\n\n\ + Caused by:\n `rust-version` must be a value like \"1.32\"", + ) + .run(); +} + +#[cargo_test] +fn rust_version_bad_nonsense() { + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "foodaddle" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build() + .cargo("check") + .with_status(101) + .with_stderr( + "error: failed to parse manifest at `[..]`\n\n\ + Caused by:\n `rust-version` must be a value like \"1.32\"", + ) + .run(); +} + +#[cargo_test] +fn rust_version_too_high() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "1.9876.0" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "error: package `foo v0.0.1 ([..])` cannot be built because it requires \ + rustc 1.9876.0 or newer, while the currently active rustc version is [..]\n\n", + ) + .run(); + p.cargo("check --ignore-rust-version").run(); +} + +#[cargo_test] +fn rust_version_dependency_fails() { + Package::new("bar", "0.0.1") + .rust_version("1.2345.0") + .file("src/lib.rs", "fn other_stuff() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + [dependencies] + bar = "0.0.1" + "#, + ) + .file("src/main.rs", "fn main(){}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + " Updating `[..]` index\n \ + Downloading crates ...\n \ + Downloaded bar v0.0.1 (registry `[..]`)\n\ + error: package `bar v0.0.1` cannot be built because it requires \ + rustc 1.2345.0 or newer, while the currently active rustc version is [..]\n\ + Either upgrade to rustc 1.2345.0 or newer, or use\n\ + cargo update -p bar@0.0.1 --precise ver\n\ + where `ver` is the latest version of `bar` supporting rustc [..]", + ) + .run(); + p.cargo("check --ignore-rust-version").run(); +} + +#[cargo_test] +fn rust_version_older_than_edition() { + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + rust-version = "1.1" + edition = "2018" + [[bin]] + name = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build() + .cargo("check") + .with_status(101) + .with_stderr_contains(" rust-version 1.1 is older than first version (1.31.0) required by the specified edition (2018)", + ) + .run(); +} diff --git a/tests/testsuite/rustc.rs b/tests/testsuite/rustc.rs new file mode 100644 index 0000000..65e0740 --- /dev/null +++ b/tests/testsuite/rustc.rs @@ -0,0 +1,794 @@ +//! Tests for the `cargo rustc` command. + +use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, basic_manifest, project}; + +const CARGO_RUSTC_ERROR: &str = + "[ERROR] extra arguments to `rustc` can only be passed to one target, consider filtering +the package by passing, e.g., `--lib` or `--bin NAME` to specify a single target"; + +#[cargo_test] +fn build_lib_for_foo() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("rustc --lib -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]-C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn lib() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("rustc --lib -v -- -C debug-assertions=off") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]-C debuginfo=2 \ + -C debug-assertions=off \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_main_and_allow_unstable_options() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("rustc -v --bin foo -- -C debug-assertions") + .with_stderr(format!( + "\ +[COMPILING] {name} v{version} ([CWD]) +[RUNNING] `rustc --crate-name {name} src/lib.rs [..]--crate-type lib \ + --emit=[..]link[..]-C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps` +[RUNNING] `rustc --crate-name {name} src/main.rs [..]--crate-type bin \ + --emit=[..]link[..]-C debuginfo=2 \ + -C debug-assertions \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency=[CWD]/target/debug/deps \ + --extern {name}=[CWD]/target/debug/deps/lib{name}-[..].rlib` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + name = "foo", + version = "0.0.1" + )) + .run(); +} + +#[cargo_test] +fn fails_when_trying_to_build_main_and_lib_with_args() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("rustc -v -- -C debug-assertions") + .with_status(101) + .with_stderr(CARGO_RUSTC_ERROR) + .run(); +} + +#[cargo_test] +fn build_with_args_to_one_of_multiple_binaries() { + let p = project() + .file("src/bin/foo.rs", "fn main() {}") + .file("src/bin/bar.rs", "fn main() {}") + .file("src/bin/baz.rs", "fn main() {}") + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("rustc -v --bin bar -- -C debug-assertions") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]\ + -C debuginfo=2 -C metadata=[..] \ + --out-dir [..]` +[RUNNING] `rustc --crate-name bar src/bin/bar.rs [..]--crate-type bin --emit=[..]link[..]\ + -C debuginfo=2 -C debug-assertions [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn fails_with_args_to_all_binaries() { + let p = project() + .file("src/bin/foo.rs", "fn main() {}") + .file("src/bin/bar.rs", "fn main() {}") + .file("src/bin/baz.rs", "fn main() {}") + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("rustc -v -- -C debug-assertions") + .with_status(101) + .with_stderr(CARGO_RUSTC_ERROR) + .run(); +} + +#[cargo_test] +fn fails_with_crate_type_to_multi_binaries() { + let p = project() + .file("src/bin/foo.rs", "fn main() {}") + .file("src/bin/bar.rs", "fn main() {}") + .file("src/bin/baz.rs", "fn main() {}") + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("rustc --crate-type lib") + .with_status(101) + .with_stderr( + "[ERROR] crate types to rustc can only be passed to one target, consider filtering +the package by passing, e.g., `--lib` or `--example` to specify a single target", + ) + .run(); +} + +#[cargo_test] +fn fails_with_crate_type_to_multi_examples() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex1" + crate-type = ["rlib"] + [[example]] + name = "ex2" + crate-type = ["rlib"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex1.rs", "") + .file("examples/ex2.rs", "") + .build(); + + p.cargo("rustc -v --example ex1 --example ex2 --crate-type lib,cdylib") + .with_status(101) + .with_stderr( + "[ERROR] crate types to rustc can only be passed to one target, consider filtering +the package by passing, e.g., `--lib` or `--example` to specify a single target", + ) + .run(); +} + +#[cargo_test] +fn fails_with_crate_type_to_binary() { + let p = project().file("src/bin/foo.rs", "fn main() {}").build(); + + p.cargo("rustc --crate-type lib") + .with_status(101) + .with_stderr( + "[ERROR] crate types can only be specified for libraries and example libraries. +Binaries, tests, and benchmarks are always the `bin` crate type", + ) + .run(); +} + +#[cargo_test] +fn build_with_crate_type_for_foo() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("rustc -v --crate-type cdylib") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type cdylib [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_with_crate_type_for_foo_with_deps() { + let p = project() + .file( + "src/lib.rs", + r#" + extern crate a; + pub fn foo() { a::hello(); } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/lib.rs", "pub fn hello() {}") + .build(); + + p.cargo("rustc -v --crate-type cdylib") + .with_stderr( + "\ +[COMPILING] a v0.1.0 ([CWD]/a) +[RUNNING] `rustc --crate-name a a/src/lib.rs [..]--crate-type lib [..] +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type cdylib [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_with_crate_types_for_foo() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("rustc -v --crate-type lib,cdylib") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib,cdylib [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_with_crate_type_to_example() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["rlib"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + p.cargo("rustc -v --example ex --crate-type cdylib") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib [..] +[RUNNING] `rustc --crate-name ex examples/ex.rs [..]--crate-type cdylib [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_with_crate_types_to_example() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["rlib"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + p.cargo("rustc -v --example ex --crate-type lib,cdylib") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib [..] +[RUNNING] `rustc --crate-name ex examples/ex.rs [..]--crate-type lib,cdylib [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_with_crate_types_to_one_of_multi_examples() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex1" + crate-type = ["rlib"] + [[example]] + name = "ex2" + crate-type = ["rlib"] + "#, + ) + .file("src/lib.rs", "") + .file("examples/ex1.rs", "") + .file("examples/ex2.rs", "") + .build(); + + p.cargo("rustc -v --example ex1 --crate-type lib,cdylib") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib [..] +[RUNNING] `rustc --crate-name ex1 examples/ex1.rs [..]--crate-type lib,cdylib [..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_with_args_to_one_of_multiple_tests() { + let p = project() + .file("tests/foo.rs", r#" "#) + .file("tests/bar.rs", r#" "#) + .file("tests/baz.rs", r#" "#) + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("rustc -v --test bar -- -C debug-assertions") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]\ + -C debuginfo=2 -C metadata=[..] \ + --out-dir [..]` +[RUNNING] `rustc --crate-name bar tests/bar.rs [..]--emit=[..]link[..]-C debuginfo=2 \ + -C debug-assertions [..]--test[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_foo_with_bar_dependency() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() { bar::baz() }") + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn baz() {}") + .build(); + + foo.cargo("rustc -v -- -C debug-assertions") + .with_stderr( + "\ +[COMPILING] bar v0.1.0 ([..]) +[RUNNING] `[..] -C debuginfo=2 [..]` +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `[..] -C debuginfo=2 -C debug-assertions [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn build_only_bar_dependency() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() { bar::baz() }") + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("src/lib.rs", "pub fn baz() {}") + .build(); + + foo.cargo("rustc -v -p bar -- -C debug-assertions") + .with_stderr( + "\ +[COMPILING] bar v0.1.0 ([..]) +[RUNNING] `rustc --crate-name bar [..]--crate-type lib [..] -C debug-assertions [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn targets_selected_default() { + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("rustc -v") + // bin + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin \ + --emit=[..]link[..]", + ) + // bench + .with_stderr_does_not_contain( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link \ + -C opt-level=3 --test [..]", + ) + // unit test + .with_stderr_does_not_contain( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link \ + -C debuginfo=2 --test [..]", + ) + .run(); +} + +#[cargo_test] +fn targets_selected_all() { + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("rustc -v --all-targets") + // bin + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin \ + --emit=[..]link[..]", + ) + // unit test + .with_stderr_contains( + "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]\ + -C debuginfo=2 --test [..]", + ) + .run(); +} + +#[cargo_test] +fn fail_with_multiple_packages() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + + [dependencies.baz] + path = "../baz" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file( + "src/main.rs", + r#" + fn main() { + if cfg!(flag = "1") { println!("Yeah from bar!"); } + } + "#, + ) + .build(); + + let _baz = project() + .at("baz") + .file("Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file( + "src/main.rs", + r#" + fn main() { + if cfg!(flag = "1") { println!("Yeah from baz!"); } + } + "#, + ) + .build(); + + foo.cargo("rustc -v -p bar -p baz") + .with_status(1) + .with_stderr_contains( + "\ +error: the argument '--package []' cannot be used multiple times +", + ) + .run(); +} + +#[cargo_test] +fn fail_with_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") + .build(); + + p.cargo("rustc -p '*z'") + .with_status(101) + .with_stderr("[ERROR] Glob patterns on package selection are not supported.") + .run(); +} + +#[cargo_test] +fn rustc_with_other_profile() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dev-dependencies] + a = { path = "a" } + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(test)] extern crate a; + + #[test] + fn foo() {} + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("rustc --profile test").run(); +} + +#[cargo_test] +fn rustc_fingerprint() { + // Verify that the fingerprint includes the rustc args. + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .build(); + + p.cargo("rustc -v -- -C debug-assertions") + .with_stderr( + "\ +[COMPILING] foo [..] +[RUNNING] `rustc [..]-C debug-assertions [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("rustc -v -- -C debug-assertions") + .with_stderr( + "\ +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("rustc -v") + .with_stderr_does_not_contain("-C debug-assertions") + .with_stderr( + "\ +[DIRTY] foo [..]: the profile configuration changed +[COMPILING] foo [..] +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("rustc -v") + .with_stderr( + "\ +[FRESH] foo [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustc_test_with_implicit_bin() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + #[cfg(foo)] + fn f() { compile_fail!("Foo shouldn't be set."); } + fn main() {} + "#, + ) + .file( + "tests/test1.rs", + r#" + #[cfg(not(foo))] + fn f() { compile_fail!("Foo should be set."); } + "#, + ) + .build(); + + p.cargo("rustc --test test1 -v -- --cfg foo") + .with_stderr_contains( + "\ +[RUNNING] `rustc --crate-name test1 tests/test1.rs [..] --cfg foo [..] +", + ) + .with_stderr_contains( + "\ +[RUNNING] `rustc --crate-name foo src/main.rs [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustc_with_print_cfg_single_target() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", r#"fn main() {} "#) + .build(); + + p.cargo("rustc -Z unstable-options --target x86_64-pc-windows-msvc --print cfg") + .masquerade_as_nightly_cargo(&["print"]) + .with_stdout_contains("debug_assertions") + .with_stdout_contains("target_arch=\"x86_64\"") + .with_stdout_contains("target_endian=\"little\"") + .with_stdout_contains("target_env=\"msvc\"") + .with_stdout_contains("target_family=\"windows\"") + .with_stdout_contains("target_os=\"windows\"") + .with_stdout_contains("target_pointer_width=\"64\"") + .with_stdout_contains("target_vendor=\"pc\"") + .with_stdout_contains("windows") + .run(); +} + +#[cargo_test] +fn rustc_with_print_cfg_multiple_targets() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", r#"fn main() {} "#) + .build(); + + p.cargo("rustc -Z unstable-options --target x86_64-pc-windows-msvc --target i686-unknown-linux-gnu --print cfg") + .masquerade_as_nightly_cargo(&["print"]) + .with_stdout_contains("debug_assertions") + .with_stdout_contains("target_arch=\"x86_64\"") + .with_stdout_contains("target_endian=\"little\"") + .with_stdout_contains("target_env=\"msvc\"") + .with_stdout_contains("target_family=\"windows\"") + .with_stdout_contains("target_os=\"windows\"") + .with_stdout_contains("target_pointer_width=\"64\"") + .with_stdout_contains("target_vendor=\"pc\"") + .with_stdout_contains("windows") + .with_stdout_contains("target_env=\"gnu\"") + .with_stdout_contains("target_family=\"unix\"") + .with_stdout_contains("target_pointer_width=\"32\"") + .with_stdout_contains("target_vendor=\"unknown\"") + .with_stdout_contains("target_os=\"linux\"") + .with_stdout_contains("unix") + .run(); +} + +#[cargo_test] +fn rustc_with_print_cfg_rustflags_env_var() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", r#"fn main() {} "#) + .build(); + + p.cargo("rustc -Z unstable-options --target x86_64-pc-windows-msvc --print cfg") + .masquerade_as_nightly_cargo(&["print"]) + .env("RUSTFLAGS", "-C target-feature=+crt-static") + .with_stdout_contains("debug_assertions") + .with_stdout_contains("target_arch=\"x86_64\"") + .with_stdout_contains("target_endian=\"little\"") + .with_stdout_contains("target_env=\"msvc\"") + .with_stdout_contains("target_family=\"windows\"") + .with_stdout_contains("target_feature=\"crt-static\"") + .with_stdout_contains("target_os=\"windows\"") + .with_stdout_contains("target_pointer_width=\"64\"") + .with_stdout_contains("target_vendor=\"pc\"") + .with_stdout_contains("windows") + .run(); +} + +#[cargo_test] +fn rustc_with_print_cfg_config_toml() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + ".cargo/config.toml", + r#" +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] +"#, + ) + .file("src/main.rs", r#"fn main() {} "#) + .build(); + + p.cargo("rustc -Z unstable-options --target x86_64-pc-windows-msvc --print cfg") + .masquerade_as_nightly_cargo(&["print"]) + .env("RUSTFLAGS", "-C target-feature=+crt-static") + .with_stdout_contains("debug_assertions") + .with_stdout_contains("target_arch=\"x86_64\"") + .with_stdout_contains("target_endian=\"little\"") + .with_stdout_contains("target_env=\"msvc\"") + .with_stdout_contains("target_family=\"windows\"") + .with_stdout_contains("target_feature=\"crt-static\"") + .with_stdout_contains("target_os=\"windows\"") + .with_stdout_contains("target_pointer_width=\"64\"") + .with_stdout_contains("target_vendor=\"pc\"") + .with_stdout_contains("windows") + .run(); +} diff --git a/tests/testsuite/rustc_info_cache.rs b/tests/testsuite/rustc_info_cache.rs new file mode 100644 index 0000000..9747fa3 --- /dev/null +++ b/tests/testsuite/rustc_info_cache.rs @@ -0,0 +1,186 @@ +//! Tests for the cache file for the rustc version info. + +use cargo_test_support::{basic_bin_manifest, paths::CargoPathExt}; +use cargo_test_support::{basic_manifest, project}; +use std::env; + +const MISS: &str = "[..] rustc info cache miss[..]"; +const HIT: &str = "[..]rustc info cache hit[..]"; +const UPDATE: &str = "[..]updated rustc info cache[..]"; + +#[cargo_test] +fn rustc_info_cache() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .with_stderr_contains("[..]failed to read rustc info cache[..]") + .with_stderr_contains(MISS) + .with_stderr_does_not_contain(HIT) + .with_stderr_contains(UPDATE) + .run(); + + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .with_stderr_contains("[..]reusing existing rustc info cache[..]") + .with_stderr_contains(HIT) + .with_stderr_does_not_contain(MISS) + .with_stderr_does_not_contain(UPDATE) + .run(); + + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .env("CARGO_CACHE_RUSTC_INFO", "0") + .with_stderr_contains("[..]rustc info cache disabled[..]") + .with_stderr_does_not_contain(UPDATE) + .run(); + + let other_rustc = { + let p = project() + .at("compiler") + .file("Cargo.toml", &basic_manifest("compiler", "0.1.0")) + .file( + "src/main.rs", + r#" + use std::process::Command; + use std::env; + + fn main() { + let mut cmd = Command::new("rustc"); + for arg in env::args_os().skip(1) { + cmd.arg(arg); + } + std::process::exit(cmd.status().unwrap().code().unwrap()); + } + "#, + ) + .build(); + p.cargo("build").run(); + + p.root() + .join("target/debug/compiler") + .with_extension(env::consts::EXE_EXTENSION) + }; + + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .env("RUSTC", other_rustc.display().to_string()) + .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]") + .with_stderr_contains(MISS) + .with_stderr_does_not_contain(HIT) + .with_stderr_contains(UPDATE) + .run(); + + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .env("RUSTC", other_rustc.display().to_string()) + .with_stderr_contains("[..]reusing existing rustc info cache[..]") + .with_stderr_contains(HIT) + .with_stderr_does_not_contain(MISS) + .with_stderr_does_not_contain(UPDATE) + .run(); + + other_rustc.move_into_the_future(); + + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .env("RUSTC", other_rustc.display().to_string()) + .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]") + .with_stderr_contains(MISS) + .with_stderr_does_not_contain(HIT) + .with_stderr_contains(UPDATE) + .run(); + + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .env("RUSTC", other_rustc.display().to_string()) + .with_stderr_contains("[..]reusing existing rustc info cache[..]") + .with_stderr_contains(HIT) + .with_stderr_does_not_contain(MISS) + .with_stderr_does_not_contain(UPDATE) + .run(); +} + +#[cargo_test] +fn rustc_info_cache_with_wrappers() { + let wrapper_project = project() + .at("wrapper") + .file("Cargo.toml", &basic_bin_manifest("wrapper")) + .file("src/main.rs", r#"fn main() { }"#) + .build(); + let wrapper = wrapper_project.bin("wrapper"); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "test" + version = "0.0.0" + authors = [] + [workspace] + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .build(); + + for &wrapper_env in ["RUSTC_WRAPPER", "RUSTC_WORKSPACE_WRAPPER"].iter() { + p.cargo("clean").with_status(0).run(); + wrapper_project.change_file( + "src/main.rs", + r#" + fn main() { + let mut args = std::env::args_os(); + let _me = args.next().unwrap(); + let rustc = args.next().unwrap(); + let status = std::process::Command::new(rustc).args(args).status().unwrap(); + std::process::exit(if status.success() { 0 } else { 1 }) + } + "#, + ); + wrapper_project.cargo("build").with_status(0).run(); + + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .env(wrapper_env, &wrapper) + .with_stderr_contains("[..]failed to read rustc info cache[..]") + .with_stderr_contains(MISS) + .with_stderr_contains(UPDATE) + .with_stderr_does_not_contain(HIT) + .with_status(0) + .run(); + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .env(wrapper_env, &wrapper) + .with_stderr_contains("[..]reusing existing rustc info cache[..]") + .with_stderr_contains(HIT) + .with_stderr_does_not_contain(UPDATE) + .with_stderr_does_not_contain(MISS) + .with_status(0) + .run(); + + wrapper_project.change_file("src/main.rs", r#"fn main() { panic!() }"#); + wrapper_project.cargo("build").with_status(0).run(); + + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .env(wrapper_env, &wrapper) + .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]") + .with_stderr_contains(MISS) + .with_stderr_contains(UPDATE) + .with_stderr_does_not_contain(HIT) + .with_status(101) + .run(); + p.cargo("build") + .env("CARGO_LOG", "cargo::util::rustc=debug") + .env(wrapper_env, &wrapper) + .with_stderr_contains("[..]reusing existing rustc info cache[..]") + .with_stderr_contains(HIT) + .with_stderr_does_not_contain(UPDATE) + .with_stderr_does_not_contain(MISS) + .with_status(101) + .run(); + } +} diff --git a/tests/testsuite/rustdoc.rs b/tests/testsuite/rustdoc.rs new file mode 100644 index 0000000..5650f3e --- /dev/null +++ b/tests/testsuite/rustdoc.rs @@ -0,0 +1,252 @@ +//! Tests for the `cargo rustdoc` command. + +use cargo_test_support::{basic_manifest, cross_compile, project}; + +#[cargo_test] +fn rustdoc_simple() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("rustdoc -v") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[RUNNING] `rustdoc [..]--crate-name foo src/lib.rs [..]\ + -o [CWD]/target/doc \ + [..] \ + -L dependency=[CWD]/target/debug/deps [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustdoc_args() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("rustdoc -v -- --cfg=foo") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([CWD]) +[RUNNING] `rustdoc [..]--crate-name foo src/lib.rs [..]\ + -o [CWD]/target/doc \ + [..] \ + --cfg=foo \ + -C metadata=[..] \ + -L dependency=[CWD]/target/debug/deps [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustdoc_binary_args_passed() { + let p = project().file("src/main.rs", "").build(); + + p.cargo("rustdoc -v") + .arg("--") + .arg("--markdown-no-toc") + .with_stderr_contains("[RUNNING] `rustdoc [..] --markdown-no-toc[..]`") + .run(); +} + +#[cargo_test] +fn rustdoc_foo_with_bar_dependency() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file("src/lib.rs", "extern crate bar; pub fn foo() {}") + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("src/lib.rs", "pub fn baz() {}") + .build(); + + foo.cargo("rustdoc -v -- --cfg=foo") + .with_stderr( + "\ +[CHECKING] bar v0.0.1 ([..]) +[RUNNING] `rustc [..]bar/src/lib.rs [..]` +[DOCUMENTING] foo v0.0.1 ([CWD]) +[RUNNING] `rustdoc [..]--crate-name foo src/lib.rs [..]\ + -o [CWD]/target/doc \ + [..] \ + --cfg=foo \ + -C metadata=[..] \ + -L dependency=[CWD]/target/debug/deps \ + --extern [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustdoc_only_bar_dependency() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file("src/main.rs", "extern crate bar; fn main() { bar::baz() }") + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("src/lib.rs", "pub fn baz() {}") + .build(); + + foo.cargo("rustdoc -v -p bar -- --cfg=foo") + .with_stderr( + "\ +[DOCUMENTING] bar v0.0.1 ([..]) +[RUNNING] `rustdoc [..]--crate-name bar [..]bar/src/lib.rs [..]\ + -o [CWD]/target/doc \ + [..] \ + --cfg=foo \ + -C metadata=[..] \ + -L dependency=[CWD]/target/debug/deps [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustdoc_same_name_documents_lib() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", r#" "#) + .build(); + + p.cargo("rustdoc -v -- --cfg=foo") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([..]) +[RUNNING] `rustdoc [..]--crate-name foo src/lib.rs [..]\ + -o [CWD]/target/doc \ + [..] \ + --cfg=foo \ + -C metadata=[..] \ + -L dependency=[CWD]/target/debug/deps [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [features] + quux = [] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("rustdoc --verbose --features quux") + .with_stderr_contains("[..]feature=[..]quux[..]") + .run(); +} + +#[cargo_test] +fn proc_macro_crate_type() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + proc-macro = true + + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("rustdoc --verbose") + .with_stderr_contains( + "\ +[RUNNING] `rustdoc --crate-type proc-macro [..]` +", + ) + .run(); +} + +#[cargo_test] +fn rustdoc_target() { + if cross_compile::disabled() { + return; + } + + let p = project().file("src/lib.rs", "").build(); + + p.cargo("rustdoc --verbose --target") + .arg(cross_compile::alternate()) + .with_stderr(format!( + "\ +[DOCUMENTING] foo v0.0.1 ([..]) +[RUNNING] `rustdoc [..]--crate-name foo src/lib.rs [..]\ + --target {target} \ + -o [CWD]/target/{target}/doc \ + [..] \ + -L dependency=[CWD]/target/{target}/debug/deps \ + -L dependency=[CWD]/target/debug/deps[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + target = cross_compile::alternate() + )) + .run(); +} + +#[cargo_test] +fn fail_with_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() { break_the_build(); }") + .build(); + + p.cargo("rustdoc -p '*z'") + .with_status(101) + .with_stderr("[ERROR] Glob patterns on package selection are not supported.") + .run(); +} diff --git a/tests/testsuite/rustdoc_extern_html.rs b/tests/testsuite/rustdoc_extern_html.rs new file mode 100644 index 0000000..440da56 --- /dev/null +++ b/tests/testsuite/rustdoc_extern_html.rs @@ -0,0 +1,426 @@ +//! Tests for the -Zrustdoc-map feature. + +use cargo_test_support::registry::{self, Package}; +use cargo_test_support::{paths, project, Project}; + +fn basic_project() -> Project { + Package::new("bar", "1.0.0") + .file("src/lib.rs", "pub struct Straw;") + .publish(); + + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + bar = "1.0" + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn myfun() -> Option { + None + } + "#, + ) + .build() +} + +#[cargo_test] +fn ignores_on_stable() { + // Requires -Zrustdoc-map to use. + let p = basic_project(); + p.cargo("doc -v --no-deps") + .with_stderr_does_not_contain("[..]--extern-html-root-url[..]") + .run(); +} + +#[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] +fn simple() { + // Basic test that it works with crates.io. + let p = basic_project(); + p.cargo("doc -v --no-deps -Zrustdoc-map") + .masquerade_as_nightly_cargo(&["rustdoc-map"]) + .with_stderr_contains( + "[RUNNING] `rustdoc [..]--crate-name foo [..]bar=https://docs.rs/bar/1.0.0/[..]", + ) + .run(); + let myfun = p.read_file("target/doc/foo/fn.myfun.html"); + assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#)); +} + +#[ignore = "Broken, temporarily disabled until https://github.com/rust-lang/rust/pull/82776 is resolved."] +#[cargo_test] +// #[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] +fn std_docs() { + // Mapping std docs somewhere else. + // For local developers, skip this test if docs aren't installed. + let docs = std::path::Path::new(&paths::sysroot()).join("share/doc/rust/html"); + if !docs.exists() { + if cargo_util::is_ci() { + panic!("std docs are not installed, check that the rust-docs component is installed"); + } else { + eprintln!( + "documentation not found at {}, \ + skipping test (run `rustdoc component add rust-docs` to install", + docs.display() + ); + return; + } + } + let p = basic_project(); + p.change_file( + ".cargo/config", + r#" + [doc.extern-map] + std = "local" + "#, + ); + p.cargo("doc -v --no-deps -Zrustdoc-map") + .masquerade_as_nightly_cargo(&["rustdoc-map"]) + .with_stderr_contains("[RUNNING] `rustdoc [..]--crate-name foo [..]std=file://[..]") + .run(); + let myfun = p.read_file("target/doc/foo/fn.myfun.html"); + assert!(myfun.contains(r#"share/doc/rust/html/core/option/enum.Option.html""#)); + + p.change_file( + ".cargo/config", + r#" + [doc.extern-map] + std = "https://example.com/rust/" + "#, + ); + p.cargo("doc -v --no-deps -Zrustdoc-map") + .masquerade_as_nightly_cargo(&["rustdoc-map"]) + .with_stderr_contains( + "[RUNNING] `rustdoc [..]--crate-name foo [..]std=https://example.com/rust/[..]", + ) + .run(); + let myfun = p.read_file("target/doc/foo/fn.myfun.html"); + assert!(myfun.contains(r#"href="https://example.com/rust/core/option/enum.Option.html""#)); +} + +#[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] +fn renamed_dep() { + // Handles renamed dependencies. + Package::new("bar", "1.0.0") + .file("src/lib.rs", "pub struct Straw;") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + groovy = { version = "1.0", package = "bar" } + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn myfun() -> Option { + None + } + "#, + ) + .build(); + p.cargo("doc -v --no-deps -Zrustdoc-map") + .masquerade_as_nightly_cargo(&["rustdoc-map"]) + .with_stderr_contains( + "[RUNNING] `rustdoc [..]--crate-name foo [..]bar=https://docs.rs/bar/1.0.0/[..]", + ) + .run(); + let myfun = p.read_file("target/doc/foo/fn.myfun.html"); + assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#)); +} + +#[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] +fn lib_name() { + // Handles lib name != package name. + Package::new("bar", "1.0.0") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "1.0.0" + + [lib] + name = "rumpelstiltskin" + "#, + ) + .file("src/lib.rs", "pub struct Straw;") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn myfun() -> Option { + None + } + "#, + ) + .build(); + p.cargo("doc -v --no-deps -Zrustdoc-map") + .masquerade_as_nightly_cargo(&["rustdoc-map"]) + .with_stderr_contains( + "[RUNNING] `rustdoc [..]--crate-name foo [..]rumpelstiltskin=https://docs.rs/bar/1.0.0/[..]", + ) + .run(); + let myfun = p.read_file("target/doc/foo/fn.myfun.html"); + assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/rumpelstiltskin/struct.Straw.html""#)); +} + +#[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] +fn alt_registry() { + // Supports other registry names. + registry::alt_init(); + Package::new("bar", "1.0.0") + .alternative(true) + .file( + "src/lib.rs", + r#" + extern crate baz; + pub struct Queen; + pub use baz::King; + "#, + ) + .registry_dep("baz", "1.0") + .publish(); + Package::new("baz", "1.0.0") + .alternative(true) + .file("src/lib.rs", "pub struct King;") + .publish(); + Package::new("grimm", "1.0.0") + .file("src/lib.rs", "pub struct Gold;") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + bar = { version = "1.0", registry="alternative" } + grimm = "1.0" + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn queen() -> bar::Queen { bar::Queen } + pub fn king() -> bar::King { bar::King } + pub fn gold() -> grimm::Gold { grimm::Gold } + "#, + ) + .file( + ".cargo/config", + r#" + [doc.extern-map.registries] + alternative = "https://example.com/{pkg_name}/{version}/" + crates-io = "https://docs.rs/" + "#, + ) + .build(); + p.cargo("doc -v --no-deps -Zrustdoc-map") + .masquerade_as_nightly_cargo(&["rustdoc-map"]) + .with_stderr_contains( + "[RUNNING] `rustdoc [..]--crate-name foo \ + [..]bar=https://example.com/bar/1.0.0/[..]grimm=https://docs.rs/grimm/1.0.0/[..]", + ) + .run(); + let queen = p.read_file("target/doc/foo/fn.queen.html"); + assert!(queen.contains(r#"href="https://example.com/bar/1.0.0/bar/struct.Queen.html""#)); + // The king example fails to link. Rustdoc seems to want the origin crate + // name (baz) for re-exports. There are many issues in the issue tracker + // for rustdoc re-exports, so I'm not sure, but I think this is maybe a + // rustdoc issue. Alternatively, Cargo could provide mappings for all + // transitive dependencies to fix this. + let king = p.read_file("target/doc/foo/fn.king.html"); + assert!(king.contains(r#"-> King"#)); + + let gold = p.read_file("target/doc/foo/fn.gold.html"); + assert!(gold.contains(r#"href="https://docs.rs/grimm/1.0.0/grimm/struct.Gold.html""#)); +} + +#[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] +fn multiple_versions() { + // What happens when there are multiple versions. + // NOTE: This is currently broken behavior. Rustdoc does not provide a way + // to match renamed dependencies. + Package::new("bar", "1.0.0") + .file("src/lib.rs", "pub struct Spin;") + .publish(); + Package::new("bar", "2.0.0") + .file("src/lib.rs", "pub struct Straw;") + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + bar = "1.0" + bar2 = {version="2.0", package="bar"} + "#, + ) + .file( + "src/lib.rs", + " + pub fn fn1() -> bar::Spin {bar::Spin} + pub fn fn2() -> bar2::Straw {bar2::Straw} + ", + ) + .build(); + p.cargo("doc -v --no-deps -Zrustdoc-map") + .masquerade_as_nightly_cargo(&["rustdoc-map"]) + .with_stderr_contains( + "[RUNNING] `rustdoc [..]--crate-name foo \ + [..]bar=https://docs.rs/bar/1.0.0/[..]bar=https://docs.rs/bar/2.0.0/[..]", + ) + .run(); + let fn1 = p.read_file("target/doc/foo/fn.fn1.html"); + // This should be 1.0.0, rustdoc seems to use the last entry when there + // are duplicates. + assert!(fn1.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Spin.html""#)); + let fn2 = p.read_file("target/doc/foo/fn.fn2.html"); + assert!(fn2.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Straw.html""#)); +} + +#[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] +fn rebuilds_when_changing() { + // Make sure it rebuilds if the map changes. + let p = basic_project(); + p.cargo("doc -v --no-deps -Zrustdoc-map") + .masquerade_as_nightly_cargo(&["rustdoc-map"]) + .with_stderr_contains("[..]--extern-html-root-url[..]") + .run(); + + // This also tests that the map for docs.rs can be overridden. + p.change_file( + ".cargo/config", + r#" + [doc.extern-map.registries] + crates-io = "https://example.com/" + "#, + ); + p.cargo("doc -v --no-deps -Zrustdoc-map") + .masquerade_as_nightly_cargo(&["rustdoc-map"]) + .with_stderr_contains( + "[RUNNING] `rustdoc [..]--extern-html-root-url [..]bar=https://example.com/bar/1.0.0/[..]", + ) + .run(); +} + +#[cargo_test(nightly, reason = "--extern-html-root-url is unstable")] +fn alt_sparse_registry() { + // Supports other registry names. + + registry::init(); + let _registry = registry::RegistryBuilder::new() + .http_index() + .alternative() + .build(); + + Package::new("bar", "1.0.0") + .alternative(true) + .file( + "src/lib.rs", + r#" + extern crate baz; + pub struct Queen; + pub use baz::King; + "#, + ) + .registry_dep("baz", "1.0") + .publish(); + Package::new("baz", "1.0.0") + .alternative(true) + .file("src/lib.rs", "pub struct King;") + .publish(); + Package::new("grimm", "1.0.0") + .file("src/lib.rs", "pub struct Gold;") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + bar = { version = "1.0", registry="alternative" } + grimm = "1.0" + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn queen() -> bar::Queen { bar::Queen } + pub fn king() -> bar::King { bar::King } + pub fn gold() -> grimm::Gold { grimm::Gold } + "#, + ) + .file( + ".cargo/config", + r#" + [doc.extern-map.registries] + alternative = "https://example.com/{pkg_name}/{version}/" + crates-io = "https://docs.rs/" + "#, + ) + .build(); + p.cargo("doc -v --no-deps -Zrustdoc-map -Zsparse-registry") + .masquerade_as_nightly_cargo(&["rustdoc-map", "sparse-registry"]) + .with_stderr_contains( + "[RUNNING] `rustdoc [..]--crate-name foo \ + [..]bar=https://example.com/bar/1.0.0/[..]grimm=https://docs.rs/grimm/1.0.0/[..]", + ) + .run(); + let queen = p.read_file("target/doc/foo/fn.queen.html"); + assert!(queen.contains(r#"href="https://example.com/bar/1.0.0/bar/struct.Queen.html""#)); + // The king example fails to link. Rustdoc seems to want the origin crate + // name (baz) for re-exports. There are many issues in the issue tracker + // for rustdoc re-exports, so I'm not sure, but I think this is maybe a + // rustdoc issue. Alternatively, Cargo could provide mappings for all + // transitive dependencies to fix this. + let king = p.read_file("target/doc/foo/fn.king.html"); + assert!(king.contains(r#"-> King"#)); + + let gold = p.read_file("target/doc/foo/fn.gold.html"); + assert!(gold.contains(r#"href="https://docs.rs/grimm/1.0.0/grimm/struct.Gold.html""#)); +} diff --git a/tests/testsuite/rustdocflags.rs b/tests/testsuite/rustdocflags.rs new file mode 100644 index 0000000..6992961 --- /dev/null +++ b/tests/testsuite/rustdocflags.rs @@ -0,0 +1,155 @@ +//! Tests for setting custom rustdoc flags. + +use cargo_test_support::project; + +#[cargo_test] +fn parses_env() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("doc -v") + .env("RUSTDOCFLAGS", "--cfg=foo") + .with_stderr_contains("[RUNNING] `rustdoc [..] --cfg=foo[..]`") + .run(); +} + +#[cargo_test] +fn parses_config() { + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [build] + rustdocflags = ["--cfg", "foo"] + "#, + ) + .build(); + + p.cargo("doc -v") + .with_stderr_contains("[RUNNING] `rustdoc [..] --cfg foo[..]`") + .run(); +} + +#[cargo_test] +fn bad_flags() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("doc") + .env("RUSTDOCFLAGS", "--bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn rerun() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("doc").env("RUSTDOCFLAGS", "--cfg=foo").run(); + p.cargo("doc") + .env("RUSTDOCFLAGS", "--cfg=foo") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); + p.cargo("doc") + .env("RUSTDOCFLAGS", "--cfg=bar") + .with_stderr( + "\ +[DOCUMENTING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn rustdocflags_passed_to_rustdoc_through_cargo_test() { + let p = project() + .file( + "src/lib.rs", + r#" + //! ``` + //! assert!(cfg!(do_not_choke)); + //! ``` + "#, + ) + .build(); + + p.cargo("test --doc") + .env("RUSTDOCFLAGS", "--cfg do_not_choke") + .run(); +} + +#[cargo_test] +fn rustdocflags_passed_to_rustdoc_through_cargo_test_only_once() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("test --doc") + .env("RUSTDOCFLAGS", "--markdown-no-toc") + .run(); +} + +#[cargo_test] +fn rustdocflags_misspelled() { + let p = project().file("src/main.rs", "fn main() { }").build(); + + p.cargo("doc") + .env("RUSTDOC_FLAGS", "foo") + .with_stderr_contains("[WARNING] Cargo does not read `RUSTDOC_FLAGS` environment variable. Did you mean `RUSTDOCFLAGS`?") + .run(); +} + +#[cargo_test] +fn whitespace() { + // Checks behavior of different whitespace characters. + let p = project().file("src/lib.rs", "").build(); + + // "too many operands" + p.cargo("doc") + .env("RUSTDOCFLAGS", "--crate-version this has spaces") + .with_stderr_contains("[ERROR] could not document `foo`") + .with_status(101) + .run(); + + const SPACED_VERSION: &str = "a\nb\tc\u{00a0}d"; + p.cargo("doc") + .env_remove("__CARGO_TEST_FORCE_ARGFILE") // Not applicable for argfile. + .env( + "RUSTDOCFLAGS", + format!("--crate-version {}", SPACED_VERSION), + ) + .run(); + + let contents = p.read_file("target/doc/foo/index.html"); + assert!(contents.contains(SPACED_VERSION)); +} + +#[cargo_test] +fn not_affected_by_target_rustflags() { + let cfg = if cfg!(windows) { "windows" } else { "unix" }; + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + &format!( + r#" + [target.'cfg({cfg})'] + rustflags = ["-D", "missing-docs"] + + [build] + rustdocflags = ["--cfg", "foo"] + "#, + ), + ) + .build(); + + // `cargo build` should fail due to missing docs. + p.cargo("build -v") + .with_status(101) + .with_stderr_contains("[RUNNING] `rustc [..] -D missing-docs[..]`") + .run(); + + // `cargo doc` shouldn't fail. + p.cargo("doc -v") + .with_stderr_contains("[RUNNING] `rustdoc [..] --cfg foo[..]`") + .run(); +} diff --git a/tests/testsuite/rustflags.rs b/tests/testsuite/rustflags.rs new file mode 100644 index 0000000..6677beb --- /dev/null +++ b/tests/testsuite/rustflags.rs @@ -0,0 +1,1673 @@ +//! Tests for setting custom rustc flags. + +use cargo_test_support::registry::Package; +use cargo_test_support::{ + basic_lib_manifest, basic_manifest, paths, project, project_in_home, rustc_host, +}; +use std::fs; + +#[cargo_test] +fn env_rustflags_normal_source() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .file("tests/c.rs", "#[test] fn f() { }") + .file( + "benches/d.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run1(_ben: &mut test::Bencher) { } + "#, + ) + .build(); + + // Use RUSTFLAGS to pass an argument that will generate an error + p.cargo("check --lib") + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --bin=a") + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --example=b") + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("test") + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("bench") + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn env_rustflags_build_script() { + // RUSTFLAGS should be passed to rustc for build scripts + // when --target is not specified. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(cfg!(foo)); } + "#, + ) + .build(); + + p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); +} + +#[cargo_test] +fn env_rustflags_build_script_dep() { + // RUSTFLAGS should be passed to rustc for build scripts + // when --target is not specified. + // In this test if --cfg foo is not passed the build will fail. + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + + [build-dependencies.bar] + path = "../bar" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file( + "src/lib.rs", + r#" + fn bar() { } + #[cfg(not(foo))] + fn bar() { } + "#, + ) + .build(); + + foo.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); +} + +#[cargo_test] +fn env_rustflags_plugin() { + // RUSTFLAGS should be passed to rustc for plugins + // when --target is not specified. + // In this test if --cfg foo is not passed the build will fail. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [lib] + name = "foo" + plugin = true + "#, + ) + .file( + "src/lib.rs", + r#" + fn main() { } + #[cfg(not(foo))] + fn main() { } + "#, + ) + .build(); + + p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); +} + +#[cargo_test] +fn env_rustflags_plugin_dep() { + // RUSTFLAGS should be passed to rustc for plugins + // when --target is not specified. + // In this test if --cfg foo is not passed the build will fail. + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [lib] + name = "foo" + plugin = true + + [dependencies.bar] + path = "../bar" + "#, + ) + .file("src/lib.rs", "fn foo() {}") + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_lib_manifest("bar")) + .file( + "src/lib.rs", + r#" + fn bar() { } + #[cfg(not(foo))] + fn bar() { } + "#, + ) + .build(); + + foo.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); +} + +#[cargo_test] +fn env_rustflags_normal_source_with_target() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .file("tests/c.rs", "#[test] fn f() { }") + .file( + "benches/d.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run1(_ben: &mut test::Bencher) { } + "#, + ) + .build(); + + let host = &rustc_host(); + + // Use RUSTFLAGS to pass an argument that will generate an error + p.cargo("check --lib --target") + .arg(host) + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --bin=a --target") + .arg(host) + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --example=b --target") + .arg(host) + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("test --target") + .arg(host) + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("bench --target") + .arg(host) + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn env_rustflags_build_script_with_target() { + // RUSTFLAGS should not be passed to rustc for build scripts + // when --target is specified. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(!cfg!(foo)); } + "#, + ) + .build(); + + let host = rustc_host(); + p.cargo("check --target") + .arg(host) + .env("RUSTFLAGS", "--cfg foo") + .run(); +} + +#[cargo_test] +fn env_rustflags_build_script_with_target_doesnt_apply_to_host_kind() { + // RUSTFLAGS should *not* be passed to rustc for build scripts when --target is specified as the + // host triple even if target-applies-to-host-kind is enabled, to match legacy Cargo behavior. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(!cfg!(foo)); } + "#, + ) + .file( + ".cargo/config.toml", + r#" + target-applies-to-host = true + "#, + ) + .build(); + + let host = rustc_host(); + p.cargo("check --target") + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .arg(host) + .arg("-Ztarget-applies-to-host") + .env("RUSTFLAGS", "--cfg foo") + .run(); +} + +#[cargo_test] +fn env_rustflags_build_script_dep_with_target() { + // RUSTFLAGS should not be passed to rustc for build scripts + // when --target is specified. + // In this test if --cfg foo is passed the build will fail. + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + + [build-dependencies.bar] + path = "../bar" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file( + "src/lib.rs", + r#" + fn bar() { } + #[cfg(foo)] + fn bar() { } + "#, + ) + .build(); + + let host = rustc_host(); + foo.cargo("check --target") + .arg(host) + .env("RUSTFLAGS", "--cfg foo") + .run(); +} + +#[cargo_test] +fn env_rustflags_plugin_with_target() { + // RUSTFLAGS should not be passed to rustc for plugins + // when --target is specified. + // In this test if --cfg foo is passed the build will fail. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [lib] + name = "foo" + plugin = true + "#, + ) + .file( + "src/lib.rs", + r#" + fn main() { } + #[cfg(foo)] + fn main() { } + "#, + ) + .build(); + + let host = rustc_host(); + p.cargo("check --target") + .arg(host) + .env("RUSTFLAGS", "--cfg foo") + .run(); +} + +#[cargo_test] +fn env_rustflags_plugin_dep_with_target() { + // RUSTFLAGS should not be passed to rustc for plugins + // when --target is specified. + // In this test if --cfg foo is passed the build will fail. + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [lib] + name = "foo" + plugin = true + + [dependencies.bar] + path = "../bar" + "#, + ) + .file("src/lib.rs", "fn foo() {}") + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_lib_manifest("bar")) + .file( + "src/lib.rs", + r#" + fn bar() { } + #[cfg(foo)] + fn bar() { } + "#, + ) + .build(); + + let host = rustc_host(); + foo.cargo("check --target") + .arg(host) + .env("RUSTFLAGS", "--cfg foo") + .run(); +} + +#[cargo_test] +fn env_rustflags_recompile() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("check").run(); + // Setting RUSTFLAGS forces a recompile + p.cargo("check") + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn env_rustflags_recompile2() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); + // Setting RUSTFLAGS forces a recompile + p.cargo("check") + .env("RUSTFLAGS", "-Z bogus") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn env_rustflags_no_recompile() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); + p.cargo("check") + .env("RUSTFLAGS", "--cfg foo") + .with_stdout("") + .run(); +} + +#[cargo_test] +fn build_rustflags_normal_source() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .file("tests/c.rs", "#[test] fn f() { }") + .file( + "benches/d.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run1(_ben: &mut test::Bencher) { } + "#, + ) + .file( + ".cargo/config", + r#" + [build] + rustflags = ["-Z", "bogus"] + "#, + ) + .build(); + + p.cargo("check --lib") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --bin=a") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --example=b") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("test") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("bench") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn build_rustflags_build_script() { + // RUSTFLAGS should be passed to rustc for build scripts + // when --target is not specified. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(cfg!(foo)); } + "#, + ) + .file( + ".cargo/config", + r#" + [build] + rustflags = ["--cfg", "foo"] + "#, + ) + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn build_rustflags_build_script_dep() { + // RUSTFLAGS should be passed to rustc for build scripts + // when --target is not specified. + // In this test if --cfg foo is not passed the build will fail. + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + + [build-dependencies.bar] + path = "../bar" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [build] + rustflags = ["--cfg", "foo"] + "#, + ) + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file( + "src/lib.rs", + r#" + fn bar() { } + #[cfg(not(foo))] + fn bar() { } + "#, + ) + .build(); + + foo.cargo("check").run(); +} + +#[cargo_test] +fn build_rustflags_plugin() { + // RUSTFLAGS should be passed to rustc for plugins + // when --target is not specified. + // In this test if --cfg foo is not passed the build will fail. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [lib] + name = "foo" + plugin = true + "#, + ) + .file( + "src/lib.rs", + r#" + fn main() { } + #[cfg(not(foo))] + fn main() { } + "#, + ) + .file( + ".cargo/config", + r#" + [build] + rustflags = ["--cfg", "foo"] + "#, + ) + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn build_rustflags_plugin_dep() { + // RUSTFLAGS should be passed to rustc for plugins + // when --target is not specified. + // In this test if --cfg foo is not passed the build will fail. + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [lib] + name = "foo" + plugin = true + + [dependencies.bar] + path = "../bar" + "#, + ) + .file("src/lib.rs", "fn foo() {}") + .file( + ".cargo/config", + r#" + [build] + rustflags = ["--cfg", "foo"] + "#, + ) + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_lib_manifest("bar")) + .file( + "src/lib.rs", + r#" + fn bar() { } + #[cfg(not(foo))] + fn bar() { } + "#, + ) + .build(); + + foo.cargo("check").run(); +} + +#[cargo_test] +fn build_rustflags_normal_source_with_target() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .file("tests/c.rs", "#[test] fn f() { }") + .file( + "benches/d.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run1(_ben: &mut test::Bencher) { } + "#, + ) + .file( + ".cargo/config", + r#" + [build] + rustflags = ["-Z", "bogus"] + "#, + ) + .build(); + + let host = &rustc_host(); + + // Use build.rustflags to pass an argument that will generate an error + p.cargo("check --lib --target") + .arg(host) + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --bin=a --target") + .arg(host) + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --example=b --target") + .arg(host) + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("test --target") + .arg(host) + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("bench --target") + .arg(host) + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn build_rustflags_build_script_with_target() { + // RUSTFLAGS should not be passed to rustc for build scripts + // when --target is specified. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(!cfg!(foo)); } + "#, + ) + .file( + ".cargo/config", + r#" + [build] + rustflags = ["--cfg", "foo"] + "#, + ) + .build(); + + let host = rustc_host(); + p.cargo("check --target").arg(host).run(); +} + +#[cargo_test] +fn build_rustflags_build_script_dep_with_target() { + // RUSTFLAGS should not be passed to rustc for build scripts + // when --target is specified. + // In this test if --cfg foo is passed the build will fail. + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + + [build-dependencies.bar] + path = "../bar" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [build] + rustflags = ["--cfg", "foo"] + "#, + ) + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file( + "src/lib.rs", + r#" + fn bar() { } + #[cfg(foo)] + fn bar() { } + "#, + ) + .build(); + + let host = rustc_host(); + foo.cargo("check --target").arg(host).run(); +} + +#[cargo_test] +fn build_rustflags_plugin_with_target() { + // RUSTFLAGS should not be passed to rustc for plugins + // when --target is specified. + // In this test if --cfg foo is passed the build will fail. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [lib] + name = "foo" + plugin = true + "#, + ) + .file( + "src/lib.rs", + r#" + fn main() { } + #[cfg(foo)] + fn main() { } + "#, + ) + .file( + ".cargo/config", + r#" + [build] + rustflags = ["--cfg", "foo"] + "#, + ) + .build(); + + let host = rustc_host(); + p.cargo("check --target").arg(host).run(); +} + +#[cargo_test] +fn build_rustflags_plugin_dep_with_target() { + // RUSTFLAGS should not be passed to rustc for plugins + // when --target is specified. + // In this test if --cfg foo is passed the build will fail. + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [lib] + name = "foo" + plugin = true + + [dependencies.bar] + path = "../bar" + "#, + ) + .file("src/lib.rs", "fn foo() {}") + .file( + ".cargo/config", + r#" + [build] + rustflags = ["--cfg", "foo"] + "#, + ) + .build(); + let _bar = project() + .at("bar") + .file("Cargo.toml", &basic_lib_manifest("bar")) + .file( + "src/lib.rs", + r#" + fn bar() { } + #[cfg(foo)] + fn bar() { } + "#, + ) + .build(); + + let host = rustc_host(); + foo.cargo("check --target").arg(host).run(); +} + +#[cargo_test] +fn build_rustflags_recompile() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("check").run(); + + // Setting RUSTFLAGS forces a recompile + let config = r#" + [build] + rustflags = ["-Z", "bogus"] + "#; + let config_file = paths::root().join("foo/.cargo/config"); + fs::create_dir_all(config_file.parent().unwrap()).unwrap(); + fs::write(config_file, config).unwrap(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn build_rustflags_recompile2() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); + + // Setting RUSTFLAGS forces a recompile + let config = r#" + [build] + rustflags = ["-Z", "bogus"] + "#; + let config_file = paths::root().join("foo/.cargo/config"); + fs::create_dir_all(config_file.parent().unwrap()).unwrap(); + fs::write(config_file, config).unwrap(); + + p.cargo("check") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn build_rustflags_no_recompile() { + let p = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [build] + rustflags = ["--cfg", "foo"] + "#, + ) + .build(); + + p.cargo("check").env("RUSTFLAGS", "--cfg foo").run(); + p.cargo("check") + .env("RUSTFLAGS", "--cfg foo") + .with_stdout("") + .run(); +} + +#[cargo_test] +fn build_rustflags_with_home_config() { + // We need a config file inside the home directory + let home = paths::home(); + let home_config = home.join(".cargo"); + fs::create_dir(&home_config).unwrap(); + fs::write( + &home_config.join("config"), + r#" + [build] + rustflags = ["-Cllvm-args=-x86-asm-syntax=intel"] + "#, + ) + .unwrap(); + + // And we need the project to be inside the home directory + // so the walking process finds the home project twice. + let p = project_in_home("foo").file("src/lib.rs", "").build(); + + p.cargo("check -v").run(); +} + +#[cargo_test] +fn target_rustflags_normal_source() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .file("tests/c.rs", "#[test] fn f() { }") + .file( + "benches/d.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run1(_ben: &mut test::Bencher) { } + "#, + ) + .file( + ".cargo/config", + &format!( + " + [target.{}] + rustflags = [\"-Z\", \"bogus\"] + ", + rustc_host() + ), + ) + .build(); + + p.cargo("check --lib") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --bin=a") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --example=b") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("test") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("bench") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn target_rustflags_also_for_build_scripts() { + let p = project() + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(cfg!(foo)); } + "#, + ) + .file( + ".cargo/config", + &format!( + " + [target.{}] + rustflags = [\"--cfg=foo\"] + ", + rustc_host() + ), + ) + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn target_rustflags_not_for_build_scripts_with_target() { + let host = rustc_host(); + let p = project() + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(!cfg!(foo)); } + "#, + ) + .file( + ".cargo/config", + &format!( + " + [target.{}] + rustflags = [\"--cfg=foo\"] + ", + host + ), + ) + .build(); + + p.cargo("check --target").arg(host).run(); + + // Enabling -Ztarget-applies-to-host should not make a difference without the config setting + p.cargo("check --target") + .arg(host) + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .arg("-Ztarget-applies-to-host") + .run(); + + // Even with the setting, the rustflags from `target.` should not apply, to match the legacy + // Cargo behavior. + p.change_file( + ".cargo/config", + &format!( + " + target-applies-to-host = true + + [target.{}] + rustflags = [\"--cfg=foo\"] + ", + host + ), + ); + p.cargo("check --target") + .arg(host) + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .arg("-Ztarget-applies-to-host") + .run(); +} + +#[cargo_test] +fn build_rustflags_for_build_scripts() { + let host = rustc_host(); + let p = project() + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { assert!(cfg!(foo)); } + "#, + ) + .file( + ".cargo/config", + " + [build] + rustflags = [\"--cfg=foo\"] + ", + ) + .build(); + + // With "legacy" behavior, build.rustflags should apply to build scripts without --target + p.cargo("check").run(); + + // But should _not_ apply _with_ --target + p.cargo("check --target") + .arg(host) + .with_status(101) + .with_stderr_contains("[..]assertion failed[..]") + .run(); + + // Enabling -Ztarget-applies-to-host should not make a difference without the config setting + p.cargo("check") + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .arg("-Ztarget-applies-to-host") + .run(); + p.cargo("check --target") + .arg(host) + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .arg("-Ztarget-applies-to-host") + .with_status(101) + .with_stderr_contains("[..]assertion failed[..]") + .run(); + + // When set to false though, the "proper" behavior where host artifacts _only_ pick up on + // [host] should be applied. + p.change_file( + ".cargo/config", + " + target-applies-to-host = false + + [build] + rustflags = [\"--cfg=foo\"] + ", + ); + p.cargo("check") + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .arg("-Ztarget-applies-to-host") + .with_status(101) + .with_stderr_contains("[..]assertion failed[..]") + .run(); + p.cargo("check --target") + .arg(host) + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .arg("-Ztarget-applies-to-host") + .with_status(101) + .with_stderr_contains("[..]assertion failed[..]") + .run(); +} + +#[cargo_test] +fn host_rustflags_for_build_scripts() { + let host = rustc_host(); + let p = project() + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + // Ensure that --cfg=foo is passed. + fn main() { assert!(cfg!(foo)); } + "#, + ) + .file( + ".cargo/config", + &format!( + " + target-applies-to-host = false + + [host.{}] + rustflags = [\"--cfg=foo\"] + ", + host + ), + ) + .build(); + + p.cargo("check --target") + .arg(host) + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .arg("-Ztarget-applies-to-host") + .arg("-Zhost-config") + .run(); +} + +// target.{}.rustflags takes precedence over build.rustflags +#[cargo_test] +fn target_rustflags_precedence() { + let p = project() + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .file("tests/c.rs", "#[test] fn f() { }") + .file( + ".cargo/config", + &format!( + " + [build] + rustflags = [\"--cfg\", \"foo\"] + + [target.{}] + rustflags = [\"-Z\", \"bogus\"] + ", + rustc_host() + ), + ) + .build(); + + p.cargo("check --lib") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --bin=a") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("check --example=b") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("test") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); + p.cargo("bench") + .with_status(101) + .with_stderr_contains("[..]bogus[..]") + .run(); +} + +#[cargo_test] +fn cfg_rustflags_normal_source() { + let p = project() + .file("src/lib.rs", "pub fn t() {}") + .file("src/bin/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .file("tests/c.rs", "#[test] fn f() { }") + .file( + ".cargo/config", + &format!( + r#" + [target.'cfg({})'] + rustflags = ["--cfg", "bar"] + "#, + if rustc_host().contains("-windows-") { + "windows" + } else { + "not(windows)" + } + ), + ) + .build(); + + p.cargo("build --lib -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg bar[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("build --bin=a -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg bar[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("build --example=b -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg bar[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("test --no-run -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg bar[..]` +[RUNNING] `rustc [..] --cfg bar[..]` +[RUNNING] `rustc [..] --cfg bar[..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` +[EXECUTABLE] `[..]/target/debug/deps/a-[..][EXE]` +[EXECUTABLE] `[..]/target/debug/deps/c-[..][EXE]` +", + ) + .run(); + + p.cargo("bench --no-run -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg bar[..]` +[RUNNING] `rustc [..] --cfg bar[..]` +[RUNNING] `rustc [..] --cfg bar[..]` +[FINISHED] bench [optimized] target(s) in [..] +[EXECUTABLE] `[..]/target/release/deps/foo-[..][EXE]` +[EXECUTABLE] `[..]/target/release/deps/a-[..][EXE]` +", + ) + .run(); +} + +// target.'cfg(...)'.rustflags takes precedence over build.rustflags +#[cargo_test] +fn cfg_rustflags_precedence() { + let p = project() + .file("src/lib.rs", "pub fn t() {}") + .file("src/bin/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .file("tests/c.rs", "#[test] fn f() { }") + .file( + ".cargo/config", + &format!( + r#" + [build] + rustflags = ["--cfg", "foo"] + + [target.'cfg({})'] + rustflags = ["--cfg", "bar"] + "#, + if rustc_host().contains("-windows-") { + "windows" + } else { + "not(windows)" + } + ), + ) + .build(); + + p.cargo("build --lib -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg bar[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("build --bin=a -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg bar[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("build --example=b -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg bar[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("test --no-run -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg bar[..]` +[RUNNING] `rustc [..] --cfg bar[..]` +[RUNNING] `rustc [..] --cfg bar[..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` +[EXECUTABLE] `[..]/target/debug/deps/a-[..][EXE]` +[EXECUTABLE] `[..]/target/debug/deps/c-[..][EXE]` +", + ) + .run(); + + p.cargo("bench --no-run -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg bar[..]` +[RUNNING] `rustc [..] --cfg bar[..]` +[RUNNING] `rustc [..] --cfg bar[..]` +[FINISHED] bench [optimized] target(s) in [..] +[EXECUTABLE] `[..]/target/release/deps/foo-[..][EXE]` +[EXECUTABLE] `[..]/target/release/deps/a-[..][EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn target_rustflags_string_and_array_form1() { + let p1 = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [build] + rustflags = ["--cfg", "foo"] + "#, + ) + .build(); + + p1.cargo("check -v") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg foo[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + let p2 = project() + .file("src/lib.rs", "") + .file( + ".cargo/config", + r#" + [build] + rustflags = "--cfg foo" + "#, + ) + .build(); + + p2.cargo("check -v") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg foo[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn target_rustflags_string_and_array_form2() { + let p1 = project() + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + rustflags = ["--cfg", "foo"] + "#, + rustc_host() + ), + ) + .file("src/lib.rs", "") + .build(); + + p1.cargo("check -v") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg foo[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + let p2 = project() + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + rustflags = "--cfg foo" + "#, + rustc_host() + ), + ) + .file("src/lib.rs", "") + .build(); + + p2.cargo("check -v") + .with_stderr( + "\ +[CHECKING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] --cfg foo[..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn two_matching_in_config() { + let p1 = project() + .file( + ".cargo/config", + r#" + [target.'cfg(unix)'] + rustflags = ["--cfg", 'foo="a"'] + [target.'cfg(windows)'] + rustflags = ["--cfg", 'foo="a"'] + [target.'cfg(target_pointer_width = "32")'] + rustflags = ["--cfg", 'foo="b"'] + [target.'cfg(target_pointer_width = "64")'] + rustflags = ["--cfg", 'foo="b"'] + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + if cfg!(foo = "a") { + println!("a"); + } else if cfg!(foo = "b") { + println!("b"); + } else { + panic!() + } + } + "#, + ) + .build(); + + p1.cargo("run").run(); + p1.cargo("build").with_stderr("[FINISHED] [..]").run(); +} + +#[cargo_test] +fn env_rustflags_misspelled() { + let p = project().file("src/main.rs", "fn main() { }").build(); + + for cmd in &["check", "build", "run", "test", "bench"] { + p.cargo(cmd) + .env("RUST_FLAGS", "foo") + .with_stderr_contains("[WARNING] Cargo does not read `RUST_FLAGS` environment variable. Did you mean `RUSTFLAGS`?") + .run(); + } +} + +#[cargo_test] +fn env_rustflags_misspelled_build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + build = "build.rs" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() { }") + .build(); + + p.cargo("check") + .env("RUST_FLAGS", "foo") + .with_stderr_contains("[WARNING] Cargo does not read `RUST_FLAGS` environment variable. Did you mean `RUSTFLAGS`?") + .run(); +} + +#[cargo_test] +fn remap_path_prefix_ignored() { + // Ensure that --remap-path-prefix does not affect metadata hash. + let p = project().file("src/lib.rs", "").build(); + p.cargo("build").run(); + let rlibs = p + .glob("target/debug/deps/*.rlib") + .collect::, _>>() + .unwrap(); + assert_eq!(rlibs.len(), 1); + p.cargo("clean").run(); + + let check_metadata_same = || { + let rlibs2 = p + .glob("target/debug/deps/*.rlib") + .collect::, _>>() + .unwrap(); + assert_eq!(rlibs, rlibs2); + }; + + p.cargo("build") + .env( + "RUSTFLAGS", + "--remap-path-prefix=/abc=/zoo --remap-path-prefix /spaced=/zoo", + ) + .run(); + check_metadata_same(); + + p.cargo("clean").run(); + p.cargo("rustc -- --remap-path-prefix=/abc=/zoo --remap-path-prefix /spaced=/zoo") + .run(); + check_metadata_same(); +} + +#[cargo_test] +fn remap_path_prefix_works() { + // Check that remap-path-prefix works. + Package::new("bar", "0.1.0") + .file("src/lib.rs", "pub fn f() -> &'static str { file!() }") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + println!("{}", bar::f()); + } + "#, + ) + .build(); + + p.cargo("run") + .env( + "RUSTFLAGS", + format!("--remap-path-prefix={}=/foo", paths::root().display()), + ) + .with_stdout("/foo/home/.cargo/registry/src/[..]/bar-0.1.0/src/lib.rs") + .run(); +} + +#[cargo_test] +fn host_config_rustflags_with_target() { + // regression test for https://github.com/rust-lang/cargo/issues/10206 + let p = project() + .file("src/lib.rs", "") + .file("build.rs.rs", "fn main() { assert!(cfg!(foo)); }") + .file(".cargo/config.toml", "target-applies-to-host = false") + .build(); + + p.cargo("check") + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .arg("-Zhost-config") + .arg("-Ztarget-applies-to-host") + .arg("-Zunstable-options") + .arg("--config") + .arg("host.rustflags=[\"--cfg=foo\"]") + .run(); +} diff --git a/tests/testsuite/search.rs b/tests/testsuite/search.rs new file mode 100644 index 0000000..1f6f403 --- /dev/null +++ b/tests/testsuite/search.rs @@ -0,0 +1,192 @@ +//! Tests for the `cargo search` command. + +use cargo_test_support::cargo_process; +use cargo_test_support::paths; +use cargo_test_support::registry::{RegistryBuilder, Response}; +use std::collections::HashSet; + +const SEARCH_API_RESPONSE: &[u8] = br#" +{ + "crates": [{ + "created_at": "2014-11-16T20:17:35Z", + "description": "Design by contract style assertions for Rust", + "documentation": null, + "downloads": 2, + "homepage": null, + "id": "hoare", + "keywords": [], + "license": null, + "links": { + "owners": "/api/v1/crates/hoare/owners", + "reverse_dependencies": "/api/v1/crates/hoare/reverse_dependencies", + "version_downloads": "/api/v1/crates/hoare/downloads", + "versions": "/api/v1/crates/hoare/versions" + }, + "max_version": "0.1.1", + "name": "hoare", + "repository": "https://github.com/nick29581/libhoare", + "updated_at": "2014-11-20T21:49:21Z", + "versions": null + }, + { + "id": "postgres", + "name": "postgres", + "updated_at": "2020-05-01T23:17:54.335921+00:00", + "versions": null, + "keywords": null, + "categories": null, + "badges": [ + { + "badge_type": "circle-ci", + "attributes": { + "repository": "sfackler/rust-postgres", + "branch": null + } + } + ], + "created_at": "2014-11-24T02:34:44.756689+00:00", + "downloads": 535491, + "recent_downloads": 88321, + "max_version": "0.17.3", + "newest_version": "0.17.3", + "description": "A native, synchronous PostgreSQL client", + "homepage": null, + "documentation": null, + "repository": "https://github.com/sfackler/rust-postgres", + "links": { + "version_downloads": "/api/v1/crates/postgres/downloads", + "versions": "/api/v1/crates/postgres/versions", + "owners": "/api/v1/crates/postgres/owners", + "owner_team": "/api/v1/crates/postgres/owner_team", + "owner_user": "/api/v1/crates/postgres/owner_user", + "reverse_dependencies": "/api/v1/crates/postgres/reverse_dependencies" + }, + "exact_match": true + } + ], + "meta": { + "total": 2 + } +}"#; + +const SEARCH_RESULTS: &str = "\ +hoare = \"0.1.1\" # Design by contract style assertions for Rust +postgres = \"0.17.3\" # A native, synchronous PostgreSQL client +"; + +#[must_use] +fn setup() -> RegistryBuilder { + RegistryBuilder::new() + .http_api() + .add_responder("/api/v1/crates", |_, _| Response { + code: 200, + headers: vec![], + body: SEARCH_API_RESPONSE.to_vec(), + }) +} + +#[cargo_test] +fn not_update() { + let registry = setup().build(); + + use cargo::core::{Shell, Source, SourceId}; + use cargo::sources::RegistrySource; + use cargo::util::Config; + + let sid = SourceId::for_registry(registry.index_url()).unwrap(); + let cfg = Config::new( + Shell::from_write(Box::new(Vec::new())), + paths::root(), + paths::home().join(".cargo"), + ); + let lock = cfg.acquire_package_cache_lock().unwrap(); + let mut regsrc = RegistrySource::remote(sid, &HashSet::new(), &cfg).unwrap(); + regsrc.invalidate_cache(); + regsrc.block_until_ready().unwrap(); + drop(lock); + + cargo_process("search postgres") + .replace_crates_io(registry.index_url()) + .with_stdout_contains(SEARCH_RESULTS) + .with_stderr("") // without "Updating ... index" + .run(); +} + +#[cargo_test] +fn replace_default() { + let registry = setup().build(); + + cargo_process("search postgres") + .replace_crates_io(registry.index_url()) + .with_stdout_contains(SEARCH_RESULTS) + .with_stderr_contains("[..]Updating [..] index") + .run(); +} + +#[cargo_test] +fn simple() { + let registry = setup().build(); + + cargo_process("search postgres --index") + .arg(registry.index_url().as_str()) + .with_stdout_contains(SEARCH_RESULTS) + .run(); +} + +#[cargo_test] +fn multiple_query_params() { + let registry = setup().build(); + + cargo_process("search postgres sql --index") + .arg(registry.index_url().as_str()) + .with_stdout_contains(SEARCH_RESULTS) + .run(); +} + +#[cargo_test] +fn ignore_quiet() { + let registry = setup().build(); + + cargo_process("search -q postgres") + .replace_crates_io(registry.index_url()) + .with_stdout_contains(SEARCH_RESULTS) + .run(); +} + +#[cargo_test] +fn colored_results() { + let registry = setup().build(); + + cargo_process("search --color=never postgres") + .replace_crates_io(registry.index_url()) + .with_stdout_does_not_contain("[..]\x1b[[..]") + .run(); + + cargo_process("search --color=always postgres") + .replace_crates_io(registry.index_url()) + .with_stdout_contains("[..]\x1b[[..]") + .run(); +} + +#[cargo_test] +fn auth_required_failure() { + let server = setup().auth_required().no_configure_token().build(); + + cargo_process("-Zregistry-auth search postgres") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(server.index_url()) + .with_status(101) + .with_stderr_contains("[ERROR] no token found, please run `cargo login`") + .run(); +} + +#[cargo_test] +fn auth_required() { + let server = setup().auth_required().build(); + + cargo_process("-Zregistry-auth search postgres") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(server.index_url()) + .with_stdout_contains(SEARCH_RESULTS) + .run(); +} diff --git a/tests/testsuite/shell_quoting.rs b/tests/testsuite/shell_quoting.rs new file mode 100644 index 0000000..bff3333 --- /dev/null +++ b/tests/testsuite/shell_quoting.rs @@ -0,0 +1,37 @@ +//! This file tests that when the commands being run are shown +//! in the output, their arguments are quoted properly +//! so that the command can be run in a terminal. + +use cargo_test_support::project; + +#[cargo_test] +fn features_are_quoted() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = ["mikeyhew@example.com"] + + [features] + some_feature = [] + default = ["some_feature"] + "#, + ) + .file("src/main.rs", "fn main() {error}") + .build(); + + p.cargo("check -v") + .env("MSYSTEM", "1") + .with_status(101) + .with_stderr_contains( + r#"[RUNNING] `rustc [..] --cfg 'feature="default"' --cfg 'feature="some_feature"' [..]`"# + ).with_stderr_contains( + r#" +Caused by: + process didn't exit successfully: [..] --cfg 'feature="default"' --cfg 'feature="some_feature"' [..]"# + ) + .run(); +} diff --git a/tests/testsuite/source_replacement.rs b/tests/testsuite/source_replacement.rs new file mode 100644 index 0000000..ef7cc55 --- /dev/null +++ b/tests/testsuite/source_replacement.rs @@ -0,0 +1,247 @@ +//! Tests for `[source]` table (source replacement). + +use std::fs; + +use cargo_test_support::registry::{Package, RegistryBuilder, TestRegistry}; +use cargo_test_support::{cargo_process, paths, project, t}; + +fn setup_replacement(config: &str) -> TestRegistry { + let crates_io = RegistryBuilder::new() + .no_configure_registry() + .http_api() + .build(); + + let root = paths::root(); + t!(fs::create_dir(&root.join(".cargo"))); + t!(fs::write(root.join(".cargo/config"), config,)); + crates_io +} + +#[cargo_test] +fn crates_io_token_not_sent_to_replacement() { + // verifies that the crates.io token is not sent to a replacement registry during publish. + let crates_io = setup_replacement( + r#" + [source.crates-io] + replace-with = 'alternative' + "#, + ); + let _alternative = RegistryBuilder::new() + .alternative() + .http_api() + .no_configure_token() + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --registry crates-io") + .replace_crates_io(crates_io.index_url()) + .with_stderr_contains("[UPDATING] crates.io index") + .run(); +} + +#[cargo_test] +fn token_sent_to_correct_registry() { + // verifies that the crates.io token is not sent to a replacement registry during yank. + let crates_io = setup_replacement( + r#" + [source.crates-io] + replace-with = 'alternative' + "#, + ); + let _alternative = RegistryBuilder::new().alternative().http_api().build(); + + cargo_process("yank foo@0.0.1 --registry crates-io") + .replace_crates_io(crates_io.index_url()) + .with_stderr( + "\ +[UPDATING] crates.io index +[YANK] foo@0.0.1 +", + ) + .run(); + + cargo_process("yank foo@0.0.1 --registry alternative") + .replace_crates_io(crates_io.index_url()) + .with_stderr( + "\ +[UPDATING] `alternative` index +[YANK] foo@0.0.1 +", + ) + .run(); +} + +#[cargo_test] +fn ambiguous_registry() { + // verifies that an error is issued when a source-replacement is configured + // and no --registry argument is given. + let crates_io = setup_replacement( + r#" + [source.crates-io] + replace-with = 'alternative' + "#, + ); + let _alternative = RegistryBuilder::new() + .alternative() + .http_api() + .no_configure_token() + .build(); + + cargo_process("yank foo@0.0.1") + .replace_crates_io(crates_io.index_url()) + .with_status(101) + .with_stderr( + "\ +error: crates-io is replaced with remote registry alternative; +include `--registry alternative` or `--registry crates-io` +", + ) + .run(); +} + +#[cargo_test] +fn yank_with_default_crates_io() { + // verifies that no error is given when registry.default is used. + let crates_io = setup_replacement( + r#" + [source.crates-io] + replace-with = 'alternative' + + [registry] + default = 'crates-io' + "#, + ); + let _alternative = RegistryBuilder::new().alternative().http_api().build(); + + cargo_process("yank foo@0.0.1") + .replace_crates_io(crates_io.index_url()) + .with_stderr( + "\ +[UPDATING] crates.io index +[YANK] foo@0.0.1 +", + ) + .run(); +} + +#[cargo_test] +fn yank_with_default_alternative() { + // verifies that no error is given when registry.default is an alt registry. + let crates_io = setup_replacement( + r#" + [source.crates-io] + replace-with = 'alternative' + + [registry] + default = 'alternative' + "#, + ); + let _alternative = RegistryBuilder::new().alternative().http_api().build(); + + cargo_process("yank foo@0.0.1") + .replace_crates_io(crates_io.index_url()) + .with_stderr( + "\ +[UPDATING] `alternative` index +[YANK] foo@0.0.1 +", + ) + .run(); +} + +#[cargo_test] +fn publish_with_replacement() { + // verifies that the crates.io token is not sent to a replacement registry during publish. + let crates_io = setup_replacement( + r#" + [source.crates-io] + replace-with = 'alternative' + "#, + ); + let _alternative = RegistryBuilder::new() + .alternative() + .http_api() + .no_configure_token() + .build(); + + // Publish bar only to alternative. This tests that the publish verification build + // does uses the source replacement. + Package::new("bar", "1.0.0").alternative(true).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Verifies that the crates.io index is used to find the publishing endpoint + // and that the crate is sent to crates.io. The source replacement is only used + // for the verification step. + p.cargo("publish --registry crates-io") + .replace_crates_io(crates_io.index_url()) + .with_stderr( + "\ +[UPDATING] crates.io index +[WARNING] manifest has no documentation, homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] foo v0.0.1 ([..]) +[VERIFYING] foo v0.0.1 ([..]) +[UPDATING] `alternative` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 (registry `alternative`) +[COMPILING] bar v1.0.0 +[COMPILING] foo v0.0.1 ([..]foo-0.0.1) +[FINISHED] dev [..] +[PACKAGED] [..] +[UPLOADING] foo v0.0.1 ([..]) +[UPDATING] crates.io index +", + ) + .run(); +} + +#[cargo_test] +fn undefined_default() { + // verifies that no error is given when registry.default is used. + let crates_io = setup_replacement( + r#" + [registry] + default = 'undefined' + "#, + ); + + cargo_process("yank foo@0.0.1") + .replace_crates_io(crates_io.index_url()) + .with_status(101) + .with_stderr( + "[ERROR] no index found for registry: `undefined` +", + ) + .run(); +} diff --git a/tests/testsuite/ssh.rs b/tests/testsuite/ssh.rs new file mode 100644 index 0000000..d812be2 --- /dev/null +++ b/tests/testsuite/ssh.rs @@ -0,0 +1,554 @@ +//! Network tests for SSH connections. +//! +//! Note that these tests will generally require setting CARGO_CONTAINER_TESTS +//! or CARGO_PUBLIC_NETWORK_TESTS. +//! +//! NOTE: The container tests almost certainly won't work on Windows. + +use cargo_test_support::containers::{Container, ContainerHandle, MkFile}; +use cargo_test_support::{paths, process, project, Project}; +use std::fs; +use std::io::Write; +use std::path::PathBuf; + +fn ssh_repo_url(container: &ContainerHandle, name: &str) -> String { + let port = container.port_mappings[&22]; + format!("ssh://testuser@127.0.0.1:{port}/repos/{name}.git") +} + +/// The path to the client's private key. +fn key_path() -> PathBuf { + paths::home().join(".ssh/id_ed25519") +} + +/// Generates the SSH keys for authenticating into the container. +fn gen_ssh_keys() -> String { + let path = key_path(); + process("ssh-keygen") + .args(&["-t", "ed25519", "-N", "", "-f"]) + .arg(&path) + .exec_with_output() + .unwrap(); + let pub_key = path.with_extension("pub"); + fs::read_to_string(pub_key).unwrap() +} + +/// Handler for running ssh-agent for SSH authentication. +/// +/// Be sure to set `SSH_AUTH_SOCK` when running a process in order to use the +/// agent. Keys will need to be copied into the container with the +/// `authorized_keys()` method. +struct Agent { + sock: PathBuf, + pid: String, + ssh_dir: PathBuf, + pub_key: String, +} + +impl Agent { + fn launch() -> Agent { + let ssh_dir = paths::home().join(".ssh"); + fs::create_dir(&ssh_dir).unwrap(); + let pub_key = gen_ssh_keys(); + + let sock = paths::root().join("agent"); + let output = process("ssh-agent") + .args(&["-s", "-a"]) + .arg(&sock) + .exec_with_output() + .unwrap(); + let stdout = std::str::from_utf8(&output.stdout).unwrap(); + let start = stdout.find("SSH_AGENT_PID=").unwrap() + 14; + let end = &stdout[start..].find(';').unwrap(); + let pid = (&stdout[start..start + end]).to_string(); + eprintln!("SSH_AGENT_PID={pid}"); + process("ssh-add") + .arg(key_path()) + .env("SSH_AUTH_SOCK", &sock) + .exec_with_output() + .unwrap(); + Agent { + sock, + pid, + ssh_dir, + pub_key, + } + } + + /// Returns a `MkFile` which can be passed into the `Container` builder to + /// copy an `authorized_keys` file containing this agent's public key. + fn authorized_keys(&self) -> MkFile { + MkFile::path("home/testuser/.ssh/authorized_keys") + .contents(self.pub_key.as_bytes()) + .mode(0o600) + .uid(100) + .gid(101) + } +} + +impl Drop for Agent { + fn drop(&mut self) { + if let Err(e) = process("ssh-agent") + .args(&["-k", "-a"]) + .arg(&self.sock) + .env("SSH_AGENT_PID", &self.pid) + .exec_with_output() + { + eprintln!("failed to stop ssh-agent: {e:?}"); + } + } +} + +/// Common project used for several tests. +fn foo_bar_project(url: &str) -> Project { + project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {{ git = "{url}" }} + "# + ), + ) + .file("src/lib.rs", "") + .build() +} + +#[cargo_test(container_test)] +fn no_known_host() { + // When host is not known, it should show an error. + let sshd = Container::new("sshd").launch(); + let url = ssh_repo_url(&sshd, "bar"); + let p = foo_bar_project(&url); + p.cargo("fetch") + .with_status(101) + .with_stderr( + "\ +[UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git` +error: failed to get `bar` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update ssh://testuser@127.0.0.1:[..]/repos/bar.git + +Caused by: + failed to clone into: [ROOT]/home/.cargo/git/db/bar-[..] + +Caused by: + error: unknown SSH host key + The SSH host key for `[127.0.0.1]:[..]` is not known and cannot be validated. + + To resolve this issue, add the host key to the `net.ssh.known-hosts` array in \ + your Cargo configuration (such as [ROOT]/home/.cargo/config.toml) or in your \ + OpenSSH known_hosts file at [ROOT]/home/.ssh/known_hosts + + The key to add is: + + [127.0.0.1]:[..] ecdsa-sha2-nistp256 AAAA[..] + + The ECDSA key fingerprint is: SHA256:[..] + This fingerprint should be validated with the server administrator that it is correct. + + See https://doc.rust-lang.org/stable/cargo/appendix/git-authentication.html#ssh-known-hosts \ + for more information. +", + ) + .run(); +} + +#[cargo_test(container_test)] +fn known_host_works() { + // The key displayed in the error message should work when added to known_hosts. + let agent = Agent::launch(); + let sshd = Container::new("sshd") + .file(agent.authorized_keys()) + .launch(); + let url = ssh_repo_url(&sshd, "bar"); + let p = foo_bar_project(&url); + let output = p + .cargo("fetch") + .env("SSH_AUTH_SOCK", &agent.sock) + .build_command() + .output() + .unwrap(); + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + + // Validate the fingerprint while we're here. + let fingerprint = stderr + .lines() + .find(|line| line.starts_with(" The ECDSA key fingerprint")) + .unwrap() + .trim(); + let fingerprint = &fingerprint[30..]; + let finger_out = sshd.exec(&["ssh-keygen", "-l", "-f", "/etc/ssh/ssh_host_ecdsa_key.pub"]); + let gen_finger = std::str::from_utf8(&finger_out.stdout).unwrap(); + // + let gen_finger = gen_finger.split_whitespace().nth(1).unwrap(); + assert_eq!(fingerprint, gen_finger); + + // Add the key to known_hosts, and try again. + let key = stderr + .lines() + .find(|line| line.starts_with(" [127.0.0.1]:")) + .unwrap() + .trim(); + fs::write(agent.ssh_dir.join("known_hosts"), key).unwrap(); + p.cargo("fetch") + .env("SSH_AUTH_SOCK", &agent.sock) + .with_stderr("[UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git`") + .run(); +} + +#[cargo_test(container_test)] +fn same_key_different_hostname() { + // The error message should mention if an identical key was found. + let agent = Agent::launch(); + let sshd = Container::new("sshd").launch(); + + let hostkey = sshd.read_file("/etc/ssh/ssh_host_ecdsa_key.pub"); + let known_hosts = format!("example.com {hostkey}"); + fs::write(agent.ssh_dir.join("known_hosts"), known_hosts).unwrap(); + + let url = ssh_repo_url(&sshd, "bar"); + let p = foo_bar_project(&url); + p.cargo("fetch") + .with_status(101) + .with_stderr( + "\ +[UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git` +error: failed to get `bar` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update ssh://testuser@127.0.0.1:[..]/repos/bar.git + +Caused by: + failed to clone into: [ROOT]/home/.cargo/git/db/bar-[..] + +Caused by: + error: unknown SSH host key + The SSH host key for `[127.0.0.1]:[..]` is not known and cannot be validated. + + To resolve this issue, add the host key to the `net.ssh.known-hosts` array in \ + your Cargo configuration (such as [ROOT]/home/.cargo/config.toml) or in your \ + OpenSSH known_hosts file at [ROOT]/home/.ssh/known_hosts + + The key to add is: + + [127.0.0.1]:[..] ecdsa-sha2-nistp256 AAAA[..] + + The ECDSA key fingerprint is: SHA256:[..] + This fingerprint should be validated with the server administrator that it is correct. + Note: This host key was found, but is associated with a different host: + [ROOT]/home/.ssh/known_hosts line 1: example.com + + See https://doc.rust-lang.org/stable/cargo/appendix/git-authentication.html#ssh-known-hosts \ + for more information. +", + ) + .run(); +} + +#[cargo_test(container_test)] +fn known_host_without_port() { + // A known_host entry without a port should match a connection to a non-standard port. + let agent = Agent::launch(); + let sshd = Container::new("sshd") + .file(agent.authorized_keys()) + .launch(); + + let hostkey = sshd.read_file("/etc/ssh/ssh_host_ecdsa_key.pub"); + // The important part of this test is that this line does not have a port. + let known_hosts = format!("127.0.0.1 {hostkey}"); + fs::write(agent.ssh_dir.join("known_hosts"), known_hosts).unwrap(); + let url = ssh_repo_url(&sshd, "bar"); + let p = foo_bar_project(&url); + p.cargo("fetch") + .env("SSH_AUTH_SOCK", &agent.sock) + .with_stderr("[UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git`") + .run(); +} + +#[cargo_test(container_test)] +fn hostname_case_insensitive() { + // hostname checking should be case-insensitive. + let agent = Agent::launch(); + let sshd = Container::new("sshd") + .file(agent.authorized_keys()) + .launch(); + + // Consider using `gethostname-rs` instead? + let hostname = process("hostname").exec_with_output().unwrap(); + let hostname = std::str::from_utf8(&hostname.stdout).unwrap().trim(); + let inv_hostname = if hostname.chars().any(|c| c.is_lowercase()) { + hostname.to_uppercase() + } else { + // There should be *some* chars in the name. + assert!(hostname.chars().any(|c| c.is_uppercase())); + hostname.to_lowercase() + }; + eprintln!("converted {hostname} to {inv_hostname}"); + + let hostkey = sshd.read_file("/etc/ssh/ssh_host_ecdsa_key.pub"); + let known_hosts = format!("{inv_hostname} {hostkey}"); + fs::write(agent.ssh_dir.join("known_hosts"), known_hosts).unwrap(); + let port = sshd.port_mappings[&22]; + let url = format!("ssh://testuser@{hostname}:{port}/repos/bar.git"); + let p = foo_bar_project(&url); + p.cargo("fetch") + .env("SSH_AUTH_SOCK", &agent.sock) + .with_stderr(&format!( + "[UPDATING] git repository `ssh://testuser@{hostname}:{port}/repos/bar.git`" + )) + .run(); +} + +#[cargo_test(container_test)] +fn invalid_key_error() { + // An error when a known_host value doesn't match. + let agent = Agent::launch(); + let sshd = Container::new("sshd") + .file(agent.authorized_keys()) + .launch(); + + let port = sshd.port_mappings[&22]; + let known_hosts = format!( + "[127.0.0.1]:{port} ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLqLMclVr7MDuaVsm3sEnnq2OrGxTFiHSw90wd6N14BU8xVC9cZldC3rJ58Wmw6bEVKPjk7foNG0lHwS5bCKX+U=\n" + ); + fs::write(agent.ssh_dir.join("known_hosts"), known_hosts).unwrap(); + let url = ssh_repo_url(&sshd, "bar"); + let p = foo_bar_project(&url); + p.cargo("fetch") + .env("SSH_AUTH_SOCK", &agent.sock) + .with_status(101) + .with_stderr(&format!("\ +[UPDATING] git repository `ssh://testuser@127.0.0.1:{port}/repos/bar.git` +error: failed to get `bar` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` + +Caused by: + failed to load source for dependency `bar` + +Caused by: + Unable to update ssh://testuser@127.0.0.1:{port}/repos/bar.git + +Caused by: + failed to clone into: [ROOT]/home/.cargo/git/db/bar-[..] + +Caused by: + error: SSH host key has changed for `[127.0.0.1]:{port}` + ********************************* + * WARNING: HOST KEY HAS CHANGED * + ********************************* + This may be caused by a man-in-the-middle attack, or the server may have changed its host key. + + The ECDSA fingerprint for the key from the remote host is: + SHA256:[..] + + You are strongly encouraged to contact the server administrator for `[127.0.0.1]:{port}` \ + to verify that this new key is correct. + + If you can verify that the server has a new key, you can resolve this error by \ + removing the old ecdsa-sha2-nistp256 key for `[127.0.0.1]:{port}` located at \ + [ROOT]/home/.ssh/known_hosts line 1, and adding the new key to the \ + `net.ssh.known-hosts` array in your Cargo configuration (such as \ + [ROOT]/home/.cargo/config.toml) or in your OpenSSH known_hosts file at \ + [ROOT]/home/.ssh/known_hosts + + The key provided by the remote host is: + + [127.0.0.1]:{port} ecdsa-sha2-nistp256 [..] + + See https://doc.rust-lang.org/stable/cargo/appendix/git-authentication.html#ssh-known-hosts for more information. +")) + .run(); + // Add the key, it should work even with the old key left behind. + let hostkey = sshd.read_file("/etc/ssh/ssh_host_ecdsa_key.pub"); + let known_hosts_path = agent.ssh_dir.join("known_hosts"); + let mut f = fs::OpenOptions::new() + .append(true) + .open(known_hosts_path) + .unwrap(); + write!(f, "[127.0.0.1]:{port} {hostkey}").unwrap(); + drop(f); + p.cargo("fetch") + .env("SSH_AUTH_SOCK", &agent.sock) + .with_stderr("[UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git`") + .run(); +} + +// For unknown reasons, this test occasionally fails on Windows with a +// LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE error: +// failed to start SSH session: Unable to exchange encryption keys; class=Ssh (23) +#[cargo_test(public_network_test, ignore_windows = "test is flaky on windows")] +fn invalid_github_key() { + // A key for github.com in known_hosts should override the built-in key. + // This uses a bogus key which should result in an error. + let ssh_dir = paths::home().join(".ssh"); + fs::create_dir(&ssh_dir).unwrap(); + let known_hosts = "\ + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLqLMclVr7MDuaVsm3sEnnq2OrGxTFiHSw90wd6N14BU8xVC9cZldC3rJ58Wmw6bEVKPjk7foNG0lHwS5bCKX+U=\n\ + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDgi+8rMcyFCBq5y7BXrb2aaYGhMjlU3QDy7YDvtNL5KSecYOsaqQHaXr87Bbx0EEkgbhK4kVMkmThlCoNITQS9Vc3zIMQ+Tg6+O4qXx719uCzywl50Tb5tDqPGMj54jcq3VUiu/dvse0yeehyvzoPNWewgGWLx11KI4A4wOwMnc6guhculEWe9DjGEjUQ34lPbmdfu/Hza7ZVu/RhgF/wc43uzXWB2KpMEqtuY1SgRlCZqTASoEtfKZi0AuM7AEdOwE5aTotS4CQZHWimb1bMFpF4DAq92CZ8Jhrm4rWETbO29WmjviCJEA3KNQyd3oA7H9AE9z/22PJaVEmjiZZ+wyLgwyIpOlsnHYNEdGeQMQ4SgLRkARLwcnKmByv1AAxsBW4LI3Os4FpwxVPdXHcBebydtvxIsbtUVkkq99nbsIlnSRFSTvb0alrdzRuKTdWpHtN1v9hagFqmeCx/kJfH76NXYBbtaWZhSOnxfEbhLYuOb+IS4jYzHAIkzy9FjVuk=\n\ + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEeMB6BUAW6FfvfLxRO3kGASe0yXnrRT4kpqncsup2b2\n"; + fs::write(ssh_dir.join("known_hosts"), known_hosts).unwrap(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = { git = "ssh://git@github.com/rust-lang/bitflags.git", tag = "1.3.2" } + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("fetch") + .with_status(101) + .with_stderr_contains(" error: SSH host key has changed for `github.com`") + .run(); +} + +// For unknown reasons, this test occasionally fails on Windows with a +// LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE error: +// failed to start SSH session: Unable to exchange encryption keys; class=Ssh (23) +#[cargo_test(public_network_test, ignore_windows = "test is flaky on windows")] +fn bundled_github_works() { + // The bundled key for github.com works. + // + // Use a bogus auth sock to force an authentication error. + // On Windows, if the agent service is running, it could allow a + // successful authentication. + // + // If the bundled hostkey did not work, it would result in an "unknown SSH + // host key" instead. + let bogus_auth_sock = paths::home().join("ssh_auth_sock"); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = { git = "ssh://git@github.com/rust-lang/bitflags.git", tag = "1.3.2" } + "#, + ) + .file("src/lib.rs", "") + .build(); + let err = if cfg!(windows) { + "error authenticating: unable to connect to agent pipe; class=Ssh (23)" + } else { + "error authenticating: failed connecting with agent; class=Ssh (23)" + }; + p.cargo("fetch") + .env("SSH_AUTH_SOCK", &bogus_auth_sock) + .with_status(101) + .with_stderr(&format!( + "\ +[UPDATING] git repository `ssh://git@github.com/rust-lang/bitflags.git` +error: failed to get `bitflags` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` + +Caused by: + failed to load source for dependency `bitflags` + +Caused by: + Unable to update ssh://git@github.com/rust-lang/bitflags.git?tag=1.3.2 + +Caused by: + failed to clone into: [ROOT]/home/.cargo/git/db/bitflags-[..] + +Caused by: + failed to authenticate when downloading repository + + * attempted ssh-agent authentication, but no usernames succeeded: `git` + + if the git CLI succeeds then `net.git-fetch-with-cli` may help here + https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli + +Caused by: + {err} +" + )) + .run(); + + // Explicit :22 should also work with bundled. + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = { git = "ssh://git@github.com:22/rust-lang/bitflags.git", tag = "1.3.2" } + "#, + ); + p.cargo("fetch") + .env("SSH_AUTH_SOCK", &bogus_auth_sock) + .with_status(101) + .with_stderr(&format!( + "\ +[UPDATING] git repository `ssh://git@github.com:22/rust-lang/bitflags.git` +error: failed to get `bitflags` as a dependency of package `foo v0.1.0 ([ROOT]/foo)` + +Caused by: + failed to load source for dependency `bitflags` + +Caused by: + Unable to update ssh://git@github.com:22/rust-lang/bitflags.git?tag=1.3.2 + +Caused by: + failed to clone into: [ROOT]/home/.cargo/git/db/bitflags-[..] + +Caused by: + failed to authenticate when downloading repository + + * attempted ssh-agent authentication, but no usernames succeeded: `git` + + if the git CLI succeeds then `net.git-fetch-with-cli` may help here + https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli + +Caused by: + {err} +" + )) + .run(); +} + +#[cargo_test(container_test)] +fn ssh_key_in_config() { + // known_host in config works. + let agent = Agent::launch(); + let sshd = Container::new("sshd") + .file(agent.authorized_keys()) + .launch(); + let hostkey = sshd.read_file("/etc/ssh/ssh_host_ecdsa_key.pub"); + let url = ssh_repo_url(&sshd, "bar"); + let p = foo_bar_project(&url); + p.change_file( + ".cargo/config.toml", + &format!( + r#" + [net.ssh] + known-hosts = ['127.0.0.1 {}'] + "#, + hostkey.trim() + ), + ); + p.cargo("fetch") + .env("SSH_AUTH_SOCK", &agent.sock) + .with_stderr("[UPDATING] git repository `ssh://testuser@127.0.0.1:[..]/repos/bar.git`") + .run(); +} diff --git a/tests/testsuite/standard_lib.rs b/tests/testsuite/standard_lib.rs new file mode 100644 index 0000000..d3be303 --- /dev/null +++ b/tests/testsuite/standard_lib.rs @@ -0,0 +1,657 @@ +//! Tests for building the standard library (-Zbuild-std). +//! +//! These tests all use a "mock" standard library so that we don't have to +//! rebuild the real one. There is a separate integration test `build-std` +//! which builds the real thing, but that should be avoided if possible. + +use cargo_test_support::registry::{Dependency, Package}; +use cargo_test_support::ProjectBuilder; +use cargo_test_support::{paths, project, rustc_host, Execs}; +use std::path::{Path, PathBuf}; + +struct Setup { + rustc_wrapper: PathBuf, + real_sysroot: String, +} + +fn setup() -> Setup { + // Our mock sysroot requires a few packages from crates.io, so make sure + // they're "published" to crates.io. Also edit their code a bit to make sure + // that they have access to our custom crates with custom apis. + Package::new("registry-dep-using-core", "1.0.0") + .file( + "src/lib.rs", + " + #![no_std] + + #[cfg(feature = \"mockbuild\")] + pub fn custom_api() { + } + + #[cfg(not(feature = \"mockbuild\"))] + pub fn non_sysroot_api() { + core::custom_api(); + } + ", + ) + .add_dep(Dependency::new("rustc-std-workspace-core", "*").optional(true)) + .feature("mockbuild", &["rustc-std-workspace-core"]) + .publish(); + Package::new("registry-dep-using-alloc", "1.0.0") + .file( + "src/lib.rs", + " + #![no_std] + + extern crate alloc; + + #[cfg(feature = \"mockbuild\")] + pub fn custom_api() { + } + + #[cfg(not(feature = \"mockbuild\"))] + pub fn non_sysroot_api() { + core::custom_api(); + alloc::custom_api(); + } + ", + ) + .add_dep(Dependency::new("rustc-std-workspace-core", "*").optional(true)) + .add_dep(Dependency::new("rustc-std-workspace-alloc", "*").optional(true)) + .feature( + "mockbuild", + &["rustc-std-workspace-core", "rustc-std-workspace-alloc"], + ) + .publish(); + Package::new("registry-dep-using-std", "1.0.0") + .file( + "src/lib.rs", + " + #[cfg(feature = \"mockbuild\")] + pub fn custom_api() { + } + + #[cfg(not(feature = \"mockbuild\"))] + pub fn non_sysroot_api() { + std::custom_api(); + } + ", + ) + .add_dep(Dependency::new("rustc-std-workspace-std", "*").optional(true)) + .feature("mockbuild", &["rustc-std-workspace-std"]) + .publish(); + + let p = ProjectBuilder::new(paths::root().join("rustc-wrapper")) + .file( + "src/main.rs", + r#" + use std::process::Command; + use std::env; + fn main() { + let mut args = env::args().skip(1).collect::>(); + + let is_sysroot_crate = env::var_os("RUSTC_BOOTSTRAP").is_some(); + if is_sysroot_crate { + args.push("--sysroot".to_string()); + args.push(env::var("REAL_SYSROOT").unwrap()); + } else if args.iter().any(|arg| arg == "--target") { + // build-std target unit + args.push("--sysroot".to_string()); + args.push("/path/to/nowhere".to_string()); + } else { + // host unit, do not use sysroot + } + + let ret = Command::new(&args[0]).args(&args[1..]).status().unwrap(); + std::process::exit(ret.code().unwrap_or(1)); + } + "#, + ) + .build(); + p.cargo("build").run(); + + Setup { + rustc_wrapper: p.bin("foo"), + real_sysroot: paths::sysroot(), + } +} + +fn enable_build_std(e: &mut Execs, setup: &Setup) { + // First up, force Cargo to use our "mock sysroot" which mimics what + // libstd looks like upstream. + let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/testsuite/mock-std"); + e.env("__CARGO_TESTS_ONLY_SRC_ROOT", &root); + + e.masquerade_as_nightly_cargo(&["build-std"]); + + // We do various shenanigans to ensure our "mock sysroot" actually links + // with the real sysroot, so we don't have to actually recompile std for + // each test. Perform all that logic here, namely: + // + // * RUSTC_WRAPPER - uses our shim executable built above to control rustc + // * REAL_SYSROOT - used by the shim executable to swap out to the real + // sysroot temporarily for some compilations + // * RUST{,DOC}FLAGS - an extra `-L` argument to ensure we can always load + // crates from the sysroot, but only indirectly through other crates. + e.env("RUSTC_WRAPPER", &setup.rustc_wrapper); + e.env("REAL_SYSROOT", &setup.real_sysroot); + let libdir = format!("/lib/rustlib/{}/lib", rustc_host()); + e.env( + "RUSTFLAGS", + format!("-Ldependency={}{}", setup.real_sysroot, libdir), + ); + e.env( + "RUSTDOCFLAGS", + format!("-Ldependency={}{}", setup.real_sysroot, libdir), + ); +} + +// Helper methods used in the tests below +trait BuildStd: Sized { + fn build_std(&mut self, setup: &Setup) -> &mut Self; + fn build_std_arg(&mut self, setup: &Setup, arg: &str) -> &mut Self; + fn target_host(&mut self) -> &mut Self; +} + +impl BuildStd for Execs { + fn build_std(&mut self, setup: &Setup) -> &mut Self { + enable_build_std(self, setup); + self.arg("-Zbuild-std"); + self + } + + fn build_std_arg(&mut self, setup: &Setup, arg: &str) -> &mut Self { + enable_build_std(self, setup); + self.arg(format!("-Zbuild-std={}", arg)); + self + } + + fn target_host(&mut self) -> &mut Self { + self.arg("--target").arg(rustc_host()); + self + } +} + +#[cargo_test(build_std_mock)] +fn basic() { + let setup = setup(); + + let p = project() + .file( + "src/main.rs", + " + fn main() { + std::custom_api(); + foo::f(); + } + + #[test] + fn smoke_bin_unit() { + std::custom_api(); + foo::f(); + } + ", + ) + .file( + "src/lib.rs", + " + extern crate alloc; + extern crate proc_macro; + + /// ``` + /// foo::f(); + /// ``` + pub fn f() { + core::custom_api(); + std::custom_api(); + alloc::custom_api(); + proc_macro::custom_api(); + } + + #[test] + fn smoke_lib_unit() { + std::custom_api(); + f(); + } + ", + ) + .file( + "tests/smoke.rs", + " + #[test] + fn smoke_integration() { + std::custom_api(); + foo::f(); + } + ", + ) + .build(); + + p.cargo("check -v").build_std(&setup).target_host().run(); + p.cargo("build").build_std(&setup).target_host().run(); + p.cargo("run").build_std(&setup).target_host().run(); + p.cargo("test").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn simple_lib_std() { + let setup = setup(); + + let p = project().file("src/lib.rs", "").build(); + p.cargo("build -v") + .build_std(&setup) + .target_host() + .with_stderr_contains("[RUNNING] `[..]--crate-name std [..]`") + .run(); + // Check freshness. + p.change_file("src/lib.rs", " "); + p.cargo("build -v") + .build_std(&setup) + .target_host() + .with_stderr_contains("[FRESH] std[..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn simple_bin_std() { + let setup = setup(); + + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("run -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn lib_nostd() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + #![no_std] + pub fn foo() { + assert_eq!(u8::MIN, 0); + } + "#, + ) + .build(); + p.cargo("build -v --lib") + .build_std_arg(&setup, "core") + .target_host() + .with_stderr_does_not_contain("[..]libstd[..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn check_core() { + let setup = setup(); + + let p = project() + .file("src/lib.rs", "#![no_std] fn unused_fn() {}") + .build(); + + p.cargo("check -v") + .build_std_arg(&setup, "core") + .target_host() + .with_stderr_contains("[WARNING] [..]unused_fn[..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn depend_same_as_std() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + pub fn f() { + registry_dep_using_core::non_sysroot_api(); + registry_dep_using_alloc::non_sysroot_api(); + registry_dep_using_std::non_sysroot_api(); + } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + registry-dep-using-core = "1.0" + registry-dep-using-alloc = "1.0" + registry-dep-using-std = "1.0" + "#, + ) + .build(); + + p.cargo("build -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn test() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + #[cfg(test)] + mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } + } + "#, + ) + .build(); + + p.cargo("test -v") + .build_std(&setup) + .target_host() + .with_stdout_contains("test tests::it_works ... ok") + .run(); +} + +#[cargo_test(build_std_mock)] +fn target_proc_macro() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + extern crate proc_macro; + pub fn f() { + let _ts = proc_macro::TokenStream::new(); + } + "#, + ) + .build(); + + p.cargo("build -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn bench() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + #![feature(test)] + extern crate test; + + #[bench] + fn b1(b: &mut test::Bencher) { + b.iter(|| ()) + } + "#, + ) + .build(); + + p.cargo("bench -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn doc() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + /// Doc + pub fn f() -> Result<(), ()> {Ok(())} + "#, + ) + .build(); + + p.cargo("doc -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn check_std() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + " + extern crate core; + extern crate alloc; + extern crate proc_macro; + pub fn f() {} + ", + ) + .file("src/main.rs", "fn main() {}") + .file( + "tests/t1.rs", + r#" + #[test] + fn t1() { + assert_eq!(1, 2); + } + "#, + ) + .build(); + + p.cargo("check -v --all-targets") + .build_std(&setup) + .target_host() + .run(); + p.cargo("check -v --all-targets --profile=test") + .build_std(&setup) + .target_host() + .run(); +} + +#[cargo_test(build_std_mock)] +fn doctest() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + /// Doc + /// ``` + /// std::custom_api(); + /// ``` + pub fn f() {} + "#, + ) + .build(); + + p.cargo("test --doc -v -Zdoctest-xcompile") + .build_std(&setup) + .with_stdout_contains("test src/lib.rs - f [..] ... ok") + .target_host() + .run(); +} + +#[cargo_test(build_std_mock)] +fn no_implicit_alloc() { + // Demonstrate that alloc is not implicitly in scope. + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + pub fn f() { + let _: Vec = alloc::vec::Vec::new(); + } + "#, + ) + .build(); + + p.cargo("build -v") + .build_std(&setup) + .target_host() + .with_stderr_contains("[..]use of undeclared [..]`alloc`") + .with_status(101) + .run(); +} + +#[cargo_test(build_std_mock)] +fn macro_expanded_shadow() { + // This tests a bug caused by the previous use of `--extern` to directly + // load sysroot crates. This necessitated the switch to `--sysroot` to + // retain existing behavior. See + // https://github.com/rust-lang/wg-cargo-std-aware/issues/40 for more + // detail. + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + macro_rules! a { + () => (extern crate std as alloc;) + } + a!(); + "#, + ) + .build(); + + p.cargo("build -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn ignores_incremental() { + // Incremental is not really needed for std, make sure it is disabled. + // Incremental also tends to have bugs that affect std libraries more than + // any other crate. + let setup = setup(); + + let p = project().file("src/lib.rs", "").build(); + p.cargo("build") + .env("CARGO_INCREMENTAL", "1") + .build_std(&setup) + .target_host() + .run(); + let incremental: Vec<_> = p + .glob(format!("target/{}/debug/incremental/*", rustc_host())) + .map(|e| e.unwrap()) + .collect(); + assert_eq!(incremental.len(), 1); + assert!(incremental[0] + .file_name() + .unwrap() + .to_str() + .unwrap() + .starts_with("foo-")); +} + +#[cargo_test(build_std_mock)] +fn cargo_config_injects_compiler_builtins() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + #![no_std] + pub fn foo() { + assert_eq!(u8::MIN, 0); + } + "#, + ) + .file( + ".cargo/config.toml", + r#" + [unstable] + build-std = ['core'] + "#, + ) + .build(); + let mut build = p.cargo("build -v --lib"); + enable_build_std(&mut build, &setup); + build + .target_host() + .with_stderr_does_not_contain("[..]libstd[..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn different_features() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + " + pub fn foo() { + std::conditional_function(); + } + ", + ) + .build(); + p.cargo("build") + .build_std(&setup) + .arg("-Zbuild-std-features=feature1") + .target_host() + .run(); +} + +#[cargo_test(build_std_mock)] +fn no_roots() { + // Checks for a bug where it would panic if there are no roots. + let setup = setup(); + + let p = project().file("tests/t1.rs", "").build(); + p.cargo("build") + .build_std(&setup) + .target_host() + .with_stderr_contains("[FINISHED] [..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn proc_macro_only() { + // Checks for a bug where it would panic if building a proc-macro only + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build") + .build_std(&setup) + .target_host() + .with_stderr_contains("[FINISHED] [..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn fetch() { + let setup = setup(); + + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("fetch") + .build_std(&setup) + .target_host() + .with_stderr_contains("[DOWNLOADED] [..]") + .run(); + p.cargo("build") + .build_std(&setup) + .target_host() + .with_stderr_does_not_contain("[DOWNLOADED] [..]") + .run(); +} diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs new file mode 100644 index 0000000..add0a99 --- /dev/null +++ b/tests/testsuite/test.rs @@ -0,0 +1,4820 @@ +//! Tests for the `cargo test` command. + +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::Package; +use cargo_test_support::{ + basic_bin_manifest, basic_lib_manifest, basic_manifest, cargo_exe, project, +}; +use cargo_test_support::{cross_compile, paths}; +use cargo_test_support::{rustc_host, rustc_host_env, sleep_ms}; +use std::fs; + +#[cargo_test] +fn cargo_test_simple() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + + #[test] + fn test_hello() { + assert_eq!(hello(), "hello") + } + "#, + ) + .build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("hello\n").run(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("test test_hello ... ok") + .run(); +} + +#[cargo_test] +fn cargo_test_release() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate bar; + pub fn foo() { bar::bar(); } + + #[test] + fn test() { foo(); } + "#, + ) + .file( + "tests/test.rs", + r#" + extern crate foo; + + #[test] + fn test() { foo::foo(); } + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("test -v --release") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[RUNNING] [..] -C opt-level=3 [..] +[COMPILING] foo v0.1.0 ([CWD]) +[RUNNING] [..] -C opt-level=3 [..] +[RUNNING] [..] -C opt-level=3 [..] +[RUNNING] [..] -C opt-level=3 [..] +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] `[..]target/release/deps/foo-[..][EXE]` +[RUNNING] `[..]target/release/deps/test-[..][EXE]` +[DOCTEST] foo +[RUNNING] `rustdoc [..]--test [..]lib.rs[..]`", + ) + .with_stdout_contains_n("test test ... ok", 2) + .with_stdout_contains("running 0 tests") + .run(); +} + +#[cargo_test] +fn cargo_test_overflow_checks() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + + [[bin]] + name = "foo" + + [profile.release] + overflow-checks = true + "#, + ) + .file( + "src/foo.rs", + r#" + use std::panic; + pub fn main() { + let r = panic::catch_unwind(|| { + [1, i32::MAX].iter().sum::(); + }); + assert!(r.is_err()); + } + "#, + ) + .build(); + + p.cargo("build --release").run(); + assert!(p.release_bin("foo").is_file()); + + p.process(&p.release_bin("foo")).with_stdout("").run(); +} + +#[cargo_test] +fn cargo_test_quiet_with_harness() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [[test]] + name = "foo" + path = "src/foo.rs" + harness = true + "#, + ) + .file( + "src/foo.rs", + r#" + fn main() {} + #[test] fn test_hello() {} + "#, + ) + .build(); + + p.cargo("test -q") + .with_stdout( + " +running 1 test +. +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] + +", + ) + .with_stderr("") + .run(); +} + +#[cargo_test] +fn cargo_test_quiet_no_harness() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [[bin]] + name = "foo" + test = false + + [[test]] + name = "foo" + path = "src/main.rs" + harness = false + "#, + ) + .file( + "src/main.rs", + r#" + fn main() {} + #[test] fn test_hello() {} + "#, + ) + .build(); + + p.cargo("test -q").with_stdout("").with_stderr("").run(); +} + +#[cargo_test] +fn cargo_doc_test_quiet() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#, + ) + .file( + "src/lib.rs", + r#" + /// ``` + /// let result = foo::add(2, 3); + /// assert_eq!(result, 5); + /// ``` + pub fn add(a: i32, b: i32) -> i32 { + a + b + } + + /// ``` + /// let result = foo::div(10, 2); + /// assert_eq!(result, 5); + /// ``` + /// + /// # Panics + /// + /// The function panics if the second argument is zero. + /// + /// ```rust,should_panic + /// // panics on division by zero + /// foo::div(10, 0); + /// ``` + pub fn div(a: i32, b: i32) -> i32 { + if b == 0 { + panic!("Divide-by-zero error"); + } + + a / b + } + + #[test] fn test_hello() {} + "#, + ) + .build(); + + p.cargo("test -q") + .with_stdout( + " +running 1 test +. +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] + + +running 3 tests +... +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] + +", + ) + .with_stderr("") + .run(); +} + +#[cargo_test] +fn cargo_test_verbose() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + fn main() {} + #[test] fn test_hello() {} + "#, + ) + .build(); + + p.cargo("test -v hello") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc [..] src/main.rs [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[CWD]/target/debug/deps/foo-[..] hello` +", + ) + .with_stdout_contains("test test_hello ... ok") + .run(); +} + +#[cargo_test] +fn many_similar_names() { + let p = project() + .file( + "src/lib.rs", + " + pub fn foo() {} + #[test] fn lib_test() {} + ", + ) + .file( + "src/main.rs", + " + extern crate foo; + fn main() {} + #[test] fn bin_test() { foo::foo() } + ", + ) + .file( + "tests/foo.rs", + r#" + extern crate foo; + #[test] fn test_test() { foo::foo() } + "#, + ) + .build(); + + p.cargo("test -v") + .with_stdout_contains("test bin_test ... ok") + .with_stdout_contains("test lib_test ... ok") + .with_stdout_contains("test test_test ... ok") + .run(); +} + +#[cargo_test] +fn cargo_test_failing_test_in_bin() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + + #[test] + fn test_hello() { + assert_eq!(hello(), "nope") + } + "#, + ) + .build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("hello\n").run(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[ERROR] test failed, to rerun pass `--bin foo`", + ) + .with_stdout_contains( + " +running 1 test +test test_hello ... FAILED + +failures: + +---- test_hello stdout ---- +[..]thread '[..]' panicked at 'assertion failed:[..]", + ) + .with_stdout_contains("[..]`(left == right)`[..]") + .with_stdout_contains("[..]left: `\"hello\"`,[..]") + .with_stdout_contains("[..]right: `\"nope\"`[..]") + .with_stdout_contains("[..]src/main.rs:12[..]") + .with_stdout_contains( + "\ +failures: + test_hello +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn cargo_test_failing_test_in_test() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", r#"pub fn main() { println!("hello"); }"#) + .file( + "tests/footest.rs", + "#[test] fn test_hello() { assert!(false) }", + ) + .build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + + p.process(&p.bin("foo")).with_stdout("hello\n").run(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[RUNNING] [..] (target/debug/deps/footest-[..][EXE]) +[ERROR] test failed, to rerun pass `--test footest`", + ) + .with_stdout_contains("running 0 tests") + .with_stdout_contains( + "\ +running 1 test +test test_hello ... FAILED + +failures: + +---- test_hello stdout ---- +[..]thread '[..]' panicked at 'assertion failed: false', \ + tests/footest.rs:1[..] +", + ) + .with_stdout_contains( + "\ +failures: + test_hello +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn cargo_test_failing_test_in_lib() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "#[test] fn test_hello() { assert!(false) }") + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[ERROR] test failed, to rerun pass `--lib`", + ) + .with_stdout_contains( + "\ +test test_hello ... FAILED + +failures: + +---- test_hello stdout ---- +[..]thread '[..]' panicked at 'assertion failed: false', \ + src/lib.rs:1[..] +", + ) + .with_stdout_contains( + "\ +failures: + test_hello +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn test_with_lib_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name = "baz" + path = "src/main.rs" + "#, + ) + .file( + "src/lib.rs", + r#" + /// + /// ```rust + /// extern crate foo; + /// fn main() { + /// println!("{:?}", foo::foo()); + /// } + /// ``` + /// + pub fn foo(){} + #[test] fn lib_test() {} + "#, + ) + .file( + "src/main.rs", + " + #[allow(unused_extern_crates)] + extern crate foo; + + fn main() {} + + #[test] + fn bin_test() {} + ", + ) + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[RUNNING] [..] (target/debug/deps/baz-[..][EXE]) +[DOCTEST] foo", + ) + .with_stdout_contains("test lib_test ... ok") + .with_stdout_contains("test bin_test ... ok") + .with_stdout_contains_n("test [..] ... ok", 3) + .run(); +} + +#[cargo_test] +fn test_with_deep_lib_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#, + ) + .file( + "src/lib.rs", + " + #[cfg(test)] + extern crate bar; + /// ``` + /// foo::foo(); + /// ``` + pub fn foo() {} + + #[test] + fn bar_test() { + bar::bar(); + } + ", + ) + .build(); + let _p2 = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("src/lib.rs", "pub fn bar() {} #[test] fn foo_test() {}") + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([..]) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target[..]) +[DOCTEST] foo", + ) + .with_stdout_contains("test bar_test ... ok") + .with_stdout_contains_n("test [..] ... ok", 2) + .run(); +} + +#[cargo_test] +fn external_test_explicit() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[test]] + name = "test" + path = "src/test.rs" + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn get_hello() -> &'static str { "Hello" } + + #[test] + fn internal_test() {} + "#, + ) + .file( + "src/test.rs", + r#" + extern crate foo; + + #[test] + fn external_test() { assert_eq!(foo::get_hello(), "Hello") } + "#, + ) + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[RUNNING] [..] (target/debug/deps/test-[..][EXE]) +[DOCTEST] foo", + ) + .with_stdout_contains("test internal_test ... ok") + .with_stdout_contains("test external_test ... ok") + .with_stdout_contains("running 0 tests") + .run(); +} + +#[cargo_test] +fn external_test_named_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[test]] + name = "test" + "#, + ) + .file("src/lib.rs", "") + .file("tests/test.rs", "#[test] fn foo() {}") + .build(); + + p.cargo("test").run(); +} + +#[cargo_test] +fn external_test_implicit() { + let p = project() + .file( + "src/lib.rs", + r#" + pub fn get_hello() -> &'static str { "Hello" } + + #[test] + fn internal_test() {} + "#, + ) + .file( + "tests/external.rs", + r#" + extern crate foo; + + #[test] + fn external_test() { assert_eq!(foo::get_hello(), "Hello") } + "#, + ) + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[RUNNING] [..] (target/debug/deps/external-[..][EXE]) +[DOCTEST] foo", + ) + .with_stdout_contains("test internal_test ... ok") + .with_stdout_contains("test external_test ... ok") + .with_stdout_contains("running 0 tests") + .run(); +} + +#[cargo_test] +fn dont_run_examples() { + let p = project() + .file("src/lib.rs", "") + .file( + "examples/dont-run-me-i-will-fail.rs", + r#" + fn main() { panic!("Examples should not be run by 'cargo test'"); } + "#, + ) + .build(); + p.cargo("test").run(); +} + +#[cargo_test] +fn pass_through_escaped() { + let p = project() + .file( + "src/lib.rs", + " + /// ```rust + /// assert!(foo::foo()); + /// ``` + pub fn foo() -> bool { + true + } + + /// ```rust + /// assert!(!foo::bar()); + /// ``` + pub fn bar() -> bool { + false + } + + #[test] fn test_foo() { + assert!(foo()); + } + #[test] fn test_bar() { + assert!(!bar()); + } + ", + ) + .build(); + + p.cargo("test -- bar") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[DOCTEST] foo +", + ) + .with_stdout_contains("running 1 test") + .with_stdout_contains("test test_bar ... ok") + .run(); + + p.cargo("test -- foo") + .with_stderr( + "\ +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[DOCTEST] foo +", + ) + .with_stdout_contains("running 1 test") + .with_stdout_contains("test test_foo ... ok") + .run(); + + p.cargo("test -- foo bar") + .with_stderr( + "\ +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[DOCTEST] foo +", + ) + .with_stdout_contains("running 2 tests") + .with_stdout_contains("test test_foo ... ok") + .with_stdout_contains("test test_bar ... ok") + .run(); +} + +// Unlike `pass_through_escaped`, doctests won't run when using `testname` as an optimization +#[cargo_test] +fn pass_through_testname() { + let p = project() + .file( + "src/lib.rs", + " + /// ```rust + /// assert!(foo::foo()); + /// ``` + pub fn foo() -> bool { + true + } + + /// ```rust + /// assert!(!foo::bar()); + /// ``` + pub fn bar() -> bool { + false + } + + #[test] fn test_foo() { + assert!(foo()); + } + #[test] fn test_bar() { + assert!(!bar()); + } + ", + ) + .build(); + + p.cargo("test bar") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +", + ) + .with_stdout_contains("running 1 test") + .with_stdout_contains("test test_bar ... ok") + .run(); + + p.cargo("test foo") + .with_stderr( + "\ +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +", + ) + .with_stdout_contains("running 1 test") + .with_stdout_contains("test test_foo ... ok") + .run(); + + p.cargo("test foo -- bar") + .with_stderr( + "\ +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +", + ) + .with_stdout_contains("running 2 tests") + .with_stdout_contains("test test_foo ... ok") + .with_stdout_contains("test test_bar ... ok") + .run(); +} + +// Regression test for running cargo-test twice with +// tests in an rlib +#[cargo_test] +fn cargo_test_twice() { + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/foo.rs", + r#" + #![crate_type = "rlib"] + + #[test] + fn dummy_test() { } + "#, + ) + .build(); + + for _ in 0..2 { + p.cargo("test").run(); + } +} + +#[cargo_test] +fn lib_bin_same_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo" + [[bin]] + name = "foo" + "#, + ) + .file("src/lib.rs", "#[test] fn lib_test() {}") + .file( + "src/main.rs", + " + #[allow(unused_extern_crates)] + extern crate foo; + + #[test] + fn bin_test() {} + ", + ) + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[DOCTEST] foo", + ) + .with_stdout_contains_n("test [..] ... ok", 2) + .with_stdout_contains("running 0 tests") + .run(); +} + +#[cargo_test] +fn lib_with_standard_name() { + let p = project() + .file("Cargo.toml", &basic_manifest("syntax", "0.0.1")) + .file( + "src/lib.rs", + " + /// ``` + /// syntax::foo(); + /// ``` + pub fn foo() {} + + #[test] + fn foo_test() {} + ", + ) + .file( + "tests/test.rs", + " + extern crate syntax; + + #[test] + fn test() { syntax::foo() } + ", + ) + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] syntax v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/syntax-[..][EXE]) +[RUNNING] [..] (target/debug/deps/test-[..][EXE]) +[DOCTEST] syntax", + ) + .with_stdout_contains("test foo_test ... ok") + .with_stdout_contains("test test ... ok") + .with_stdout_contains_n("test [..] ... ok", 3) + .run(); +} + +#[cargo_test] +fn lib_with_standard_name2() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "syntax" + version = "0.0.1" + authors = [] + + [lib] + name = "syntax" + test = false + doctest = false + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file( + "src/main.rs", + " + extern crate syntax; + + fn main() {} + + #[test] + fn test() { syntax::foo() } + ", + ) + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] syntax v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/syntax-[..][EXE])", + ) + .with_stdout_contains("test test ... ok") + .run(); +} + +#[cargo_test] +fn lib_without_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "syntax" + version = "0.0.1" + authors = [] + + [lib] + test = false + doctest = false + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file( + "src/main.rs", + " + extern crate syntax; + + fn main() {} + + #[test] + fn test() { syntax::foo() } + ", + ) + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] syntax v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/syntax-[..][EXE])", + ) + .with_stdout_contains("test test ... ok") + .run(); +} + +#[cargo_test] +fn bin_without_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "syntax" + version = "0.0.1" + authors = [] + + [lib] + test = false + doctest = false + + [[bin]] + path = "src/main.rs" + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file( + "src/main.rs", + " + extern crate syntax; + + fn main() {} + + #[test] + fn test() { syntax::foo() } + ", + ) + .build(); + + p.cargo("test") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + binary target bin.name is required", + ) + .run(); +} + +#[cargo_test] +fn bench_without_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "syntax" + version = "0.0.1" + authors = [] + + [lib] + test = false + doctest = false + + [[bench]] + path = "src/bench.rs" + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file( + "src/main.rs", + " + extern crate syntax; + + fn main() {} + + #[test] + fn test() { syntax::foo() } + ", + ) + .file( + "src/bench.rs", + " + #![feature(test)] + extern crate syntax; + extern crate test; + + #[bench] + fn external_bench(_b: &mut test::Bencher) {} + ", + ) + .build(); + + p.cargo("test") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + benchmark target bench.name is required", + ) + .run(); +} + +#[cargo_test] +fn test_without_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "syntax" + version = "0.0.1" + authors = [] + + [lib] + test = false + doctest = false + + [[test]] + path = "src/test.rs" + "#, + ) + .file( + "src/lib.rs", + r#" + pub fn foo() {} + pub fn get_hello() -> &'static str { "Hello" } + "#, + ) + .file( + "src/main.rs", + " + extern crate syntax; + + fn main() {} + + #[test] + fn test() { syntax::foo() } + ", + ) + .file( + "src/test.rs", + r#" + extern crate syntax; + + #[test] + fn external_test() { assert_eq!(syntax::get_hello(), "Hello") } + "#, + ) + .build(); + + p.cargo("test") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + test target test.name is required", + ) + .run(); +} + +#[cargo_test] +fn example_without_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "syntax" + version = "0.0.1" + authors = [] + + [lib] + test = false + doctest = false + + [[example]] + path = "examples/example.rs" + "#, + ) + .file("src/lib.rs", "pub fn foo() {}") + .file( + "src/main.rs", + " + extern crate syntax; + + fn main() {} + + #[test] + fn test() { syntax::foo() } + ", + ) + .file( + "examples/example.rs", + r#" + extern crate syntax; + + fn main() { + println!("example1"); + } + "#, + ) + .build(); + + p.cargo("test") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to parse manifest at `[..]` + +Caused by: + example target example.name is required", + ) + .run(); +} + +#[cargo_test] +fn bin_there_for_integration() { + let p = project() + .file( + "src/main.rs", + " + fn main() { std::process::exit(101); } + #[test] fn main_test() {} + ", + ) + .file( + "tests/foo.rs", + r#" + use std::process::Command; + #[test] + fn test_test() { + let status = Command::new("target/debug/foo").status().unwrap(); + assert_eq!(status.code(), Some(101)); + } + "#, + ) + .build(); + + p.cargo("test -v") + .with_stdout_contains("test main_test ... ok") + .with_stdout_contains("test test_test ... ok") + .run(); +} + +#[cargo_test] +fn test_dylib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo" + crate_type = ["dylib"] + + [dependencies.bar] + path = "bar" + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate bar as the_bar; + + pub fn bar() { the_bar::baz(); } + + #[test] + fn foo() { bar(); } + "#, + ) + .file( + "tests/test.rs", + r#" + extern crate foo as the_foo; + + #[test] + fn foo() { the_foo::bar(); } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + name = "bar" + crate_type = ["dylib"] + "#, + ) + .file("bar/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] bar v0.0.1 ([CWD]/bar) +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[RUNNING] [..] (target/debug/deps/test-[..][EXE])", + ) + .with_stdout_contains_n("test foo ... ok", 2) + .run(); + + p.root().move_into_the_past(); + p.cargo("test") + .with_stderr( + "\ +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[RUNNING] [..] (target/debug/deps/test-[..][EXE])", + ) + .with_stdout_contains_n("test foo ... ok", 2) + .run(); +} + +#[cargo_test] +fn test_twice_with_build_cmd() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "#[test] fn foo() {}") + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[DOCTEST] foo", + ) + .with_stdout_contains("test foo ... ok") + .with_stdout_contains("running 0 tests") + .run(); + + p.cargo("test") + .with_stderr( + "\ +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[DOCTEST] foo", + ) + .with_stdout_contains("test foo ... ok") + .with_stdout_contains("running 0 tests") + .run(); +} + +#[cargo_test] +fn test_then_build() { + let p = project().file("src/lib.rs", "#[test] fn foo() {}").build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE]) +[DOCTEST] foo", + ) + .with_stdout_contains("test foo ... ok") + .with_stdout_contains("running 0 tests") + .run(); + + p.cargo("build").with_stdout("").run(); +} + +#[cargo_test] +fn test_no_run() { + let p = project() + .file("src/lib.rs", "#[test] fn foo() { panic!() }") + .build(); + + p.cargo("test --no-run") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[..][EXE]) +", + ) + .run(); +} + +#[cargo_test] +fn test_no_run_emit_json() { + let p = project() + .file("src/lib.rs", "#[test] fn foo() { panic!() }") + .build(); + + p.cargo("test --no-run --message-format json") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn test_run_specific_bin_target() { + let prj = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name="bin1" + path="src/bin1.rs" + + [[bin]] + name="bin2" + path="src/bin2.rs" + "#, + ) + .file("src/bin1.rs", "#[test] fn test1() { }") + .file("src/bin2.rs", "#[test] fn test2() { }") + .build(); + + prj.cargo("test --bin bin2") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/bin2-[..][EXE])", + ) + .with_stdout_contains("test test2 ... ok") + .run(); +} + +#[cargo_test] +fn test_run_implicit_bin_target() { + let prj = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name="mybin" + path="src/mybin.rs" + "#, + ) + .file( + "src/mybin.rs", + "#[test] fn test_in_bin() { } + fn main() { panic!(\"Don't execute me!\"); }", + ) + .file("tests/mytest.rs", "#[test] fn test_in_test() { }") + .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") + .file( + "examples/myexm.rs", + "#[test] fn test_in_exm() { } + fn main() { panic!(\"Don't execute me!\"); }", + ) + .build(); + + prj.cargo("test --bins") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/mybin-[..][EXE])", + ) + .with_stdout_contains("test test_in_bin ... ok") + .run(); +} + +#[cargo_test] +fn test_run_specific_test_target() { + let prj = project() + .file("src/bin/a.rs", "fn main() { }") + .file("src/bin/b.rs", "#[test] fn test_b() { } fn main() { }") + .file("tests/a.rs", "#[test] fn test_a() { }") + .file("tests/b.rs", "#[test] fn test_b() { }") + .build(); + + prj.cargo("test --test b") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/b-[..][EXE])", + ) + .with_stdout_contains("test test_b ... ok") + .run(); +} + +#[cargo_test] +fn test_run_implicit_test_target() { + let prj = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name="mybin" + path="src/mybin.rs" + "#, + ) + .file( + "src/mybin.rs", + "#[test] fn test_in_bin() { } + fn main() { panic!(\"Don't execute me!\"); }", + ) + .file("tests/mytest.rs", "#[test] fn test_in_test() { }") + .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") + .file( + "examples/myexm.rs", + "fn main() { compile_error!(\"Don't build me!\"); }", + ) + .build(); + + prj.cargo("test --tests") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/mybin-[..][EXE]) +[RUNNING] [..] (target/debug/deps/mytest-[..][EXE])", + ) + .with_stdout_contains("test test_in_test ... ok") + .run(); +} + +#[cargo_test] +fn test_run_implicit_bench_target() { + let prj = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name="mybin" + path="src/mybin.rs" + "#, + ) + .file( + "src/mybin.rs", + "#[test] fn test_in_bin() { } + fn main() { panic!(\"Don't execute me!\"); }", + ) + .file("tests/mytest.rs", "#[test] fn test_in_test() { }") + .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") + .file( + "examples/myexm.rs", + "fn main() { compile_error!(\"Don't build me!\"); }", + ) + .build(); + + prj.cargo("test --benches") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/mybin-[..][EXE]) +[RUNNING] [..] (target/debug/deps/mybench-[..][EXE])", + ) + .with_stdout_contains("test test_in_bench ... ok") + .run(); +} + +#[cargo_test] +fn test_run_implicit_example_target() { + let prj = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name = "mybin" + path = "src/mybin.rs" + + [[example]] + name = "myexm1" + + [[example]] + name = "myexm2" + test = true + "#, + ) + .file( + "src/mybin.rs", + "#[test] fn test_in_bin() { } + fn main() { panic!(\"Don't execute me!\"); }", + ) + .file("tests/mytest.rs", "#[test] fn test_in_test() { }") + .file("benches/mybench.rs", "#[test] fn test_in_bench() { }") + .file( + "examples/myexm1.rs", + "#[test] fn test_in_exm() { } + fn main() { panic!(\"Don't execute me!\"); }", + ) + .file( + "examples/myexm2.rs", + "#[test] fn test_in_exm() { } + fn main() { panic!(\"Don't execute me!\"); }", + ) + .build(); + + // Compiles myexm1 as normal, but does not run it. + prj.cargo("test -v") + .with_stderr_contains("[RUNNING] `rustc [..]myexm1.rs [..]--crate-type bin[..]") + .with_stderr_contains("[RUNNING] `rustc [..]myexm2.rs [..]--test[..]") + .with_stderr_does_not_contain("[RUNNING] [..]myexm1-[..]") + .with_stderr_contains("[RUNNING] [..]target/debug/examples/myexm2-[..]") + .run(); + + // Only tests myexm2. + prj.cargo("test --tests") + .with_stderr_does_not_contain("[RUNNING] [..]myexm1-[..]") + .with_stderr_contains("[RUNNING] [..]target/debug/examples/myexm2-[..]") + .run(); + + // Tests all examples. + prj.cargo("test --examples") + .with_stderr_contains("[RUNNING] [..]target/debug/examples/myexm1-[..]") + .with_stderr_contains("[RUNNING] [..]target/debug/examples/myexm2-[..]") + .run(); + + // Test an example, even without `test` set. + prj.cargo("test --example myexm1") + .with_stderr_contains("[RUNNING] [..]target/debug/examples/myexm1-[..]") + .run(); + + // Tests all examples. + prj.cargo("test --all-targets") + .with_stderr_contains("[RUNNING] [..]target/debug/examples/myexm1-[..]") + .with_stderr_contains("[RUNNING] [..]target/debug/examples/myexm2-[..]") + .run(); +} + +#[cargo_test] +fn test_filtered_excludes_compiling_examples() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name = "mybin" + test = false + "#, + ) + .file( + "src/lib.rs", + "#[cfg(test)] mod tests { #[test] fn test_in_lib() { } }", + ) + .file( + "src/bin/mybin.rs", + "#[test] fn test_in_bin() { } + fn main() { panic!(\"Don't execute me!\"); }", + ) + .file("tests/mytest.rs", "#[test] fn test_in_test() { }") + .file( + "benches/mybench.rs", + "#[test] fn test_in_bench() { assert!(false) }", + ) + .file( + "examples/myexm1.rs", + "#[test] fn test_in_exm() { assert!(false) } + fn main() { panic!(\"Don't execute me!\"); }", + ) + .build(); + + p.cargo("test -v test_in_") + .with_stdout( + " +running 1 test +test tests::test_in_lib ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] + + +running 1 test +test test_in_test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] + +", + ) + .with_stderr_unordered( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo src/lib.rs [..] --crate-type lib [..]` +[RUNNING] `rustc --crate-name foo src/lib.rs [..] --test [..]` +[RUNNING] `rustc --crate-name mybin src/bin/mybin.rs [..] --crate-type bin [..]` +[RUNNING] `rustc --crate-name mytest tests/mytest.rs [..] --test [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[CWD]/target/debug/deps/foo-[..] test_in_` +[RUNNING] `[CWD]/target/debug/deps/mytest-[..] test_in_` +", + ) + .with_stderr_does_not_contain("[RUNNING][..]rustc[..]myexm1[..]") + .with_stderr_does_not_contain("[RUNNING][..]deps/mybin-[..] test_in_") + .run(); +} + +#[cargo_test] +fn test_no_harness() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[bin]] + name = "foo" + test = false + + [[test]] + name = "bar" + path = "foo.rs" + harness = false + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("foo.rs", "fn main() {}") + .build(); + + p.cargo("test -- --nocapture") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/bar-[..][EXE]) +", + ) + .run(); +} + +#[cargo_test] +fn selective_testing() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.d1] + path = "d1" + [dependencies.d2] + path = "d2" + + [lib] + name = "foo" + doctest = false + "#, + ) + .file("src/lib.rs", "") + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [lib] + name = "d1" + doctest = false + "#, + ) + .file("d1/src/lib.rs", "") + .file( + "d1/src/main.rs", + "#[allow(unused_extern_crates)] extern crate d1; fn main() {}", + ) + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.1" + authors = [] + + [lib] + name = "d2" + doctest = false + "#, + ) + .file("d2/src/lib.rs", "") + .file( + "d2/src/main.rs", + "#[allow(unused_extern_crates)] extern crate d2; fn main() {}", + ); + let p = p.build(); + + println!("d1"); + p.cargo("test -p d1") + .with_stderr( + "\ +[COMPILING] d1 v0.0.1 ([CWD]/d1) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/d1-[..][EXE]) +[RUNNING] [..] (target/debug/deps/d1-[..][EXE])", + ) + .with_stdout_contains_n("running 0 tests", 2) + .run(); + + println!("d2"); + p.cargo("test -p d2") + .with_stderr( + "\ +[COMPILING] d2 v0.0.1 ([CWD]/d2) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/d2-[..][EXE]) +[RUNNING] [..] (target/debug/deps/d2-[..][EXE])", + ) + .with_stdout_contains_n("running 0 tests", 2) + .run(); + + println!("whole"); + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..][EXE])", + ) + .with_stdout_contains("running 0 tests") + .run(); +} + +#[cargo_test] +fn almost_cyclic_but_not_quite() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dev-dependencies.b] + path = "b" + [dev-dependencies.c] + path = "c" + "#, + ) + .file( + "src/lib.rs", + r#" + #[cfg(test)] extern crate b; + #[cfg(test)] extern crate c; + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.0.1" + authors = [] + + [dependencies.foo] + path = ".." + "#, + ) + .file( + "b/src/lib.rs", + r#" + #[allow(unused_extern_crates)] + extern crate foo; + "#, + ) + .file("c/Cargo.toml", &basic_manifest("c", "0.0.1")) + .file("c/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.cargo("test").run(); +} + +#[cargo_test] +fn build_then_selective_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.b] + path = "b" + "#, + ) + .file( + "src/lib.rs", + "#[allow(unused_extern_crates)] extern crate b;", + ) + .file( + "src/main.rs", + r#" + #[allow(unused_extern_crates)] + extern crate b; + #[allow(unused_extern_crates)] + extern crate foo; + fn main() {} + "#, + ) + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("build").run(); + p.root().move_into_the_past(); + p.cargo("test -p b").run(); +} + +#[cargo_test] +fn example_dev_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dev-dependencies.bar] + path = "bar" + "#, + ) + .file("src/lib.rs", "") + .file("examples/e1.rs", "extern crate bar; fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file( + "bar/src/lib.rs", + r#" + // make sure this file takes awhile to compile + macro_rules! f0( () => (1) ); + macro_rules! f1( () => ({(f0!()) + (f0!())}) ); + macro_rules! f2( () => ({(f1!()) + (f1!())}) ); + macro_rules! f3( () => ({(f2!()) + (f2!())}) ); + macro_rules! f4( () => ({(f3!()) + (f3!())}) ); + macro_rules! f5( () => ({(f4!()) + (f4!())}) ); + macro_rules! f6( () => ({(f5!()) + (f5!())}) ); + macro_rules! f7( () => ({(f6!()) + (f6!())}) ); + macro_rules! f8( () => ({(f7!()) + (f7!())}) ); + pub fn bar() { + f8!(); + } + "#, + ) + .build(); + p.cargo("test").run(); + p.cargo("run --example e1 --release -v").run(); +} + +#[cargo_test] +fn selective_testing_with_docs() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.d1] + path = "d1" + "#, + ) + .file( + "src/lib.rs", + r#" + /// ``` + /// not valid rust + /// ``` + pub fn foo() {} + "#, + ) + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [lib] + name = "d1" + path = "d1.rs" + "#, + ) + .file("d1/d1.rs", ""); + let p = p.build(); + + p.cargo("test -p d1") + .with_stderr( + "\ +[COMPILING] d1 v0.0.1 ([CWD]/d1) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/d1[..][EXE]) +[DOCTEST] d1", + ) + .with_stdout_contains_n("running 0 tests", 2) + .run(); +} + +#[cargo_test] +fn example_bin_same_name() { + let p = project() + .file("src/bin/foo.rs", r#"fn main() { println!("bin"); }"#) + .file("examples/foo.rs", r#"fn main() { println!("example"); }"#) + .build(); + + p.cargo("test --no-run -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc [..]` +[RUNNING] `rustc [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` +", + ) + .run(); + + assert!(!p.bin("foo").is_file()); + assert!(p.bin("examples/foo").is_file()); + + p.process(&p.bin("examples/foo")) + .with_stdout("example\n") + .run(); + + p.cargo("run") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..]", + ) + .with_stdout("bin") + .run(); + assert!(p.bin("foo").is_file()); +} + +#[cargo_test] +fn test_with_example_twice() { + let p = project() + .file("src/bin/foo.rs", r#"fn main() { println!("bin"); }"#) + .file("examples/foo.rs", r#"fn main() { println!("example"); }"#) + .build(); + + println!("first"); + p.cargo("test -v").run(); + assert!(p.bin("examples/foo").is_file()); + println!("second"); + p.cargo("test -v").run(); + assert!(p.bin("examples/foo").is_file()); +} + +#[cargo_test] +fn example_with_dev_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo" + test = false + doctest = false + + [dev-dependencies.a] + path = "a" + "#, + ) + .file("src/lib.rs", "") + .file( + "examples/ex.rs", + "#[allow(unused_extern_crates)] extern crate a; fn main() {}", + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("test -v") + .with_stderr( + "\ +[..] +[..] +[..] +[..] +[RUNNING] `rustc --crate-name ex [..] --extern a=[..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn bin_is_preserved() { + let p = project() + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v").run(); + assert!(p.bin("foo").is_file()); + + println!("test"); + p.cargo("test -v").run(); + assert!(p.bin("foo").is_file()); +} + +#[cargo_test] +fn bad_example() { + let p = project().file("src/lib.rs", ""); + let p = p.build(); + + p.cargo("run --example foo") + .with_status(101) + .with_stderr( + "\ +[ERROR] no example target named `foo`. + +", + ) + .run(); + p.cargo("run --bin foo") + .with_status(101) + .with_stderr( + "\ +[ERROR] no bin target named `foo`. + +", + ) + .run(); +} + +#[cargo_test] +fn doctest_feature() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + [features] + bar = [] + "#, + ) + .file( + "src/lib.rs", + r#" + /// ```rust + /// assert_eq!(foo::foo(), 1); + /// ``` + #[cfg(feature = "bar")] + pub fn foo() -> i32 { 1 } + "#, + ) + .build(); + + p.cargo("test --features bar") + .with_stderr( + "\ +[COMPILING] foo [..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo[..][EXE]) +[DOCTEST] foo", + ) + .with_stdout_contains("running 0 tests") + .with_stdout_contains("test [..] ... ok") + .run(); +} + +#[cargo_test] +fn dashes_to_underscores() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo-bar", "0.0.1")) + .file( + "src/lib.rs", + r#" + /// ``` + /// assert_eq!(foo_bar::foo(), 1); + /// ``` + pub fn foo() -> i32 { 1 } + "#, + ) + .build(); + + p.cargo("test -v").run(); +} + +#[cargo_test] +fn doctest_dev_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dev-dependencies] + b = { path = "b" } + "#, + ) + .file( + "src/lib.rs", + r#" + /// ``` + /// extern crate b; + /// ``` + pub fn foo() {} + "#, + ) + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("test -v").run(); +} + +#[cargo_test] +fn filter_no_doc_tests() { + let p = project() + .file( + "src/lib.rs", + r#" + /// ``` + /// extern crate b; + /// ``` + pub fn foo() {} + "#, + ) + .file("tests/foo.rs", "") + .build(); + + p.cargo("test --test=foo") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo[..][EXE])", + ) + .with_stdout_contains("running 0 tests") + .run(); +} + +#[cargo_test] +fn dylib_doctest() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo" + crate-type = ["rlib", "dylib"] + test = false + "#, + ) + .file( + "src/lib.rs", + r#" + /// ``` + /// foo::foo(); + /// ``` + pub fn foo() {} + "#, + ) + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[DOCTEST] foo", + ) + .with_stdout_contains("test [..] ... ok") + .run(); +} + +#[cargo_test] +fn dylib_doctest2() { + // Can't doc-test dylibs, as they're statically linked together. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo" + crate-type = ["dylib"] + test = false + "#, + ) + .file( + "src/lib.rs", + r#" + /// ``` + /// foo::foo(); + /// ``` + pub fn foo() {} + "#, + ) + .build(); + + p.cargo("test").with_stdout("").run(); +} + +#[cargo_test] +fn cyclic_dev_dep_doc_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dev-dependencies] + bar = { path = "bar" } + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! extern crate bar; + //! ``` + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + foo = { path = ".." } + "#, + ) + .file( + "bar/src/lib.rs", + r#" + #[allow(unused_extern_crates)] + extern crate foo; + "#, + ) + .build(); + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[COMPILING] bar v0.0.1 ([..]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo[..][EXE]) +[DOCTEST] foo", + ) + .with_stdout_contains("running 0 tests") + .with_stdout_contains("test [..] ... ok") + .run(); +} + +#[cargo_test] +fn dev_dep_with_build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dev-dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("examples/foo.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("bar/src/lib.rs", "") + .file("bar/build.rs", "fn main() {}") + .build(); + p.cargo("test").run(); +} + +#[cargo_test] +fn no_fail_fast() { + let p = project() + .file( + "src/lib.rs", + r#" + pub fn add_one(x: i32) -> i32{ + x + 1 + } + + /// ```rust + /// use foo::sub_one; + /// assert_eq!(sub_one(101), 100); + /// ``` + pub fn sub_one(x: i32) -> i32{ + x - 1 + } + "#, + ) + .file( + "tests/test_add_one.rs", + r#" + extern crate foo; + use foo::*; + + #[test] + fn add_one_test() { + assert_eq!(add_one(1), 2); + } + + #[test] + fn fail_add_one_test() { + assert_eq!(add_one(1), 1); + } + "#, + ) + .file( + "tests/test_sub_one.rs", + r#" + extern crate foo; + use foo::*; + + #[test] + fn sub_one_test() { + assert_eq!(sub_one(1), 0); + } + "#, + ) + .build(); + p.cargo("test --no-fail-fast") + .with_status(101) + .with_stderr( + "\ +[COMPILING] foo v0.0.1 [..] +[FINISHED] test [..] +[RUNNING] unittests src/lib.rs (target/debug/deps/foo[..]) +[RUNNING] tests/test_add_one.rs (target/debug/deps/test_add_one[..]) +[ERROR] test failed, to rerun pass `--test test_add_one` +[RUNNING] tests/test_sub_one.rs (target/debug/deps/test_sub_one[..]) +[DOCTEST] foo +[ERROR] 1 target failed: + `--test test_add_one` +", + ) + .with_stdout_contains("running 0 tests") + .with_stdout_contains("test result: FAILED. [..]") + .with_stdout_contains("test sub_one_test ... ok") + .with_stdout_contains_n("test [..] ... ok", 3) + .run(); +} + +#[cargo_test] +fn test_multiple_packages() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.d1] + path = "d1" + [dependencies.d2] + path = "d2" + + [lib] + name = "foo" + doctest = false + "#, + ) + .file("src/lib.rs", "") + .file( + "d1/Cargo.toml", + r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [lib] + name = "d1" + doctest = false + "#, + ) + .file("d1/src/lib.rs", "") + .file( + "d2/Cargo.toml", + r#" + [package] + name = "d2" + version = "0.0.1" + authors = [] + + [lib] + name = "d2" + doctest = false + "#, + ) + .file("d2/src/lib.rs", ""); + let p = p.build(); + + p.cargo("test -p d1 -p d2") + .with_stderr_contains("[RUNNING] [..] (target/debug/deps/d1-[..][EXE])") + .with_stderr_contains("[RUNNING] [..] (target/debug/deps/d2-[..][EXE])") + .with_stdout_contains_n("running 0 tests", 2) + .run(); +} + +#[cargo_test] +fn bin_does_not_rebuild_tests() { + let p = project() + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .file("tests/foo.rs", ""); + let p = p.build(); + + p.cargo("test -v").run(); + + sleep_ms(1000); + fs::write(p.root().join("src/main.rs"), "fn main() { 3; }").unwrap(); + + p.cargo("test -v --no-run") + .with_stderr( + "\ +[DIRTY] foo v0.0.1 ([..]): the file `src/main.rs` has changed ([..]) +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] src/main.rs [..]` +[RUNNING] `rustc [..] src/main.rs [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` +[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` +[EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn selective_test_wonky_profile() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.release] + opt-level = 2 + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", ""); + let p = p.build(); + + p.cargo("test -v --no-run --release -p foo -p a").run(); +} + +#[cargo_test] +fn selective_test_optional_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a", optional = true } + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", ""); + let p = p.build(); + + p.cargo("test -v --no-run --features a -p a") + .with_stderr( + "\ +[COMPILING] a v0.0.1 ([..]) +[RUNNING] `rustc [..] a/src/lib.rs [..]` +[RUNNING] `rustc [..] a/src/lib.rs [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[EXECUTABLE] `[..]/target/debug/deps/a-[..][EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn only_test_docs() { + let p = project() + .file( + "src/lib.rs", + r#" + #[test] + fn foo() { + let a: u32 = "hello"; + } + + /// ``` + /// foo::bar(); + /// println!("ok"); + /// ``` + pub fn bar() { + } + "#, + ) + .file("tests/foo.rs", "this is not rust"); + let p = p.build(); + + p.cargo("test --doc") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[DOCTEST] foo", + ) + .with_stdout_contains("test [..] ... ok") + .run(); +} + +#[cargo_test] +fn test_panic_abort_with_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar" } + + [profile.dev] + panic = 'abort' + "#, + ) + .file( + "src/lib.rs", + r#" + extern crate bar; + + #[test] + fn foo() {} + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + p.cargo("test -v").run(); +} + +#[cargo_test] +fn cfg_test_even_with_no_harness() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + harness = false + doctest = false + "#, + ) + .file( + "src/lib.rs", + r#"#[cfg(test)] fn main() { println!("hello!"); }"#, + ) + .build(); + p.cargo("test -v") + .with_stdout("hello!\n") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..]` +", + ) + .run(); +} + +#[cargo_test] +fn panic_abort_multiple() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + + [profile.release] + panic = 'abort' + "#, + ) + .file( + "src/lib.rs", + "#[allow(unused_extern_crates)] extern crate a;", + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "") + .build(); + p.cargo("test --release -v -p foo -p a").run(); +} + +#[cargo_test] +fn pass_correct_cfgs_flags_to_rustdoc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [features] + default = ["feature_a/default"] + nightly = ["feature_a/nightly"] + + [dependencies.feature_a] + path = "libs/feature_a" + default-features = false + "#, + ) + .file( + "src/lib.rs", + r#" + #[cfg(test)] + mod tests { + #[test] + fn it_works() { + assert!(true); + } + } + "#, + ) + .file( + "libs/feature_a/Cargo.toml", + r#" + [package] + name = "feature_a" + version = "0.1.0" + authors = [] + + [features] + default = ["mock_serde_codegen"] + nightly = ["mock_serde_derive"] + + [dependencies] + mock_serde_derive = { path = "../mock_serde_derive", optional = true } + + [build-dependencies] + mock_serde_codegen = { path = "../mock_serde_codegen", optional = true } + "#, + ) + .file( + "libs/feature_a/src/lib.rs", + r#" + #[cfg(feature = "mock_serde_derive")] + const MSG: &'static str = "This is safe"; + + #[cfg(feature = "mock_serde_codegen")] + const MSG: &'static str = "This is risky"; + + pub fn get() -> &'static str { + MSG + } + "#, + ) + .file( + "libs/mock_serde_derive/Cargo.toml", + &basic_manifest("mock_serde_derive", "0.1.0"), + ) + .file("libs/mock_serde_derive/src/lib.rs", "") + .file( + "libs/mock_serde_codegen/Cargo.toml", + &basic_manifest("mock_serde_codegen", "0.1.0"), + ) + .file("libs/mock_serde_codegen/src/lib.rs", ""); + let p = p.build(); + + p.cargo("test --package feature_a --verbose") + .with_stderr_contains( + "\ +[DOCTEST] feature_a +[RUNNING] `rustdoc [..]--test [..]mock_serde_codegen[..]`", + ) + .run(); + + p.cargo("test --verbose") + .with_stderr_contains( + "\ +[DOCTEST] foo +[RUNNING] `rustdoc [..]--test [..]feature_a[..]`", + ) + .run(); +} + +#[cargo_test] +fn test_release_ignore_panic() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + + [profile.test] + panic = 'abort' + [profile.release] + panic = 'abort' + "#, + ) + .file( + "src/lib.rs", + "#[allow(unused_extern_crates)] extern crate a;", + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", ""); + let p = p.build(); + println!("test"); + p.cargo("test -v").run(); + println!("bench"); + p.cargo("bench -v").run(); +} + +#[cargo_test] +fn test_many_with_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + + [features] + foo = [] + + [workspace] + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("test -v -p a -p foo --features foo").run(); +} + +#[cargo_test] +fn test_all_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#, + ) + .file("src/main.rs", "#[test] fn foo_test() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[test] fn bar_test() {}") + .build(); + + p.cargo("test --workspace") + .with_stdout_contains("test foo_test ... ok") + .with_stdout_contains("test bar_test ... ok") + .run(); +} + +#[cargo_test] +fn test_all_exclude() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[test] pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "#[test] pub fn baz() { assert!(false); }") + .build(); + + p.cargo("test --workspace --exclude baz") + .with_stdout_contains( + "running 1 test +test bar ... ok", + ) + .run(); +} + +#[cargo_test] +fn test_all_exclude_not_found() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[test] pub fn bar() {}") + .build(); + + p.cargo("test --workspace --exclude baz") + .with_stderr_contains("[WARNING] excluded package(s) `baz` not found in workspace [..]") + .with_stdout_contains( + "running 1 test +test bar ... ok", + ) + .run(); +} + +#[cargo_test] +fn test_all_exclude_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[test] pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "#[test] pub fn baz() { assert!(false); }") + .build(); + + p.cargo("test --workspace --exclude '*z'") + .with_stdout_contains( + "running 1 test +test bar ... ok", + ) + .run(); +} + +#[cargo_test] +fn test_all_exclude_glob_not_found() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[test] pub fn bar() {}") + .build(); + + p.cargo("test --workspace --exclude '*z'") + .with_stderr_contains( + "[WARNING] excluded package pattern(s) `*z` not found in workspace [..]", + ) + .with_stdout_contains( + "running 1 test +test bar ... ok", + ) + .run(); +} + +#[cargo_test] +fn test_all_exclude_broken_glob() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + p.cargo("test --workspace --exclude '[*z'") + .with_status(101) + .with_stderr_contains("[ERROR] cannot build glob pattern from `[*z`") + .run(); +} + +#[cargo_test] +fn test_all_virtual_manifest() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/lib.rs", "#[test] fn a() {}") + .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) + .file("b/src/lib.rs", "#[test] fn b() {}") + .build(); + + p.cargo("test --workspace") + .with_stdout_contains("running 1 test\ntest a ... ok") + .with_stdout_contains("running 1 test\ntest b ... ok") + .run(); +} + +#[cargo_test] +fn test_virtual_manifest_all_implied() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/lib.rs", "#[test] fn a() {}") + .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) + .file("b/src/lib.rs", "#[test] fn b() {}") + .build(); + + p.cargo("test") + .with_stdout_contains("running 1 test\ntest a ... ok") + .with_stdout_contains("running 1 test\ntest b ... ok") + .run(); +} + +#[cargo_test] +fn test_virtual_manifest_one_project() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[test] fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "#[test] fn baz() { assert!(false); }") + .build(); + + p.cargo("test -p bar") + .with_stdout_contains("running 1 test\ntest bar ... ok") + .with_stdout_does_not_contain("running 1 test\ntest baz ... ok") + .run(); +} + +#[cargo_test] +fn test_virtual_manifest_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[test] fn bar() { assert!(false); }") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "#[test] fn baz() {}") + .build(); + + p.cargo("test -p '*z'") + .with_stdout_does_not_contain("running 1 test\ntest bar ... ok") + .with_stdout_contains("running 1 test\ntest baz ... ok") + .run(); +} + +#[cargo_test] +fn test_virtual_manifest_glob_not_found() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[test] fn bar() {}") + .build(); + + p.cargo("test -p bar -p '*z'") + .with_status(101) + .with_stderr("[ERROR] package pattern(s) `*z` not found in workspace [..]") + .run(); +} + +#[cargo_test] +fn test_virtual_manifest_broken_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "#[test] fn bar() {}") + .build(); + + p.cargo("test -p '[*z'") + .with_status(101) + .with_stderr_contains("[ERROR] cannot build glob pattern from `[*z`") + .run(); +} + +#[cargo_test] +fn test_all_member_dependency_same_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + a = "0.1.0" + "#, + ) + .file("a/src/lib.rs", "#[test] fn a() {}") + .build(); + + Package::new("a", "0.1.0").publish(); + + p.cargo("test --workspace") + .with_stdout_contains("test a ... ok") + .run(); +} + +#[cargo_test] +fn doctest_only_with_dev_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dev-dependencies] + b = { path = "b" } + "#, + ) + .file( + "src/lib.rs", + r#" + /// ``` + /// extern crate b; + /// + /// b::b(); + /// ``` + pub fn a() {} + "#, + ) + .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) + .file("b/src/lib.rs", "pub fn b() {}") + .build(); + + p.cargo("test --doc -v").run(); +} + +#[cargo_test] +fn test_many_targets() { + let p = project() + .file( + "src/bin/a.rs", + r#" + fn main() {} + #[test] fn bin_a() {} + "#, + ) + .file( + "src/bin/b.rs", + r#" + fn main() {} + #[test] fn bin_b() {} + "#, + ) + .file( + "src/bin/c.rs", + r#" + fn main() {} + #[test] fn bin_c() { panic!(); } + "#, + ) + .file( + "examples/a.rs", + r#" + fn main() {} + #[test] fn example_a() {} + "#, + ) + .file( + "examples/b.rs", + r#" + fn main() {} + #[test] fn example_b() {} + "#, + ) + .file("examples/c.rs", "#[test] fn example_c() { panic!(); }") + .file("tests/a.rs", "#[test] fn test_a() {}") + .file("tests/b.rs", "#[test] fn test_b() {}") + .file("tests/c.rs", "does not compile") + .build(); + + p.cargo("test --verbose --bin a --bin b --example a --example b --test a --test b") + .with_stdout_contains("test bin_a ... ok") + .with_stdout_contains("test bin_b ... ok") + .with_stdout_contains("test test_a ... ok") + .with_stdout_contains("test test_b ... ok") + .with_stderr_contains("[RUNNING] `rustc --crate-name a examples/a.rs [..]`") + .with_stderr_contains("[RUNNING] `rustc --crate-name b examples/b.rs [..]`") + .run(); +} + +#[cargo_test] +fn doctest_and_registry() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + b = { path = "b" } + c = { path = "c" } + + [workspace] + "#, + ) + .file("src/lib.rs", "") + .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) + .file( + "b/src/lib.rs", + " + /// ``` + /// b::foo(); + /// ``` + pub fn foo() {} + ", + ) + .file( + "c/Cargo.toml", + r#" + [package] + name = "c" + version = "0.1.0" + + [dependencies] + b = "0.1" + "#, + ) + .file("c/src/lib.rs", "") + .build(); + + Package::new("b", "0.1.0").publish(); + + p.cargo("test --workspace -v").run(); +} + +#[cargo_test] +fn cargo_test_env() { + let src = format!( + r#" + #![crate_type = "rlib"] + + #[test] + fn env_test() {{ + use std::env; + eprintln!("{{}}", env::var("{}").unwrap()); + }} + "#, + cargo::CARGO_ENV + ); + + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", &src) + .build(); + + let cargo = cargo_exe().canonicalize().unwrap(); + p.cargo("test --lib -- --nocapture") + .with_stderr_contains(cargo.to_str().unwrap()) + .with_stdout_contains("test env_test ... ok") + .run(); + + // Check that `cargo test` propagates the environment's $CARGO + let rustc = cargo_util::paths::resolve_executable("rustc".as_ref()) + .unwrap() + .canonicalize() + .unwrap(); + let rustc = rustc.to_str().unwrap(); + p.cargo("test --lib -- --nocapture") + // we use rustc since $CARGO is only used if it points to a path that exists + .env(cargo::CARGO_ENV, rustc) + .with_stderr_contains(rustc) + .with_stdout_contains("test env_test ... ok") + .run(); +} + +#[cargo_test] +fn test_order() { + let p = project() + .file("src/lib.rs", "#[test] fn test_lib() {}") + .file("tests/a.rs", "#[test] fn test_a() {}") + .file("tests/z.rs", "#[test] fn test_z() {}") + .build(); + + p.cargo("test --workspace") + .with_stdout_contains( + " +running 1 test +test test_lib ... ok + +test result: ok. [..] + + +running 1 test +test test_a ... ok + +test result: ok. [..] + + +running 1 test +test test_z ... ok + +test result: ok. [..] +", + ) + .run(); +} + +#[cargo_test] +fn cyclic_dev() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dev-dependencies] + foo = { path = "." } + "#, + ) + .file("src/lib.rs", "#[test] fn test_lib() {}") + .file("tests/foo.rs", "extern crate foo;") + .build(); + + p.cargo("test --workspace").run(); +} + +#[cargo_test] +fn publish_a_crate_without_tests() { + Package::new("testless", "0.1.0") + .file( + "Cargo.toml", + r#" + [package] + name = "testless" + version = "0.1.0" + exclude = ["tests/*"] + + [[test]] + name = "a_test" + "#, + ) + .file("src/lib.rs", "") + // In real life, the package will have a test, + // which would be excluded from .crate file by the + // `exclude` field. Our test harness does not honor + // exclude though, so let's just not add the file! + // .file("tests/a_test.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + testless = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("test").run(); + p.cargo("test --package testless").run(); +} + +#[cargo_test] +fn find_dependency_of_proc_macro_dependency_with_target() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["root", "proc_macro_dep"] + "#, + ) + .file( + "root/Cargo.toml", + r#" + [package] + name = "root" + version = "0.1.0" + authors = [] + + [dependencies] + proc_macro_dep = { path = "../proc_macro_dep" } + "#, + ) + .file( + "root/src/lib.rs", + r#" + #[macro_use] + extern crate proc_macro_dep; + + #[derive(Noop)] + pub struct X; + "#, + ) + .file( + "proc_macro_dep/Cargo.toml", + r#" + [package] + name = "proc_macro_dep" + version = "0.1.0" + authors = [] + + [lib] + proc-macro = true + + [dependencies] + baz = "^0.1" + "#, + ) + .file( + "proc_macro_dep/src/lib.rs", + r#" + extern crate baz; + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro_derive(Noop)] + pub fn noop(_input: TokenStream) -> TokenStream { + "".parse().unwrap() + } + "#, + ) + .build(); + Package::new("bar", "0.1.0").publish(); + Package::new("baz", "0.1.0") + .dep("bar", "0.1") + .file("src/lib.rs", "extern crate bar;") + .publish(); + p.cargo("test --workspace --target").arg(rustc_host()).run(); +} + +#[cargo_test] +fn test_hint_not_masked_by_doctest() { + let p = project() + .file( + "src/lib.rs", + r#" + /// ``` + /// assert_eq!(1, 1); + /// ``` + pub fn this_works() {} + "#, + ) + .file( + "tests/integ.rs", + r#" + #[test] + fn this_fails() { + panic!(); + } + "#, + ) + .build(); + p.cargo("test --no-fail-fast") + .with_status(101) + .with_stdout_contains("test this_fails ... FAILED") + .with_stdout_contains("[..]this_works (line [..]ok") + .with_stderr_contains("[ERROR] test failed, to rerun pass `--test integ`") + .run(); +} + +#[cargo_test] +fn test_hint_workspace_virtual() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b", "c"] + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/lib.rs", "#[test] fn t1() {}") + .file("b/Cargo.toml", &basic_manifest("b", "0.1.0")) + .file("b/src/lib.rs", "#[test] fn t1() {assert!(false)}") + .file("c/Cargo.toml", &basic_manifest("c", "0.1.0")) + .file( + "c/src/lib.rs", + r#" + /// ```rust + /// assert_eq!(1, 2); + /// ``` + pub fn foo() {} + "#, + ) + .file( + "c/src/main.rs", + r#" + fn main() {} + + #[test] + fn from_main() { assert_eq!(1, 2); } + "#, + ) + .file( + "c/tests/t1.rs", + r#" + #[test] + fn from_int_test() { assert_eq!(1, 2); } + "#, + ) + .file( + "c/examples/ex1.rs", + r#" + fn main() {} + + #[test] + fn from_example() { assert_eq!(1, 2); } + "#, + ) + // This does not use #[bench] since it is unstable. #[test] works just + // the same for our purpose of checking the hint. + .file( + "c/benches/b1.rs", + r#" + #[test] + fn from_bench() { assert_eq!(1, 2); } + "#, + ) + .build(); + + // This depends on Units being sorted so that `b` fails first. + p.cargo("test") + .with_stderr_unordered( + "\ +[COMPILING] c v0.1.0 [..] +[COMPILING] a v0.1.0 [..] +[COMPILING] b v0.1.0 [..] +[FINISHED] test [..] +[RUNNING] unittests src/lib.rs (target/debug/deps/a[..]) +[RUNNING] unittests src/lib.rs (target/debug/deps/b[..]) +[ERROR] test failed, to rerun pass `-p b --lib` +", + ) + .with_status(101) + .run(); + p.cargo("test") + .cwd("b") + .with_stderr( + "\ +[FINISHED] test [..] +[RUNNING] unittests src/lib.rs ([ROOT]/foo/target/debug/deps/b[..]) +[ERROR] test failed, to rerun pass `--lib` +", + ) + .with_status(101) + .run(); + p.cargo("test --no-fail-fast") + .with_stderr( + "\ +[FINISHED] test [..] +[RUNNING] unittests src/lib.rs (target/debug/deps/a[..]) +[RUNNING] unittests src/lib.rs (target/debug/deps/b[..]) +[ERROR] test failed, to rerun pass `-p b --lib` +[RUNNING] unittests src/lib.rs (target/debug/deps/c[..]) +[RUNNING] unittests src/main.rs (target/debug/deps/c[..]) +[ERROR] test failed, to rerun pass `-p c --bin c` +[RUNNING] tests/t1.rs (target/debug/deps/t1[..]) +[ERROR] test failed, to rerun pass `-p c --test t1` +[DOCTEST] a +[DOCTEST] b +[DOCTEST] c +[ERROR] doctest failed, to rerun pass `-p c --doc` +[ERROR] 4 targets failed: + `-p b --lib` + `-p c --bin c` + `-p c --test t1` + `-p c --doc` +", + ) + .with_status(101) + .run(); + // Check others that are not in the default set. + p.cargo("test -p c --examples --benches --no-fail-fast") + .with_stderr( + "\ +[COMPILING] c v0.1.0 [..] +[FINISHED] test [..] +[RUNNING] unittests src/lib.rs (target/debug/deps/c[..]) +[RUNNING] unittests src/main.rs (target/debug/deps/c[..]) +[ERROR] test failed, to rerun pass `-p c --bin c` +[RUNNING] benches/b1.rs (target/debug/deps/b1[..]) +[ERROR] test failed, to rerun pass `-p c --bench b1` +[RUNNING] unittests examples/ex1.rs (target/debug/examples/ex1[..]) +[ERROR] test failed, to rerun pass `-p c --example ex1` +[ERROR] 3 targets failed: + `-p c --bin c` + `-p c --bench b1` + `-p c --example ex1` +", + ) + .with_status(101) + .run() +} + +#[cargo_test] +fn test_hint_workspace_nonvirtual() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["a"] + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/lib.rs", "#[test] fn t1() {assert!(false)}") + .build(); + + p.cargo("test --workspace") + .with_stderr_contains("[ERROR] test failed, to rerun pass `-p a --lib`") + .with_status(101) + .run(); + p.cargo("test -p a") + .with_stderr_contains("[ERROR] test failed, to rerun pass `-p a --lib`") + .with_status(101) + .run(); +} + +#[cargo_test] +fn json_artifact_includes_test_flag() { + // Verify that the JSON artifact output includes `test` flag. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.test] + opt-level = 1 + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("test --lib -v --message-format=json") + .with_json( + r#" + { + "reason":"compiler-artifact", + "profile": { + "debug_assertions": true, + "debuginfo": 2, + "opt_level": "1", + "overflow_checks": true, + "test": true + }, + "executable": "[..]/foo-[..]", + "features": [], + "package_id":"foo 0.0.1 ([..])", + "manifest_path": "[..]", + "target":{ + "kind":["lib"], + "crate_types":["lib"], + "doc": true, + "doctest": true, + "edition": "2015", + "name":"foo", + "src_path":"[..]lib.rs", + "test": true + }, + "filenames":"{...}", + "fresh": false + } + + {"reason": "build-finished", "success": true} + "#, + ) + .run(); +} + +#[cargo_test] +fn json_artifact_includes_executable_for_library_tests() { + let p = project() + .file("src/main.rs", "fn main() { }") + .file("src/lib.rs", r#"#[test] fn lib_test() {}"#) + .build(); + + p.cargo("test --lib -v --no-run --message-format=json") + .with_json( + r#" + { + "executable": "[..]/foo/target/debug/deps/foo-[..][EXE]", + "features": [], + "filenames": "{...}", + "fresh": false, + "package_id": "foo 0.0.1 ([..])", + "manifest_path": "[..]", + "profile": "{...}", + "reason": "compiler-artifact", + "target": { + "crate_types": [ "lib" ], + "kind": [ "lib" ], + "doc": true, + "doctest": true, + "edition": "2015", + "name": "foo", + "src_path": "[..]/foo/src/lib.rs", + "test": true + } + } + + {"reason": "build-finished", "success": true} + "#, + ) + .run(); +} + +#[cargo_test] +fn json_artifact_includes_executable_for_integration_tests() { + let p = project() + .file( + "tests/integration_test.rs", + r#"#[test] fn integration_test() {}"#, + ) + .build(); + + p.cargo("test -v --no-run --message-format=json --test integration_test") + .with_json( + r#" + { + "executable": "[..]/foo/target/debug/deps/integration_test-[..][EXE]", + "features": [], + "filenames": "{...}", + "fresh": false, + "package_id": "foo 0.0.1 ([..])", + "manifest_path": "[..]", + "profile": "{...}", + "reason": "compiler-artifact", + "target": { + "crate_types": [ "bin" ], + "kind": [ "test" ], + "doc": false, + "doctest": false, + "edition": "2015", + "name": "integration_test", + "src_path": "[..]/foo/tests/integration_test.rs", + "test": true + } + } + + {"reason": "build-finished", "success": true} + "#, + ) + .run(); +} + +#[cargo_test] +fn test_build_script_links() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + links = 'something' + + [lib] + test = false + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .build(); + + p.cargo("test --no-run").run(); +} + +#[cargo_test] +fn doctest_skip_staticlib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [lib] + crate-type = ["staticlib"] + "#, + ) + .file( + "src/lib.rs", + r#" + //! ``` + //! assert_eq!(1,2); + //! ``` + "#, + ) + .build(); + + p.cargo("test --doc") + .with_status(101) + .with_stderr( + "\ +[WARNING] doc tests are not supported for crate type(s) `staticlib` in package `foo` +[ERROR] no library targets found in package `foo`", + ) + .run(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo [..] +[FINISHED] test [..] +[RUNNING] [..] (target/debug/deps/foo-[..])", + ) + .run(); +} + +#[cargo_test] +fn can_not_mix_doc_tests_and_regular_tests() { + let p = project() + .file( + "src/lib.rs", + "\ +/// ``` +/// assert_eq!(1, 1) +/// ``` +pub fn foo() -> u8 { 1 } + +#[cfg(test)] mod tests { + #[test] fn it_works() { assert_eq!(2 + 2, 4); } +} +", + ) + .build(); + + p.cargo("test") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..]) +[DOCTEST] foo +", + ) + .with_stdout( + " +running 1 test +test tests::it_works ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] + + +running 1 test +test src/lib.rs - foo (line 1) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] +\n", + ) + .run(); + + p.cargo("test --lib") + .with_stderr( + "\ +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] [..] (target/debug/deps/foo-[..])\n", + ) + .with_stdout( + " +running 1 test +test tests::it_works ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] +\n", + ) + .run(); + + // This has been modified to attempt to diagnose spurious errors on CI. + // For some reason, this is recompiling the lib when it shouldn't. If the + // root cause is ever found, the changes here should be reverted. + // See https://github.com/rust-lang/cargo/issues/6887 + p.cargo("test --doc -vv") + .with_stderr_does_not_contain("[COMPILING] foo [..]") + .with_stderr_contains("[DOCTEST] foo") + .with_stdout( + " +running 1 test +test src/lib.rs - foo (line 1) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] + +", + ) + .env("CARGO_LOG", "cargo=trace") + .run(); + + p.cargo("test --lib --doc") + .with_status(101) + .with_stderr("[ERROR] Can't mix --doc with other target selecting options\n") + .run(); +} + +#[cargo_test] +fn can_not_no_run_doc_tests() { + let p = project() + .file( + "src/lib.rs", + r#" + /// ``` + /// let _x = 1 + "foo"; + /// ``` + pub fn foo() -> u8 { 1 } + "#, + ) + .build(); + + p.cargo("test --doc --no-run") + .with_status(101) + .with_stderr("[ERROR] Can't skip running doc tests with --no-run") + .run(); +} + +#[cargo_test] +fn test_all_targets_lib() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("test --all-targets") + .with_stderr( + "\ +[COMPILING] foo [..] +[FINISHED] test [..] +[RUNNING] [..]foo[..] +", + ) + .run(); +} + +#[cargo_test] +fn test_dep_with_dev() { + Package::new("devdep", "0.1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + + [dev-dependencies] + devdep = "0.1" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("test -p bar") + .with_status(101) + .with_stderr( + "[ERROR] package `bar` cannot be tested because it requires dev-dependencies \ + and is not a member of the workspace", + ) + .run(); +} + +#[cargo_test(nightly, reason = "-Zdoctest-xcompile is unstable")] +fn cargo_test_doctest_xcompile_ignores() { + // -Zdoctest-xcompile also enables --enable-per-target-ignores which + // allows the ignore-TARGET syntax. + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/lib.rs", + r#" + ///```ignore-x86_64 + ///assert!(cfg!(not(target_arch = "x86_64"))); + ///``` + pub fn foo() -> u8 { + 4 + } + "#, + ) + .build(); + + p.cargo("build").run(); + #[cfg(not(target_arch = "x86_64"))] + p.cargo("test") + .with_stdout_contains( + "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]", + ) + .run(); + #[cfg(target_arch = "x86_64")] + p.cargo("test") + .with_status(101) + .with_stdout_contains( + "test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out[..]", + ) + .run(); + + #[cfg(not(target_arch = "x86_64"))] + p.cargo("test -Zdoctest-xcompile") + .masquerade_as_nightly_cargo(&["doctest-xcompile"]) + .with_stdout_contains( + "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]", + ) + .run(); + + #[cfg(target_arch = "x86_64")] + p.cargo("test -Zdoctest-xcompile") + .masquerade_as_nightly_cargo(&["doctest-xcompile"]) + .with_stdout_contains( + "test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out[..]", + ) + .run(); +} + +#[cargo_test(nightly, reason = "-Zdoctest-xcompile is unstable")] +fn cargo_test_doctest_xcompile() { + if !cross_compile::can_run_on_host() { + return; + } + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/lib.rs", + r#" + + ///``` + ///assert!(1 == 1); + ///``` + pub fn foo() -> u8 { + 4 + } + "#, + ) + .build(); + + p.cargo("build").run(); + p.cargo(&format!("test --target {}", cross_compile::alternate())) + .with_stdout_contains("running 0 tests") + .run(); + p.cargo(&format!( + "test --target {} -Zdoctest-xcompile", + cross_compile::alternate() + )) + .masquerade_as_nightly_cargo(&["doctest-xcompile"]) + .with_stdout_contains( + "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]", + ) + .run(); +} + +#[cargo_test(nightly, reason = "-Zdoctest-xcompile is unstable")] +fn cargo_test_doctest_xcompile_runner() { + if !cross_compile::can_run_on_host() { + return; + } + + let runner = project() + .file("Cargo.toml", &basic_bin_manifest("runner")) + .file( + "src/main.rs", + r#" + pub fn main() { + eprintln!("this is a runner"); + let args: Vec = std::env::args().collect(); + std::process::Command::new(&args[1]).spawn(); + } + "#, + ) + .build(); + + runner.cargo("build").run(); + assert!(runner.bin("runner").is_file()); + let runner_path = paths::root().join("runner"); + fs::copy(&runner.bin("runner"), &runner_path).unwrap(); + + let config = paths::root().join(".cargo/config"); + + fs::create_dir_all(config.parent().unwrap()).unwrap(); + // Escape Windows backslashes for TOML config. + let runner_str = runner_path.to_str().unwrap().replace('\\', "\\\\"); + fs::write( + config, + format!( + r#" + [target.'cfg(target_arch = "{}")'] + runner = "{}" + "#, + cross_compile::alternate_arch(), + runner_str + ), + ) + .unwrap(); + + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/lib.rs", + &format!( + r#" + ///``` + ///assert!(cfg!(target_arch = "{}")); + ///``` + pub fn foo() -> u8 {{ + 4 + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + p.cargo("build").run(); + p.cargo(&format!("test --target {}", cross_compile::alternate())) + .with_stdout_contains("running 0 tests") + .run(); + p.cargo(&format!( + "test --target {} -Zdoctest-xcompile", + cross_compile::alternate() + )) + .masquerade_as_nightly_cargo(&["doctest-xcompile"]) + .with_stdout_contains( + "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]", + ) + .with_stderr_contains("this is a runner") + .run(); +} + +#[cargo_test(nightly, reason = "-Zdoctest-xcompile is unstable")] +fn cargo_test_doctest_xcompile_no_runner() { + if !cross_compile::can_run_on_host() { + return; + } + + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/lib.rs", + &format!( + r#" + ///``` + ///assert!(cfg!(target_arch = "{}")); + ///``` + pub fn foo() -> u8 {{ + 4 + }} + "#, + cross_compile::alternate_arch() + ), + ) + .build(); + + p.cargo("build").run(); + p.cargo(&format!("test --target {}", cross_compile::alternate())) + .with_stdout_contains("running 0 tests") + .run(); + p.cargo(&format!( + "test --target {} -Zdoctest-xcompile", + cross_compile::alternate() + )) + .masquerade_as_nightly_cargo(&["doctest-xcompile"]) + .with_stdout_contains( + "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]", + ) + .run(); +} + +#[cargo_test(nightly, reason = "-Zpanic-abort-tests in rustc is unstable")] +fn panic_abort_tests() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + + [dependencies] + a = { path = 'a' } + + [profile.dev] + panic = 'abort' + [profile.test] + panic = 'abort' + "#, + ) + .file( + "src/lib.rs", + r#" + #[test] + fn foo() { + a::foo(); + } + "#, + ) + .file("a/Cargo.toml", &basic_lib_manifest("a")) + .file("a/src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("test -Z panic-abort-tests -v") + .with_stderr_contains("[..]--crate-name a [..]-C panic=abort[..]") + .with_stderr_contains("[..]--crate-name foo [..]-C panic=abort[..]") + .with_stderr_contains("[..]--crate-name foo [..]-C panic=abort[..]--test[..]") + .masquerade_as_nightly_cargo(&["panic-abort-tests"]) + .run(); +} + +#[cargo_test(nightly, reason = "-Zpanic-abort-tests in rustc is unstable")] +fn panic_abort_only_test() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + + [dependencies] + a = { path = 'a' } + + [profile.test] + panic = 'abort' + "#, + ) + .file( + "src/lib.rs", + r#" + #[test] + fn foo() { + a::foo(); + } + "#, + ) + .file("a/Cargo.toml", &basic_lib_manifest("a")) + .file("a/src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("test -Z panic-abort-tests -v") + .with_stderr_contains("warning: `panic` setting is ignored for `test` profile") + .masquerade_as_nightly_cargo(&["panic-abort-tests"]) + .run(); +} + +#[cargo_test(nightly, reason = "-Zpanic-abort-tests in rustc is unstable")] +fn panic_abort_test_profile_inherits() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + + [dependencies] + a = { path = 'a' } + + [profile.dev] + panic = 'abort' + "#, + ) + .file( + "src/lib.rs", + r#" + #[test] + fn foo() { + a::foo(); + } + "#, + ) + .file("a/Cargo.toml", &basic_lib_manifest("a")) + .file("a/src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("test -Z panic-abort-tests -v") + .masquerade_as_nightly_cargo(&["panic-abort-tests"]) + .with_status(0) + .run(); +} + +#[cargo_test] +fn bin_env_for_test() { + // Test for the `CARGO_BIN_EXE_` environment variables for tests. + // + // Note: The Unicode binary uses a `[[bin]]` definition because different + // filesystems normalize utf-8 in different ways. For example, HFS uses + // "gru\u{308}ßen" and APFS uses "gr\u{fc}ßen". Defining it in TOML forces + // one form to be used. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [[bin]] + name = 'grüßen' + path = 'src/bin/grussen.rs' + "#, + ) + .file("src/bin/foo.rs", "fn main() {}") + .file("src/bin/with-dash.rs", "fn main() {}") + .file("src/bin/grussen.rs", "fn main() {}") + .build(); + + let bin_path = |name| p.bin(name).to_string_lossy().replace("\\", "\\\\"); + p.change_file( + "tests/check_env.rs", + &r#" + #[test] + fn run_bins() { + assert_eq!(env!("CARGO_BIN_EXE_foo"), ""); + assert_eq!(env!("CARGO_BIN_EXE_with-dash"), ""); + assert_eq!(env!("CARGO_BIN_EXE_grüßen"), ""); + } + "# + .replace("", &bin_path("foo")) + .replace("", &bin_path("with-dash")) + .replace("", &bin_path("grüßen")), + ); + + p.cargo("test --test check_env").run(); + p.cargo("check --test check_env").run(); +} + +#[cargo_test] +fn test_workspaces_cwd() { + // This tests that all the different test types are executed from the + // crate directory (manifest_dir), and not from the workspace root. + + let make_lib_file = |expected| { + format!( + r#" + //! ``` + //! assert_eq!("{expected}", std::fs::read_to_string("file.txt").unwrap()); + //! assert_eq!("{expected}", include_str!("../file.txt")); + //! assert_eq!( + //! std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR")), + //! std::env::current_dir().unwrap(), + //! ); + //! ``` + + #[test] + fn test_unit_{expected}_cwd() {{ + assert_eq!("{expected}", std::fs::read_to_string("file.txt").unwrap()); + assert_eq!("{expected}", include_str!("../file.txt")); + assert_eq!( + std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR")), + std::env::current_dir().unwrap(), + ); + }} + "#, + expected = expected + ) + }; + let make_test_file = |expected| { + format!( + r#" + #[test] + fn test_integration_{expected}_cwd() {{ + assert_eq!("{expected}", std::fs::read_to_string("file.txt").unwrap()); + assert_eq!("{expected}", include_str!("../file.txt")); + assert_eq!( + std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR")), + std::env::current_dir().unwrap(), + ); + }} + "#, + expected = expected + ) + }; + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "root-crate" + version = "0.0.0" + + [workspace] + members = [".", "nested-crate", "very/deeply/nested/deep-crate"] + "#, + ) + .file("file.txt", "root") + .file("src/lib.rs", &make_lib_file("root")) + .file("tests/integration.rs", &make_test_file("root")) + .file( + "nested-crate/Cargo.toml", + r#" + [package] + name = "nested-crate" + version = "0.0.0" + "#, + ) + .file("nested-crate/file.txt", "nested") + .file("nested-crate/src/lib.rs", &make_lib_file("nested")) + .file( + "nested-crate/tests/integration.rs", + &make_test_file("nested"), + ) + .file( + "very/deeply/nested/deep-crate/Cargo.toml", + r#" + [package] + name = "deep-crate" + version = "0.0.0" + "#, + ) + .file("very/deeply/nested/deep-crate/file.txt", "deep") + .file( + "very/deeply/nested/deep-crate/src/lib.rs", + &make_lib_file("deep"), + ) + .file( + "very/deeply/nested/deep-crate/tests/integration.rs", + &make_test_file("deep"), + ) + .build(); + + p.cargo("test --workspace --all") + .with_stderr_contains("[DOCTEST] root-crate") + .with_stderr_contains("[DOCTEST] nested-crate") + .with_stderr_contains("[DOCTEST] deep-crate") + .with_stdout_contains("test test_unit_root_cwd ... ok") + .with_stdout_contains("test test_unit_nested_cwd ... ok") + .with_stdout_contains("test test_unit_deep_cwd ... ok") + .with_stdout_contains("test test_integration_root_cwd ... ok") + .with_stdout_contains("test test_integration_nested_cwd ... ok") + .with_stdout_contains("test test_integration_deep_cwd ... ok") + .run(); + + p.cargo("test -p root-crate --all") + .with_stderr_contains("[DOCTEST] root-crate") + .with_stdout_contains("test test_unit_root_cwd ... ok") + .with_stdout_contains("test test_integration_root_cwd ... ok") + .run(); + + p.cargo("test -p nested-crate --all") + .with_stderr_contains("[DOCTEST] nested-crate") + .with_stdout_contains("test test_unit_nested_cwd ... ok") + .with_stdout_contains("test test_integration_nested_cwd ... ok") + .run(); + + p.cargo("test -p deep-crate --all") + .with_stderr_contains("[DOCTEST] deep-crate") + .with_stdout_contains("test test_unit_deep_cwd ... ok") + .with_stdout_contains("test test_integration_deep_cwd ... ok") + .run(); + + p.cargo("test --all") + .cwd("nested-crate") + .with_stderr_contains("[DOCTEST] nested-crate") + .with_stdout_contains("test test_unit_nested_cwd ... ok") + .with_stdout_contains("test test_integration_nested_cwd ... ok") + .run(); + + p.cargo("test --all") + .cwd("very/deeply/nested/deep-crate") + .with_stderr_contains("[DOCTEST] deep-crate") + .with_stdout_contains("test test_unit_deep_cwd ... ok") + .with_stdout_contains("test test_integration_deep_cwd ... ok") + .run(); +} + +#[cargo_test] +fn execution_error() { + // Checks the behavior when a test fails to launch. + let p = project() + .file( + "tests/t1.rs", + r#" + #[test] + fn foo() {} + "#, + ) + .build(); + let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env()); + p.cargo("test") + .env(&key, "does_not_exist") + // The actual error is usually "no such file", but on Windows it has a + // custom message. Since matching against the error string produced by + // Rust is not very reliable, this just uses `[..]`. + .with_stderr( + "\ +[COMPILING] foo v0.0.1 [..] +[FINISHED] test [..] +[RUNNING] tests/t1.rs (target/debug/deps/t1[..]) +error: test failed, to rerun pass `--test t1` + +Caused by: + could not execute process `does_not_exist [ROOT]/foo/target/debug/deps/t1[..]` (never executed) + +Caused by: + [..] +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn nonzero_exit_status() { + // Tests for nonzero exit codes from tests. + let p = project() + .file( + "tests/t1.rs", + r#" + #[test] + fn t() { panic!("this is a normal error") } + "#, + ) + .file( + "tests/t2.rs", + r#" + #[test] + fn t() { std::process::exit(4) } + "#, + ) + .build(); + + p.cargo("test --test t1") + .with_stderr( + "\ +[COMPILING] foo [..] +[FINISHED] test [..] +[RUNNING] tests/t1.rs (target/debug/deps/t1[..]) +error: test failed, to rerun pass `--test t1` +", + ) + .with_stdout_contains("[..]this is a normal error[..]") + .with_status(101) + .run(); + + p.cargo("test --test t2") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 [..] +[FINISHED] test [..] +[RUNNING] tests/t2.rs (target/debug/deps/t2[..]) +error: test failed, to rerun pass `--test t2` + +Caused by: + process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2[..]` (exit [..]: 4) +", + ) + .with_status(4) + .run(); + + // no-fail-fast always uses 101 + p.cargo("test --no-fail-fast") + .with_stderr( + "\ +[FINISHED] test [..] +[RUNNING] tests/t1.rs (target/debug/deps/t1[..]) +error: test failed, to rerun pass `--test t1` +[RUNNING] tests/t2.rs (target/debug/deps/t2[..]) +error: test failed, to rerun pass `--test t2` + +Caused by: + process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2[..]` (exit [..]: 4) +error: 2 targets failed: + `--test t1` + `--test t2` +", + ) + .with_status(101) + .run(); +} diff --git a/tests/testsuite/timings.rs b/tests/testsuite/timings.rs new file mode 100644 index 0000000..8f06ac6 --- /dev/null +++ b/tests/testsuite/timings.rs @@ -0,0 +1,53 @@ +//! Tests for --timings. + +use cargo_test_support::project; +use cargo_test_support::registry::Package; + +#[cargo_test] +fn timings_works() { + Package::new("dep", "0.1.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = "0.1" + "#, + ) + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .file("tests/t1.rs", "") + .file("examples/ex1.rs", "fn main() {}") + .build(); + + p.cargo("build --all-targets --timings") + .with_stderr_unordered( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] dep v0.1.0 [..] +[COMPILING] dep v0.1.0 +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] + Timing report saved to [..]/foo/target/cargo-timings/cargo-timing-[..].html +", + ) + .run(); + + p.cargo("clean").run(); + + p.cargo("test --timings").run(); + + p.cargo("clean").run(); + + p.cargo("check --timings").run(); + + p.cargo("clean").run(); + + p.cargo("doc --timings").run(); +} diff --git a/tests/testsuite/tool_paths.rs b/tests/testsuite/tool_paths.rs new file mode 100644 index 0000000..a211b53 --- /dev/null +++ b/tests/testsuite/tool_paths.rs @@ -0,0 +1,402 @@ +//! Tests for configuration values that point to programs. + +use cargo_test_support::{basic_lib_manifest, project, rustc_host, rustc_host_env}; + +#[cargo_test] +fn pathless_tools() { + let target = rustc_host(); + + let foo = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + linker = "nonexistent-linker" + "#, + target + ), + ) + .build(); + + foo.cargo("build --verbose") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc [..] -C linker=nonexistent-linker [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn absolute_tools() { + let target = rustc_host(); + + // Escaped as they appear within a TOML config file + let linker = if cfg!(windows) { + r#"C:\\bogus\\nonexistent-linker"# + } else { + r#"/bogus/nonexistent-linker"# + }; + + let foo = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file("src/lib.rs", "") + .file( + ".cargo/config", + &format!( + r#" + [target.{target}] + linker = "{linker}" + "#, + target = target, + linker = linker + ), + ) + .build(); + + foo.cargo("build --verbose") + .with_stderr( + "\ +[COMPILING] foo v0.5.0 ([CWD]) +[RUNNING] `rustc [..] -C linker=[..]bogus/nonexistent-linker [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn relative_tools() { + let target = rustc_host(); + + // Escaped as they appear within a TOML config file + let linker = if cfg!(windows) { + r#".\\tools\\nonexistent-linker"# + } else { + r#"./tools/nonexistent-linker"# + }; + + // Funky directory structure to test that relative tool paths are made absolute + // by reference to the `.cargo/..` directory and not to (for example) the CWD. + let p = project() + .no_manifest() + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .file( + ".cargo/config", + &format!( + r#" + [target.{target}] + linker = "{linker}" + "#, + target = target, + linker = linker + ), + ) + .build(); + + let prefix = p.root().into_os_string().into_string().unwrap(); + + p.cargo("build --verbose") + .cwd("bar") + .with_stderr(&format!( + "\ +[COMPILING] bar v0.5.0 ([CWD]) +[RUNNING] `rustc [..] -C linker={prefix}/./tools/nonexistent-linker [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + prefix = prefix, + )) + .run(); +} + +#[cargo_test] +fn custom_runner() { + let target = rustc_host(); + + let p = project() + .file("src/main.rs", "fn main() {}") + .file("tests/test.rs", "") + .file("benches/bench.rs", "") + .file( + ".cargo/config", + &format!( + r#" + [target.{}] + runner = "nonexistent-runner -r" + "#, + target + ), + ) + .build(); + + p.cargo("run -- --param") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param` +", + ) + .run(); + + p.cargo("test --test test --verbose -- --param") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc [..]` +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `nonexistent-runner -r [..]/target/debug/deps/test-[..][EXE] --param` +", + ) + .run(); + + p.cargo("bench --bench bench --verbose -- --param") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc [..]` +[RUNNING] `rustc [..]` +[FINISHED] bench [optimized] target(s) in [..] +[RUNNING] `nonexistent-runner -r [..]/target/release/deps/bench-[..][EXE] --param --bench` +", + ) + .run(); +} + +// can set a custom runner via `target.'cfg(..)'.runner` +#[cargo_test] +fn custom_runner_cfg() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [target.'cfg(not(target_os = "none"))'] + runner = "nonexistent-runner -r" + "#, + ) + .build(); + + p.cargo("run -- --param") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param` +", + ) + .run(); +} + +// custom runner set via `target.$triple.runner` have precedence over `target.'cfg(..)'.runner` +#[cargo_test] +fn custom_runner_cfg_precedence() { + let target = rustc_host(); + + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + &format!( + r#" + [target.'cfg(not(target_os = "none"))'] + runner = "ignored-runner" + + [target.{}] + runner = "nonexistent-runner -r" + "#, + target + ), + ) + .build(); + + p.cargo("run -- --param") + .with_status(101) + .with_stderr_contains( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param` +", + ) + .run(); +} + +#[cargo_test] +fn custom_runner_cfg_collision() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config", + r#" + [target.'cfg(not(target_arch = "avr"))'] + runner = "true" + + [target.'cfg(not(target_os = "none"))'] + runner = "false" + "#, + ) + .build(); + + p.cargo("run -- --param") + .with_status(101) + .with_stderr( + "\ +[ERROR] several matching instances of `target.'cfg(..)'.runner` in configurations +first match `cfg(not(target_arch = \"avr\"))` located in [..]/foo/.cargo/config +second match `cfg(not(target_os = \"none\"))` located in [..]/foo/.cargo/config +", + ) + .run(); +} + +#[cargo_test] +fn custom_runner_env() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env()); + + p.cargo("run") + .env(&key, "nonexistent-runner --foo") + .with_status(101) + // FIXME: Update "Caused by" error message once rust/pull/87704 is merged. + // On Windows, changing to a custom executable resolver has changed the + // error messages. + .with_stderr(&format!( + "\ +[COMPILING] foo [..] +[FINISHED] dev [..] +[RUNNING] `nonexistent-runner --foo target/debug/foo[EXE]` +[ERROR] could not execute process `nonexistent-runner --foo target/debug/foo[EXE]` (never executed) + +Caused by: + [..] +" + )) + .run(); +} + +#[cargo_test] +fn custom_runner_env_overrides_config() { + let target = rustc_host(); + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config.toml", + &format!( + r#" + [target.{}] + runner = "should-not-run -r" + "#, + target + ), + ) + .build(); + + let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env()); + + p.cargo("run") + .env(&key, "should-run --foo") + .with_status(101) + .with_stderr_contains("[RUNNING] `should-run --foo target/debug/foo[EXE]`") + .run(); +} + +#[cargo_test] +#[cfg(unix)] // Assumes `true` is in PATH. +fn custom_runner_env_true() { + // Check for a bug where "true" was interpreted as a boolean instead of + // the executable. + let p = project().file("src/main.rs", "fn main() {}").build(); + + let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env()); + + p.cargo("run") + .env(&key, "true") + .with_stderr_contains("[RUNNING] `true target/debug/foo[EXE]`") + .run(); +} + +#[cargo_test] +fn custom_linker_env() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + let key = format!("CARGO_TARGET_{}_LINKER", rustc_host_env()); + + p.cargo("build -v") + .env(&key, "nonexistent-linker") + .with_status(101) + .with_stderr_contains("[RUNNING] `rustc [..]-C linker=nonexistent-linker [..]") + .run(); +} + +#[cargo_test] +fn target_in_environment_contains_lower_case() { + let p = project().file("src/main.rs", "fn main() {}").build(); + + let target = rustc_host(); + let env_key = format!( + "CARGO_TARGET_{}_LINKER", + target.to_lowercase().replace('-', "_") + ); + + p.cargo("build -v --target") + .arg(target) + .env(&env_key, "nonexistent-linker") + .with_stderr_contains(format!( + "warning: Environment variables are expected to use uppercase \ + letters and underscores, the variable `{}` will be ignored and \ + have no effect", + env_key + )) + .run(); +} + +#[cargo_test] +fn cfg_ignored_fields() { + // Test for some ignored fields in [target.'cfg()'] tables. + let p = project() + .file( + ".cargo/config", + r#" + # Try some empty tables. + [target.'cfg(not(foo))'] + [target.'cfg(not(bar))'.somelib] + + # A bunch of unused fields. + [target.'cfg(not(target_os = "none"))'] + linker = 'false' + ar = 'false' + foo = {rustc-flags = "-l foo"} + invalid = 1 + runner = 'false' + rustflags = '' + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[WARNING] unused key `somelib` in [target] config table `cfg(not(bar))` +[WARNING] unused key `ar` in [target] config table `cfg(not(target_os = \"none\"))` +[WARNING] unused key `foo` in [target] config table `cfg(not(target_os = \"none\"))` +[WARNING] unused key `invalid` in [target] config table `cfg(not(target_os = \"none\"))` +[WARNING] unused key `linker` in [target] config table `cfg(not(target_os = \"none\"))` +[CHECKING] foo v0.0.1 ([..]) +[FINISHED] [..] +", + ) + .run(); +} diff --git a/tests/testsuite/tree.rs b/tests/testsuite/tree.rs new file mode 100644 index 0000000..c3c1ca6 --- /dev/null +++ b/tests/testsuite/tree.rs @@ -0,0 +1,2150 @@ +//! Tests for the `cargo tree` command. + +use super::features2::switch_to_resolver_2; +use cargo_test_support::cross_compile::{self, alternate}; +use cargo_test_support::registry::{Dependency, Package}; +use cargo_test_support::{basic_manifest, git, project, rustc_host, Project}; + +fn make_simple_proj() -> Project { + Package::new("c", "1.0.0").publish(); + Package::new("b", "1.0.0").dep("c", "1.0").publish(); + Package::new("a", "1.0.0").dep("b", "1.0").publish(); + Package::new("bdep", "1.0.0").dep("b", "1.0").publish(); + Package::new("devdep", "1.0.0").dep("b", "1.0.0").publish(); + + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = "1.0" + c = "1.0" + + [build-dependencies] + bdep = "1.0" + + [dev-dependencies] + devdep = "1.0" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build() +} + +#[cargo_test] +fn simple() { + // A simple test with a few different dependencies. + let p = make_simple_proj(); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── a v1.0.0 +│ └── b v1.0.0 +│ └── c v1.0.0 +└── c v1.0.0 +[build-dependencies] +└── bdep v1.0.0 + └── b v1.0.0 (*) +[dev-dependencies] +└── devdep v1.0.0 + └── b v1.0.0 (*) +", + ) + .run(); + + p.cargo("tree -p bdep") + .with_stdout( + "\ +bdep v1.0.0 +└── b v1.0.0 + └── c v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn virtual_workspace() { + // Multiple packages in a virtual workspace. + Package::new("somedep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "baz", "c"] + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "1.0.0")) + .file("a/src/lib.rs", "") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + + [dependencies] + c = { path = "../c" } + somedep = "1.0" + "#, + ) + .file("baz/src/lib.rs", "") + .file("c/Cargo.toml", &basic_manifest("c", "1.0.0")) + .file("c/src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +a v1.0.0 ([..]/foo/a) + +baz v0.1.0 ([..]/foo/baz) +├── c v1.0.0 ([..]/foo/c) +└── somedep v1.0.0 + +c v1.0.0 ([..]/foo/c) +", + ) + .run(); + + p.cargo("tree -p a").with_stdout("a v1.0.0 [..]").run(); + + p.cargo("tree") + .cwd("baz") + .with_stdout( + "\ +baz v0.1.0 ([..]/foo/baz) +├── c v1.0.0 ([..]/foo/c) +└── somedep v1.0.0 +", + ) + .run(); + + // exclude baz + p.cargo("tree --workspace --exclude baz") + .with_stdout( + "\ +a v1.0.0 ([..]/foo/a) + +c v1.0.0 ([..]/foo/c) +", + ) + .run(); + + // exclude glob '*z' + p.cargo("tree --workspace --exclude '*z'") + .with_stdout( + "\ +a v1.0.0 ([..]/foo/a) + +c v1.0.0 ([..]/foo/c) +", + ) + .run(); + + // include glob '*z' + p.cargo("tree -p '*z'") + .with_stdout( + "\ +baz v0.1.0 ([..]/foo/baz) +├── c v1.0.0 ([..]/foo/c) +└── somedep v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn dedupe_edges() { + // Works around https://github.com/rust-lang/cargo/issues/7985 + Package::new("bitflags", "1.0.0").publish(); + Package::new("manyfeat", "1.0.0") + .feature("f1", &[]) + .feature("f2", &[]) + .feature("f3", &[]) + .dep("bitflags", "1.0") + .publish(); + Package::new("a", "1.0.0") + .feature_dep("manyfeat", "1.0", &["f1"]) + .publish(); + Package::new("b", "1.0.0") + .feature_dep("manyfeat", "1.0", &["f2"]) + .publish(); + Package::new("c", "1.0.0") + .feature_dep("manyfeat", "1.0", &["f3"]) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = "1.0" + b = "1.0" + c = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── a v1.0.0 +│ └── manyfeat v1.0.0 +│ └── bitflags v1.0.0 +├── b v1.0.0 +│ └── manyfeat v1.0.0 (*) +└── c v1.0.0 + └── manyfeat v1.0.0 (*) +", + ) + .run(); +} + +#[cargo_test] +fn renamed_deps() { + // Handles renamed dependencies. + Package::new("one", "1.0.0").publish(); + Package::new("two", "1.0.0").publish(); + Package::new("bar", "1.0.0").dep("one", "1.0").publish(); + Package::new("bar", "2.0.0").dep("two", "1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [dependencies] + bar1 = {version = "1.0", package="bar"} + bar2 = {version = "2.0", package="bar"} + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +foo v1.0.0 ([..]/foo) +├── bar v1.0.0 +│ └── one v1.0.0 +└── bar v2.0.0 + └── two v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn source_kinds() { + // Handles git and path sources. + Package::new("regdep", "1.0.0").publish(); + let git_project = git::new("gitdep", |p| { + p.file("Cargo.toml", &basic_manifest("gitdep", "1.0.0")) + .file("src/lib.rs", "") + }); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + regdep = "1.0" + pathdep = {{ path = "pathdep" }} + gitdep = {{ git = "{}" }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .file("pathdep/Cargo.toml", &basic_manifest("pathdep", "1.0.0")) + .file("pathdep/src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── gitdep v1.0.0 (file://[..]/gitdep#[..]) +├── pathdep v1.0.0 ([..]/foo/pathdep) +└── regdep v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn features() { + // Exercises a variety of feature behaviors. + Package::new("optdep_default", "1.0.0").publish(); + Package::new("optdep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + optdep_default = { version = "1.0", optional = true } + optdep = { version = "1.0", optional = true } + + [features] + default = ["optdep_default"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +a v0.1.0 ([..]/foo) +└── optdep_default v1.0.0 +", + ) + .run(); + + p.cargo("tree --no-default-features") + .with_stdout( + "\ +a v0.1.0 ([..]/foo) +", + ) + .run(); + + p.cargo("tree --all-features") + .with_stdout( + "\ +a v0.1.0 ([..]/foo) +├── optdep v1.0.0 +└── optdep_default v1.0.0 +", + ) + .run(); + + p.cargo("tree --features optdep") + .with_stdout( + "\ +a v0.1.0 ([..]/foo) +├── optdep v1.0.0 +└── optdep_default v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn filters_target() { + // --target flag + if cross_compile::disabled() { + return; + } + Package::new("targetdep", "1.0.0").publish(); + Package::new("hostdep", "1.0.0").publish(); + Package::new("devdep", "1.0.0").publish(); + Package::new("build_target_dep", "1.0.0").publish(); + Package::new("build_host_dep", "1.0.0") + .target_dep("targetdep", "1.0", alternate()) + .target_dep("hostdep", "1.0", rustc_host()) + .publish(); + Package::new("pm_target", "1.0.0") + .proc_macro(true) + .publish(); + Package::new("pm_host", "1.0.0").proc_macro(true).publish(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [target.'{alt}'.dependencies] + targetdep = "1.0" + pm_target = "1.0" + + [target.'{host}'.dependencies] + hostdep = "1.0" + pm_host = "1.0" + + [target.'{alt}'.dev-dependencies] + devdep = "1.0" + + [target.'{alt}'.build-dependencies] + build_target_dep = "1.0" + + [target.'{host}'.build-dependencies] + build_host_dep = "1.0" + "#, + alt = alternate(), + host = rustc_host() + ), + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── hostdep v1.0.0 +└── pm_host v1.0.0 (proc-macro) +[build-dependencies] +└── build_host_dep v1.0.0 + └── hostdep v1.0.0 +", + ) + .run(); + + p.cargo("tree --target") + .arg(alternate()) + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── pm_target v1.0.0 (proc-macro) +└── targetdep v1.0.0 +[build-dependencies] +└── build_host_dep v1.0.0 + └── hostdep v1.0.0 +[dev-dependencies] +└── devdep v1.0.0 +", + ) + .run(); + + p.cargo("tree --target") + .arg(rustc_host()) + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── hostdep v1.0.0 +└── pm_host v1.0.0 (proc-macro) +[build-dependencies] +└── build_host_dep v1.0.0 + └── hostdep v1.0.0 +", + ) + .run(); + + p.cargo("tree --target=all") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── hostdep v1.0.0 +├── pm_host v1.0.0 (proc-macro) +├── pm_target v1.0.0 (proc-macro) +└── targetdep v1.0.0 +[build-dependencies] +├── build_host_dep v1.0.0 +│ ├── hostdep v1.0.0 +│ └── targetdep v1.0.0 +└── build_target_dep v1.0.0 +[dev-dependencies] +└── devdep v1.0.0 +", + ) + .run(); + + // no-proc-macro + p.cargo("tree --target=all -e no-proc-macro") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── hostdep v1.0.0 +└── targetdep v1.0.0 +[build-dependencies] +├── build_host_dep v1.0.0 +│ ├── hostdep v1.0.0 +│ └── targetdep v1.0.0 +└── build_target_dep v1.0.0 +[dev-dependencies] +└── devdep v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn no_selected_target_dependency() { + // --target flag + if cross_compile::disabled() { + return; + } + Package::new("targetdep", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [target.'{alt}'.dependencies] + targetdep = "1.0" + + "#, + alt = alternate(), + ), + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +", + ) + .run(); + + p.cargo("tree -i targetdep") + .with_stderr( + "\ +[WARNING] nothing to print. + +To find dependencies that require specific target platforms, \ +try to use option `--target all` first, and then narrow your search scope accordingly. +", + ) + .run(); + p.cargo("tree -i targetdep --target all") + .with_stdout( + "\ +targetdep v1.0.0 +└── foo v0.1.0 ([..]/foo) +", + ) + .run(); +} + +#[cargo_test] +fn dep_kinds() { + Package::new("inner-devdep", "1.0.0").publish(); + Package::new("inner-builddep", "1.0.0").publish(); + Package::new("inner-normal", "1.0.0").publish(); + Package::new("inner-pm", "1.0.0").proc_macro(true).publish(); + Package::new("inner-buildpm", "1.0.0") + .proc_macro(true) + .publish(); + Package::new("normaldep", "1.0.0") + .dep("inner-normal", "1.0") + .dev_dep("inner-devdep", "1.0") + .build_dep("inner-builddep", "1.0") + .publish(); + Package::new("devdep", "1.0.0") + .dep("inner-normal", "1.0") + .dep("inner-pm", "1.0") + .dev_dep("inner-devdep", "1.0") + .build_dep("inner-builddep", "1.0") + .build_dep("inner-buildpm", "1.0") + .publish(); + Package::new("builddep", "1.0.0") + .dep("inner-normal", "1.0") + .dev_dep("inner-devdep", "1.0") + .build_dep("inner-builddep", "1.0") + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + normaldep = "1.0" + + [dev-dependencies] + devdep = "1.0" + + [build-dependencies] + builddep = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── normaldep v1.0.0 + └── inner-normal v1.0.0 + [build-dependencies] + └── inner-builddep v1.0.0 +[build-dependencies] +└── builddep v1.0.0 + └── inner-normal v1.0.0 + [build-dependencies] + └── inner-builddep v1.0.0 +[dev-dependencies] +└── devdep v1.0.0 + ├── inner-normal v1.0.0 + └── inner-pm v1.0.0 (proc-macro) + [build-dependencies] + ├── inner-builddep v1.0.0 + └── inner-buildpm v1.0.0 (proc-macro) +", + ) + .run(); + + p.cargo("tree -e no-dev") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── normaldep v1.0.0 + └── inner-normal v1.0.0 + [build-dependencies] + └── inner-builddep v1.0.0 +[build-dependencies] +└── builddep v1.0.0 + └── inner-normal v1.0.0 + [build-dependencies] + └── inner-builddep v1.0.0 +", + ) + .run(); + + p.cargo("tree -e normal") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── normaldep v1.0.0 + └── inner-normal v1.0.0 +", + ) + .run(); + + p.cargo("tree -e dev,build") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +[build-dependencies] +└── builddep v1.0.0 + [build-dependencies] + └── inner-builddep v1.0.0 +[dev-dependencies] +└── devdep v1.0.0 + [build-dependencies] + ├── inner-builddep v1.0.0 + └── inner-buildpm v1.0.0 (proc-macro) +", + ) + .run(); + + p.cargo("tree -e dev,build,no-proc-macro") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +[build-dependencies] +└── builddep v1.0.0 + [build-dependencies] + └── inner-builddep v1.0.0 +[dev-dependencies] +└── devdep v1.0.0 + [build-dependencies] + └── inner-builddep v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn cyclic_dev_dep() { + // Cyclical dev-dependency and inverse flag. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dev-dependencies] + dev-dep = { path = "dev-dep" } + "#, + ) + .file("src/lib.rs", "") + .file( + "dev-dep/Cargo.toml", + r#" + [package] + name = "dev-dep" + version = "0.1.0" + + [dependencies] + foo = { path=".." } + "#, + ) + .file("dev-dep/src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +[dev-dependencies] +└── dev-dep v0.1.0 ([..]/foo/dev-dep) + └── foo v0.1.0 ([..]/foo) (*) +", + ) + .run(); + + p.cargo("tree --invert foo") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── dev-dep v0.1.0 ([..]/foo/dev-dep) + [dev-dependencies] + └── foo v0.1.0 ([..]/foo) (*) +", + ) + .run(); +} + +#[cargo_test] +fn invert() { + Package::new("b1", "1.0.0").dep("c", "1.0").publish(); + Package::new("b2", "1.0.0").dep("d", "1.0").publish(); + Package::new("c", "1.0.0").publish(); + Package::new("d", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + b1 = "1.0" + b2 = "1.0" + c = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── b1 v1.0.0 +│ └── c v1.0.0 +├── b2 v1.0.0 +│ └── d v1.0.0 +└── c v1.0.0 +", + ) + .run(); + + p.cargo("tree --invert c") + .with_stdout( + "\ +c v1.0.0 +├── b1 v1.0.0 +│ └── foo v0.1.0 ([..]/foo) +└── foo v0.1.0 ([..]/foo) +", + ) + .run(); +} + +#[cargo_test] +fn invert_with_build_dep() { + // -i for a common dependency between normal and build deps. + Package::new("common", "1.0.0").publish(); + Package::new("bdep", "1.0.0").dep("common", "1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + common = "1.0" + + [build-dependencies] + bdep = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── common v1.0.0 +[build-dependencies] +└── bdep v1.0.0 + └── common v1.0.0 +", + ) + .run(); + + p.cargo("tree -i common") + .with_stdout( + "\ +common v1.0.0 +├── bdep v1.0.0 +│ [build-dependencies] +│ └── foo v0.1.0 ([..]/foo) +└── foo v0.1.0 ([..]/foo) +", + ) + .run(); +} + +#[cargo_test] +fn no_indent() { + let p = make_simple_proj(); + + p.cargo("tree --prefix=none") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +a v1.0.0 +b v1.0.0 +c v1.0.0 +c v1.0.0 +bdep v1.0.0 +b v1.0.0 (*) +devdep v1.0.0 +b v1.0.0 (*) +", + ) + .run(); +} + +#[cargo_test] +fn prefix_depth() { + let p = make_simple_proj(); + + p.cargo("tree --prefix=depth") + .with_stdout( + "\ +0foo v0.1.0 ([..]/foo) +1a v1.0.0 +2b v1.0.0 +3c v1.0.0 +1c v1.0.0 +1bdep v1.0.0 +2b v1.0.0 (*) +1devdep v1.0.0 +2b v1.0.0 (*) +", + ) + .run(); +} + +#[cargo_test] +fn no_dedupe() { + let p = make_simple_proj(); + + p.cargo("tree --no-dedupe") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── a v1.0.0 +│ └── b v1.0.0 +│ └── c v1.0.0 +└── c v1.0.0 +[build-dependencies] +└── bdep v1.0.0 + └── b v1.0.0 + └── c v1.0.0 +[dev-dependencies] +└── devdep v1.0.0 + └── b v1.0.0 + └── c v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn no_dedupe_cycle() { + // --no-dedupe with a dependency cycle + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dev-dependencies] + bar = {path = "bar"} + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + foo = {path=".."} + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +[dev-dependencies] +└── bar v0.1.0 ([..]/foo/bar) + └── foo v0.1.0 ([..]/foo) (*) +", + ) + .run(); + + p.cargo("tree --no-dedupe") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +[dev-dependencies] +└── bar v0.1.0 ([..]/foo/bar) + └── foo v0.1.0 ([..]/foo) (*) +", + ) + .run(); +} + +#[cargo_test] +fn duplicates() { + Package::new("dog", "1.0.0").publish(); + Package::new("dog", "2.0.0").publish(); + Package::new("cat", "1.0.0").publish(); + Package::new("cat", "2.0.0").publish(); + Package::new("dep", "1.0.0") + .dep("dog", "1.0") + .dep("cat", "1.0") + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + dog1 = { version = "1.0", package = "dog" } + dog2 = { version = "2.0", package = "dog" } + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [dependencies] + dep = "1.0" + cat = "2.0" + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("tree -p a") + .with_stdout( + "\ +a v0.1.0 ([..]/foo/a) +├── dog v1.0.0 +└── dog v2.0.0 +", + ) + .run(); + + p.cargo("tree -p b") + .with_stdout( + "\ +b v0.1.0 ([..]/foo/b) +├── cat v2.0.0 +└── dep v1.0.0 + ├── cat v1.0.0 + └── dog v1.0.0 +", + ) + .run(); + + p.cargo("tree -p a -d") + .with_stdout( + "\ +dog v1.0.0 +└── a v0.1.0 ([..]/foo/a) + +dog v2.0.0 +└── a v0.1.0 ([..]/foo/a) +", + ) + .run(); + + p.cargo("tree -p b -d") + .with_stdout( + "\ +cat v1.0.0 +└── dep v1.0.0 + └── b v0.1.0 ([..]/foo/b) + +cat v2.0.0 +└── b v0.1.0 ([..]/foo/b) +", + ) + .run(); +} + +#[cargo_test] +fn duplicates_with_target() { + // --target flag + if cross_compile::disabled() { + return; + } + Package::new("a", "1.0.0").publish(); + Package::new("dog", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = "1.0" + dog = "1.0" + + [build-dependencies] + a = "1.0" + dog = "1.0" + + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + p.cargo("tree -d").with_stdout("").run(); + + p.cargo("tree -d --target") + .arg(alternate()) + .with_stdout("") + .run(); + + p.cargo("tree -d --target") + .arg(rustc_host()) + .with_stdout("") + .run(); + + p.cargo("tree -d --target=all").with_stdout("").run(); +} + +#[cargo_test] +fn charset() { + let p = make_simple_proj(); + p.cargo("tree --charset ascii") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +|-- a v1.0.0 +| `-- b v1.0.0 +| `-- c v1.0.0 +`-- c v1.0.0 +[build-dependencies] +`-- bdep v1.0.0 + `-- b v1.0.0 (*) +[dev-dependencies] +`-- devdep v1.0.0 + `-- b v1.0.0 (*) +", + ) + .run(); +} + +#[cargo_test] +fn format() { + Package::new("dep", "1.0.0").publish(); + Package::new("other-dep", "1.0.0").publish(); + + Package::new("dep_that_is_awesome", "1.0.0") + .file( + "Cargo.toml", + r#" + [package] + name = "dep_that_is_awesome" + version = "1.0.0" + + [lib] + name = "awesome_dep" + "#, + ) + .file("src/lib.rs", "pub struct Straw;") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + license = "MIT" + repository = "https://github.com/rust-lang/cargo" + + [dependencies] + dep = {version="1.0", optional=true} + other-dep = {version="1.0", optional=true} + dep_that_is_awesome = {version="1.0", optional=true} + + + [features] + default = ["foo"] + foo = ["bar"] + bar = [] + "#, + ) + .file("src/main.rs", "") + .build(); + + p.cargo("tree --format <<<{p}>>>") + .with_stdout("<<>>") + .run(); + + p.cargo("tree --format {}") + .with_stderr( + "\ +[ERROR] tree format `{}` not valid + +Caused by: + unsupported pattern `` +", + ) + .with_status(101) + .run(); + + p.cargo("tree --format {p}-{{hello}}") + .with_stdout("foo v0.1.0 ([..]/foo)-{hello}") + .run(); + + p.cargo("tree --format") + .arg("{p} {l} {r}") + .with_stdout("foo v0.1.0 ([..]/foo) MIT https://github.com/rust-lang/cargo") + .run(); + + p.cargo("tree --format") + .arg("{p} {f}") + .with_stdout("foo v0.1.0 ([..]/foo) bar,default,foo") + .run(); + + p.cargo("tree --all-features --format") + .arg("{p} [{f}]") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) [bar,default,dep,dep_that_is_awesome,foo,other-dep] +├── dep v1.0.0 [] +├── dep_that_is_awesome v1.0.0 [] +└── other-dep v1.0.0 [] +", + ) + .run(); + + p.cargo("tree") + .arg("--features=other-dep,dep_that_is_awesome") + .arg("--format={lib}") + .with_stdout( + " +├── awesome_dep +└── other_dep +", + ) + .run(); +} + +#[cargo_test] +fn dev_dep_feature() { + // New feature resolver with optional dep + Package::new("optdep", "1.0.0").publish(); + Package::new("bar", "1.0.0") + .add_dep(Dependency::new("optdep", "1.0").optional(true)) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dev-dependencies] + bar = { version = "1.0", features = ["optdep"] } + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Old behavior. + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── bar v1.0.0 + └── optdep v1.0.0 +[dev-dependencies] +└── bar v1.0.0 (*) +", + ) + .run(); + + p.cargo("tree -e normal") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── bar v1.0.0 + └── optdep v1.0.0 +", + ) + .run(); + + // New behavior. + switch_to_resolver_2(&p); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── bar v1.0.0 + └── optdep v1.0.0 +[dev-dependencies] +└── bar v1.0.0 (*) +", + ) + .run(); + + p.cargo("tree -e normal") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── bar v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn host_dep_feature() { + // New feature resolver with optional build dep + Package::new("optdep", "1.0.0").publish(); + Package::new("bar", "1.0.0") + .add_dep(Dependency::new("optdep", "1.0").optional(true)) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [build-dependencies] + bar = { version = "1.0", features = ["optdep"] } + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + // Old behavior + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── bar v1.0.0 + └── optdep v1.0.0 +[build-dependencies] +└── bar v1.0.0 (*) +", + ) + .run(); + + // -p + p.cargo("tree -p bar") + .with_stdout( + "\ +bar v1.0.0 +└── optdep v1.0.0 +", + ) + .run(); + + // invert + p.cargo("tree -i optdep") + .with_stdout( + "\ +optdep v1.0.0 +└── bar v1.0.0 + └── foo v0.1.0 ([..]/foo) + [build-dependencies] + └── foo v0.1.0 ([..]/foo) +", + ) + .run(); + + // New behavior. + switch_to_resolver_2(&p); + + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── bar v1.0.0 +[build-dependencies] +└── bar v1.0.0 + └── optdep v1.0.0 +", + ) + .run(); + + p.cargo("tree -p bar") + .with_stdout( + "\ +bar v1.0.0 + +bar v1.0.0 +└── optdep v1.0.0 +", + ) + .run(); + + p.cargo("tree -i optdep") + .with_stdout( + "\ +optdep v1.0.0 +└── bar v1.0.0 + [build-dependencies] + └── foo v0.1.0 ([..]/foo) +", + ) + .run(); + + // Check that -d handles duplicates with features. + p.cargo("tree -d") + .with_stdout( + "\ +bar v1.0.0 +└── foo v0.1.0 ([..]/foo) + +bar v1.0.0 +[build-dependencies] +└── foo v0.1.0 ([..]/foo) +", + ) + .run(); +} + +#[cargo_test] +fn proc_macro_features() { + // New feature resolver with a proc-macro + Package::new("optdep", "1.0.0").publish(); + Package::new("somedep", "1.0.0") + .add_dep(Dependency::new("optdep", "1.0").optional(true)) + .publish(); + Package::new("pm", "1.0.0") + .proc_macro(true) + .feature_dep("somedep", "1.0", &["optdep"]) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + pm = "1.0" + somedep = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Old behavior + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── pm v1.0.0 (proc-macro) +│ └── somedep v1.0.0 +│ └── optdep v1.0.0 +└── somedep v1.0.0 (*) +", + ) + .run(); + + // Old behavior + no-proc-macro + p.cargo("tree -e no-proc-macro") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── somedep v1.0.0 + └── optdep v1.0.0 +", + ) + .run(); + + // -p + p.cargo("tree -p somedep") + .with_stdout( + "\ +somedep v1.0.0 +└── optdep v1.0.0 +", + ) + .run(); + + // -p -e no-proc-macro + p.cargo("tree -p somedep -e no-proc-macro") + .with_stdout( + "\ +somedep v1.0.0 +└── optdep v1.0.0 +", + ) + .run(); + + // invert + p.cargo("tree -i somedep") + .with_stdout( + "\ +somedep v1.0.0 +├── foo v0.1.0 ([..]/foo) +└── pm v1.0.0 (proc-macro) + └── foo v0.1.0 ([..]/foo) +", + ) + .run(); + + // invert + no-proc-macro + p.cargo("tree -i somedep -e no-proc-macro") + .with_stdout( + "\ +somedep v1.0.0 +└── foo v0.1.0 ([..]/foo) +", + ) + .run(); + + // New behavior. + switch_to_resolver_2(&p); + + // Note the missing (*) + p.cargo("tree") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── pm v1.0.0 (proc-macro) +│ └── somedep v1.0.0 +│ └── optdep v1.0.0 +└── somedep v1.0.0 +", + ) + .run(); + + p.cargo("tree -e no-proc-macro") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── somedep v1.0.0 +", + ) + .run(); + + p.cargo("tree -p somedep") + .with_stdout( + "\ +somedep v1.0.0 + +somedep v1.0.0 +└── optdep v1.0.0 +", + ) + .run(); + + p.cargo("tree -i somedep") + .with_stdout( + "\ +somedep v1.0.0 +└── foo v0.1.0 ([..]/foo) + +somedep v1.0.0 +└── pm v1.0.0 (proc-macro) + └── foo v0.1.0 ([..]/foo) +", + ) + .run(); + + p.cargo("tree -i somedep -e no-proc-macro") + .with_stdout( + "\ +somedep v1.0.0 +└── foo v0.1.0 ([..]/foo) + +somedep v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn itarget_opt_dep() { + // New feature resolver with optional target dep + Package::new("optdep", "1.0.0").publish(); + Package::new("common", "1.0.0") + .add_dep(Dependency::new("optdep", "1.0").optional(true)) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [dependencies] + common = "1.0" + + [target.'cfg(whatever)'.dependencies] + common = { version = "1.0", features = ["optdep"] } + + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Old behavior + p.cargo("tree") + .with_stdout( + "\ +foo v1.0.0 ([..]/foo) +└── common v1.0.0 + └── optdep v1.0.0 +", + ) + .run(); + + // New behavior. + switch_to_resolver_2(&p); + + p.cargo("tree") + .with_stdout( + "\ +foo v1.0.0 ([..]/foo) +└── common v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn ambiguous_name() { + // -p that is ambiguous. + Package::new("dep", "1.0.0").publish(); + Package::new("dep", "2.0.0").publish(); + Package::new("bar", "1.0.0").dep("dep", "2.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = "1.0" + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree -p dep") + .with_stderr_contains( + "\ +error: There are multiple `dep` packages in your project, and the specification `dep` is ambiguous. +Please re-run this command with `-p ` where `` is one of the following: + dep@1.0.0 + dep@2.0.0 +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn workspace_features_are_local() { + // The features for workspace packages should be the same as `cargo build` + // (i.e., the features selected depend on the "current" package). + Package::new("optdep", "1.0.0").publish(); + Package::new("somedep", "1.0.0") + .add_dep(Dependency::new("optdep", "1.0").optional(true)) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + somedep = {version="1.0", features=["optdep"]} + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [dependencies] + somedep = "1.0" + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout( + "\ +a v0.1.0 ([..]/foo/a) +└── somedep v1.0.0 + └── optdep v1.0.0 + +b v0.1.0 ([..]/foo/b) +└── somedep v1.0.0 (*) +", + ) + .run(); + + p.cargo("tree -p a") + .with_stdout( + "\ +a v0.1.0 ([..]/foo/a) +└── somedep v1.0.0 + └── optdep v1.0.0 +", + ) + .run(); + + p.cargo("tree -p b") + .with_stdout( + "\ +b v0.1.0 ([..]/foo/b) +└── somedep v1.0.0 +", + ) + .run(); +} + +#[cargo_test] +fn unknown_edge_kind() { + let p = project() + .file("Cargo.toml", "") + .file("src/lib.rs", "") + .build(); + + p.cargo("tree -e unknown") + .with_stderr( + "\ +[ERROR] unknown edge kind `unknown`, valid values are \ +\"normal\", \"build\", \"dev\", \ +\"no-normal\", \"no-build\", \"no-dev\", \"no-proc-macro\", \ +\"features\", or \"all\" +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn mixed_no_edge_kinds() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree -e no-build,normal") + .with_stderr( + "\ +[ERROR] `normal` dependency kind cannot be mixed with \ +\"no-normal\", \"no-build\", or \"no-dev\" dependency kinds +", + ) + .with_status(101) + .run(); + + // `no-proc-macro` can be mixed with others + p.cargo("tree -e no-proc-macro,normal") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +", + ) + .run(); +} + +#[cargo_test] +fn depth_limit() { + let p = make_simple_proj(); + + p.cargo("tree --depth 0") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +[build-dependencies] +[dev-dependencies] +", + ) + .run(); + + p.cargo("tree --depth 1") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── a v1.0.0 +└── c v1.0.0 +[build-dependencies] +└── bdep v1.0.0 +[dev-dependencies] +└── devdep v1.0.0 +", + ) + .run(); + + p.cargo("tree --depth 2") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── a v1.0.0 +│ └── b v1.0.0 +└── c v1.0.0 +[build-dependencies] +└── bdep v1.0.0 + └── b v1.0.0 (*) +[dev-dependencies] +└── devdep v1.0.0 + └── b v1.0.0 (*) +", + ) + .run(); + + // specify a package + p.cargo("tree -p bdep --depth 1") + .with_stdout( + "\ +bdep v1.0.0 +└── b v1.0.0 +", + ) + .run(); + + // different prefix + p.cargo("tree --depth 1 --prefix depth") + .with_stdout( + "\ +0foo v0.1.0 ([..]/foo) +1a v1.0.0 +1c v1.0.0 +1bdep v1.0.0 +1devdep v1.0.0 +", + ) + .run(); + + // with edge-kinds + p.cargo("tree --depth 1 -e no-dev") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── a v1.0.0 +└── c v1.0.0 +[build-dependencies] +└── bdep v1.0.0 +", + ) + .run(); + + // invert + p.cargo("tree --depth 1 --invert c") + .with_stdout( + "\ +c v1.0.0 +├── b v1.0.0 +└── foo v0.1.0 ([..]/foo) +", + ) + .run(); +} + +#[cargo_test] +fn prune() { + let p = make_simple_proj(); + + p.cargo("tree --prune c") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── a v1.0.0 + └── b v1.0.0 +[build-dependencies] +└── bdep v1.0.0 + └── b v1.0.0 (*) +[dev-dependencies] +└── devdep v1.0.0 + └── b v1.0.0 (*) +", + ) + .run(); + + // multiple prune + p.cargo("tree --prune c --prune bdep") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── a v1.0.0 + └── b v1.0.0 +[build-dependencies] +[dev-dependencies] +└── devdep v1.0.0 + └── b v1.0.0 (*) +", + ) + .run(); + + // with edge-kinds + p.cargo("tree --prune c -e normal") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── a v1.0.0 + └── b v1.0.0 +", + ) + .run(); + + // pruning self does not works + p.cargo("tree --prune foo") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── a v1.0.0 +│ └── b v1.0.0 +│ └── c v1.0.0 +└── c v1.0.0 +[build-dependencies] +└── bdep v1.0.0 + └── b v1.0.0 (*) +[dev-dependencies] +└── devdep v1.0.0 + └── b v1.0.0 (*) +", + ) + .run(); + + // dep not exist + p.cargo("tree --prune no-dep") + .with_stderr( + "\ +[ERROR] package ID specification `no-dep` did not match any packages + +Did you mean `bdep`? +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn cyclic_features() { + // Check for stack overflow with cyclic features (oops!). + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [features] + a = ["b"] + b = ["a"] + default = ["a"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree -e features") + .with_stdout("foo v1.0.0 ([ROOT]/foo)") + .run(); + + p.cargo("tree -e features -i foo") + .with_stdout( + "\ +foo v1.0.0 ([ROOT]/foo) +├── foo feature \"a\" +│ ├── foo feature \"b\" +│ │ └── foo feature \"a\" (*) +│ └── foo feature \"default\" (command-line) +├── foo feature \"b\" (*) +└── foo feature \"default\" (command-line) +", + ) + .run(); +} + +#[cargo_test] +fn dev_dep_cycle_with_feature() { + // Cycle with features and a dev-dependency. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [dev-dependencies] + bar = { path = "bar" } + + [features] + a = ["bar/feat1"] + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "1.0.0" + + [dependencies] + foo = { path = ".." } + + [features] + feat1 = ["foo/a"] + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("tree -e features --features a") + .with_stdout( + "\ +foo v1.0.0 ([ROOT]/foo) +[dev-dependencies] +└── bar feature \"default\" + └── bar v1.0.0 ([ROOT]/foo/bar) + └── foo feature \"default\" (command-line) + └── foo v1.0.0 ([ROOT]/foo) (*) +", + ) + .run(); + + p.cargo("tree -e features --features a -i foo") + .with_stdout( + "\ +foo v1.0.0 ([ROOT]/foo) +├── foo feature \"a\" (command-line) +│ └── bar feature \"feat1\" +│ └── foo feature \"a\" (command-line) (*) +└── foo feature \"default\" (command-line) + └── bar v1.0.0 ([ROOT]/foo/bar) + ├── bar feature \"default\" + │ [dev-dependencies] + │ └── foo v1.0.0 ([ROOT]/foo) (*) + └── bar feature \"feat1\" (*) +", + ) + .run(); +} + +#[cargo_test] +fn dev_dep_cycle_with_feature_nested() { + // Checks for an issue where a cyclic dev dependency tries to activate a + // feature on its parent that tries to activate the feature back on the + // dev-dependency. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + + [dev-dependencies] + bar = { path = "bar" } + + [features] + a = ["bar/feat1"] + b = ["a"] + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "1.0.0" + + [dependencies] + foo = { path = ".." } + + [features] + feat1 = ["foo/b"] + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("tree -e features") + .with_stdout( + "\ +foo v1.0.0 ([ROOT]/foo) +[dev-dependencies] +└── bar feature \"default\" + └── bar v1.0.0 ([ROOT]/foo/bar) + └── foo feature \"default\" (command-line) + └── foo v1.0.0 ([ROOT]/foo) (*) +", + ) + .run(); + + p.cargo("tree -e features --features a -i foo") + .with_stdout( + "\ +foo v1.0.0 ([ROOT]/foo) +├── foo feature \"a\" (command-line) +│ └── foo feature \"b\" +│ └── bar feature \"feat1\" +│ └── foo feature \"a\" (command-line) (*) +├── foo feature \"b\" (*) +└── foo feature \"default\" (command-line) + └── bar v1.0.0 ([ROOT]/foo/bar) + ├── bar feature \"default\" + │ [dev-dependencies] + │ └── foo v1.0.0 ([ROOT]/foo) (*) + └── bar feature \"feat1\" (*) +", + ) + .run(); + + p.cargo("tree -e features --features b -i foo") + .with_stdout( + "\ +foo v1.0.0 ([ROOT]/foo) +├── foo feature \"a\" +│ └── foo feature \"b\" (command-line) +│ └── bar feature \"feat1\" +│ └── foo feature \"a\" (*) +├── foo feature \"b\" (command-line) (*) +└── foo feature \"default\" (command-line) + └── bar v1.0.0 ([ROOT]/foo/bar) + ├── bar feature \"default\" + │ [dev-dependencies] + │ └── foo v1.0.0 ([ROOT]/foo) (*) + └── bar feature \"feat1\" (*) +", + ) + .run(); + + p.cargo("tree -e features --features bar/feat1 -i foo") + .with_stdout( + "\ +foo v1.0.0 ([ROOT]/foo) +├── foo feature \"a\" +│ └── foo feature \"b\" +│ └── bar feature \"feat1\" (command-line) +│ └── foo feature \"a\" (*) +├── foo feature \"b\" (*) +└── foo feature \"default\" (command-line) + └── bar v1.0.0 ([ROOT]/foo/bar) + ├── bar feature \"default\" + │ [dev-dependencies] + │ └── foo v1.0.0 ([ROOT]/foo) (*) + └── bar feature \"feat1\" (command-line) (*) +", + ) + .run(); +} diff --git a/tests/testsuite/tree_graph_features.rs b/tests/testsuite/tree_graph_features.rs new file mode 100644 index 0000000..48d654c --- /dev/null +++ b/tests/testsuite/tree_graph_features.rs @@ -0,0 +1,362 @@ +//! Tests for the `cargo tree` command with -e features option. + +use cargo_test_support::project; +use cargo_test_support::registry::{Dependency, Package}; + +#[cargo_test] +fn dep_feature_various() { + // Checks different ways of setting features via dependencies. + Package::new("optdep", "1.0.0") + .feature("default", &["cat"]) + .feature("cat", &[]) + .publish(); + Package::new("defaultdep", "1.0.0") + .feature("default", &["f1"]) + .feature("f1", &["optdep"]) + .add_dep(Dependency::new("optdep", "1.0").optional(true)) + .publish(); + Package::new("nodefaultdep", "1.0.0") + .feature("default", &["f1"]) + .feature("f1", &[]) + .publish(); + Package::new("nameddep", "1.0.0") + .add_dep(Dependency::new("serde", "1.0").optional(true)) + .feature("default", &["serde-stuff"]) + .feature("serde-stuff", &["serde/derive"]) + .feature("vehicle", &["car"]) + .feature("car", &[]) + .publish(); + Package::new("serde_derive", "1.0.0").publish(); + Package::new("serde", "1.0.0") + .feature("derive", &["serde_derive"]) + .add_dep(Dependency::new("serde_derive", "1.0").optional(true)) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + defaultdep = "1.0" + nodefaultdep = {version="1.0", default-features = false} + nameddep = {version="1.0", features = ["vehicle", "serde"]} + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree -e features") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── nodefaultdep v1.0.0 +├── defaultdep feature \"default\" +│ ├── defaultdep v1.0.0 +│ │ └── optdep feature \"default\" +│ │ ├── optdep v1.0.0 +│ │ └── optdep feature \"cat\" +│ │ └── optdep v1.0.0 +│ └── defaultdep feature \"f1\" +│ ├── defaultdep v1.0.0 (*) +│ └── defaultdep feature \"optdep\" +│ └── defaultdep v1.0.0 (*) +├── nameddep feature \"default\" +│ ├── nameddep v1.0.0 +│ │ └── serde feature \"default\" +│ │ └── serde v1.0.0 +│ │ └── serde_derive feature \"default\" +│ │ └── serde_derive v1.0.0 +│ └── nameddep feature \"serde-stuff\" +│ ├── nameddep v1.0.0 (*) +│ ├── nameddep feature \"serde\" +│ │ └── nameddep v1.0.0 (*) +│ └── serde feature \"derive\" +│ ├── serde v1.0.0 (*) +│ └── serde feature \"serde_derive\" +│ └── serde v1.0.0 (*) +├── nameddep feature \"serde\" (*) +└── nameddep feature \"vehicle\" + ├── nameddep v1.0.0 (*) + └── nameddep feature \"car\" + └── nameddep v1.0.0 (*) +", + ) + .run(); +} + +#[cargo_test] +fn graph_features_ws_interdependent() { + // A workspace with interdependent crates. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + b = {path="../b", features=["feat2"]} + + [features] + default = ["a1"] + a1 = [] + a2 = [] + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [features] + default = ["feat1"] + feat1 = [] + feat2 = [] + "#, + ) + .file("b/src/lib.rs", "") + .build(); + + p.cargo("tree -e features") + .with_stdout( + "\ +a v0.1.0 ([..]/foo/a) +├── b feature \"default\" (command-line) +│ ├── b v0.1.0 ([..]/foo/b) +│ └── b feature \"feat1\" +│ └── b v0.1.0 ([..]/foo/b) +└── b feature \"feat2\" + └── b v0.1.0 ([..]/foo/b) + +b v0.1.0 ([..]/foo/b) +", + ) + .run(); + + p.cargo("tree -e features -i a -i b") + .with_stdout( + "\ +a v0.1.0 ([..]/foo/a) +├── a feature \"a1\" +│ └── a feature \"default\" (command-line) +└── a feature \"default\" (command-line) + +b v0.1.0 ([..]/foo/b) +├── b feature \"default\" (command-line) +│ └── a v0.1.0 ([..]/foo/a) (*) +├── b feature \"feat1\" +│ └── b feature \"default\" (command-line) (*) +└── b feature \"feat2\" + └── a v0.1.0 ([..]/foo/a) (*) +", + ) + .run(); +} + +#[cargo_test] +fn slash_feature_name() { + // dep_name/feat_name syntax + Package::new("opt", "1.0.0").feature("feat1", &[]).publish(); + Package::new("notopt", "1.0.0") + .feature("cat", &[]) + .feature("animal", &["cat"]) + .publish(); + Package::new("opt2", "1.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + opt = {version = "1.0", optional=true} + opt2 = {version = "1.0", optional=true} + notopt = "1.0" + + [features] + f1 = ["opt/feat1", "notopt/animal"] + f2 = ["f1"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("tree -e features --features f1") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── notopt feature \"default\" +│ └── notopt v1.0.0 +└── opt feature \"default\" + └── opt v1.0.0 +", + ) + .run(); + + p.cargo("tree -e features --features f1 -i foo") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── foo feature \"default\" (command-line) +├── foo feature \"f1\" (command-line) +└── foo feature \"opt\" + └── foo feature \"f1\" (command-line) +", + ) + .run(); + + p.cargo("tree -e features --features f1 -i notopt") + .with_stdout( + "\ +notopt v1.0.0 +├── notopt feature \"animal\" +│ └── foo feature \"f1\" (command-line) +├── notopt feature \"cat\" +│ └── notopt feature \"animal\" (*) +└── notopt feature \"default\" + └── foo v0.1.0 ([..]/foo) + ├── foo feature \"default\" (command-line) + ├── foo feature \"f1\" (command-line) + └── foo feature \"opt\" + └── foo feature \"f1\" (command-line) +", + ) + .run(); + + p.cargo("tree -e features --features notopt/animal -i notopt") + .with_stdout( + "\ +notopt v1.0.0 +├── notopt feature \"animal\" (command-line) +├── notopt feature \"cat\" +│ └── notopt feature \"animal\" (command-line) +└── notopt feature \"default\" + └── foo v0.1.0 ([..]/foo) + └── foo feature \"default\" (command-line) +", + ) + .run(); + + p.cargo("tree -e features --all-features") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── notopt feature \"default\" +│ └── notopt v1.0.0 +├── opt feature \"default\" +│ └── opt v1.0.0 +└── opt2 feature \"default\" + └── opt2 v1.0.0 +", + ) + .run(); + + p.cargo("tree -e features --all-features -i opt2") + .with_stdout( + "\ +opt2 v1.0.0 +└── opt2 feature \"default\" + └── foo v0.1.0 ([..]/foo) + ├── foo feature \"default\" (command-line) + ├── foo feature \"f1\" (command-line) + │ └── foo feature \"f2\" (command-line) + ├── foo feature \"f2\" (command-line) + ├── foo feature \"opt\" (command-line) + │ └── foo feature \"f1\" (command-line) (*) + └── foo feature \"opt2\" (command-line) +", + ) + .run(); +} + +#[cargo_test] +fn features_enables_inactive_target() { + // Features that enable things on targets that are not enabled. + Package::new("optdep", "1.0.0") + .feature("feat1", &[]) + .publish(); + Package::new("dep1", "1.0.0") + .feature("somefeat", &[]) + .publish(); + Package::new("dep2", "1.0.0") + .add_dep( + Dependency::new("optdep", "1.0.0") + .optional(true) + .target("cfg(whatever)"), + ) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [target.'cfg(whatever)'.dependencies] + optdep = {version="1.0", optional=true} + dep1 = "1.0" + + [dependencies] + dep2 = "1.0" + + [features] + f1 = ["optdep"] + f2 = ["optdep/feat1"] + f3 = ["dep1/somefeat"] + f4 = ["dep2/optdep"] + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("tree -e features") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── dep2 feature \"default\" + └── dep2 v1.0.0 +", + ) + .run(); + p.cargo("tree -e features --all-features") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +└── dep2 feature \"default\" + └── dep2 v1.0.0 +", + ) + .run(); + p.cargo("tree -e features --all-features --target=all") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo) +├── dep1 feature \"default\" +│ └── dep1 v1.0.0 +├── dep2 feature \"default\" +│ └── dep2 v1.0.0 +│ └── optdep feature \"default\" +│ └── optdep v1.0.0 +└── optdep feature \"default\" (*) +", + ) + .run(); +} diff --git a/tests/testsuite/unit_graph.rs b/tests/testsuite/unit_graph.rs new file mode 100644 index 0000000..9145117 --- /dev/null +++ b/tests/testsuite/unit_graph.rs @@ -0,0 +1,233 @@ +//! Tests for --unit-graph option. + +use cargo_test_support::project; +use cargo_test_support::registry::Package; + +#[cargo_test] +fn gated() { + let p = project().file("src/lib.rs", "").build(); + p.cargo("build --unit-graph") + .with_status(101) + .with_stderr( + "\ +[ERROR] the `--unit-graph` flag is unstable[..] +See [..] +See [..] +", + ) + .run(); +} + +#[cargo_test] +fn simple() { + Package::new("a", "1.0.0") + .dep("b", "1.0") + .feature("feata", &["b/featb"]) + .publish(); + Package::new("b", "1.0.0") + .dep("c", "1.0") + .feature("featb", &["c/featc"]) + .publish(); + Package::new("c", "1.0.0").feature("featc", &[]).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build --features a/feata --unit-graph -Zunstable-options") + .masquerade_as_nightly_cargo(&["unit-graph"]) + .with_json( + r#"{ + "roots": [ + 3 + ], + "units": [ + { + "dependencies": [ + { + "extern_crate_name": "b", + "index": 1, + "noprelude": false, + "public": false + } + ], + "features": [ + "feata" + ], + "mode": "build", + "pkg_id": "a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "platform": null, + "profile": { + "codegen_backend": null, + "codegen_units": null, + "debug_assertions": true, + "debuginfo": 2, + "incremental": false, + "lto": "false", + "name": "dev", + "opt_level": "0", + "overflow_checks": true, + "panic": "unwind", + "rpath": false, + "split_debuginfo": "{...}", + "strip": "none" + }, + "target": { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "a", + "src_path": "[..]/a-1.0.0/src/lib.rs", + "test": true + } + }, + { + "dependencies": [ + { + "extern_crate_name": "c", + "index": 2, + "noprelude": false, + "public": false + } + ], + "features": [ + "featb" + ], + "mode": "build", + "pkg_id": "b 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "platform": null, + "profile": { + "codegen_backend": null, + "codegen_units": null, + "debug_assertions": true, + "debuginfo": 2, + "incremental": false, + "lto": "false", + "name": "dev", + "opt_level": "0", + "overflow_checks": true, + "panic": "unwind", + "rpath": false, + "split_debuginfo": "{...}", + "strip": "none" + }, + "target": { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "b", + "src_path": "[..]/b-1.0.0/src/lib.rs", + "test": true + } + }, + { + "dependencies": [], + "features": [ + "featc" + ], + "mode": "build", + "pkg_id": "c 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "platform": null, + "profile": { + "codegen_backend": null, + "codegen_units": null, + "debug_assertions": true, + "debuginfo": 2, + "incremental": false, + "lto": "false", + "name": "dev", + "opt_level": "0", + "overflow_checks": true, + "panic": "unwind", + "rpath": false, + "split_debuginfo": "{...}", + "strip": "none" + }, + "target": { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "c", + "src_path": "[..]/c-1.0.0/src/lib.rs", + "test": true + } + }, + { + "dependencies": [ + { + "extern_crate_name": "a", + "index": 0, + "noprelude": false, + "public": false + } + ], + "features": [], + "mode": "build", + "pkg_id": "foo 0.1.0 (path+file://[..]/foo)", + "platform": null, + "profile": { + "codegen_backend": null, + "codegen_units": null, + "debug_assertions": true, + "debuginfo": 2, + "incremental": false, + "lto": "false", + "name": "dev", + "opt_level": "0", + "overflow_checks": true, + "panic": "unwind", + "rpath": false, + "split_debuginfo": "{...}", + "strip": "none" + }, + "target": { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "foo", + "src_path": "[..]/foo/src/lib.rs", + "test": true + } + } + ], + "version": 1 + } + "#, + ) + .run(); +} diff --git a/tests/testsuite/update.rs b/tests/testsuite/update.rs new file mode 100644 index 0000000..a2105ba --- /dev/null +++ b/tests/testsuite/update.rs @@ -0,0 +1,832 @@ +//! Tests for the `cargo update` command. + +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_manifest, project}; + +#[cargo_test] +fn minor_update_two_places() { + Package::new("log", "0.1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + log = "0.1" + foo = { path = "foo" } + "#, + ) + .file("src/lib.rs", "") + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + log = "0.1" + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("check").run(); + Package::new("log", "0.1.1").publish(); + + p.change_file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + log = "0.1.1" + "#, + ); + + p.cargo("check").run(); +} + +#[cargo_test] +fn transitive_minor_update() { + Package::new("log", "0.1.0").publish(); + Package::new("serde", "0.1.0").dep("log", "0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + serde = "0.1" + log = "0.1" + foo = { path = "foo" } + "#, + ) + .file("src/lib.rs", "") + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + serde = "0.1" + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + Package::new("log", "0.1.1").publish(); + Package::new("serde", "0.1.1").dep("log", "0.1.1").publish(); + + // Note that `serde` isn't actually updated here! The default behavior for + // `update` right now is to as conservatively as possible attempt to satisfy + // an update. In this case we previously locked the dependency graph to `log + // 0.1.0`, but nothing on the command line says we're allowed to update + // that. As a result the update of `serde` here shouldn't update to `serde + // 0.1.1` as that would also force an update to `log 0.1.1`. + // + // Also note that this is probably counterintuitive and weird. We may wish + // to change this one day. + p.cargo("update -p serde") + .with_stderr( + "\ +[UPDATING] `[..]` index +", + ) + .run(); +} + +#[cargo_test] +fn conservative() { + Package::new("log", "0.1.0").publish(); + Package::new("serde", "0.1.0").dep("log", "0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + serde = "0.1" + log = "0.1" + foo = { path = "foo" } + "#, + ) + .file("src/lib.rs", "") + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + serde = "0.1" + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + Package::new("log", "0.1.1").publish(); + Package::new("serde", "0.1.1").dep("log", "0.1").publish(); + + p.cargo("update -p serde") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] serde v0.1.0 -> v0.1.1 +", + ) + .run(); +} + +#[cargo_test] +fn update_via_new_dep() { + Package::new("log", "0.1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + log = "0.1" + # foo = { path = "foo" } + "#, + ) + .file("src/lib.rs", "") + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + log = "0.1.1" + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("check").run(); + Package::new("log", "0.1.1").publish(); + + p.uncomment_root_manifest(); + p.cargo("check").env("CARGO_LOG", "cargo=trace").run(); +} + +#[cargo_test] +fn update_via_new_member() { + Package::new("log", "0.1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [workspace] + # members = [ "foo" ] + + [dependencies] + log = "0.1" + "#, + ) + .file("src/lib.rs", "") + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + log = "0.1.1" + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("check").run(); + Package::new("log", "0.1.1").publish(); + + p.uncomment_root_manifest(); + p.cargo("check").run(); +} + +#[cargo_test] +fn add_dep_deep_new_requirement() { + Package::new("log", "0.1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + log = "0.1" + # bar = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + Package::new("log", "0.1.1").publish(); + Package::new("bar", "0.1.0").dep("log", "0.1.1").publish(); + + p.uncomment_root_manifest(); + p.cargo("check").run(); +} + +#[cargo_test] +fn everything_real_deep() { + Package::new("log", "0.1.0").publish(); + Package::new("foo", "0.1.0").dep("log", "0.1").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + foo = "0.1" + # bar = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + Package::new("log", "0.1.1").publish(); + Package::new("bar", "0.1.0").dep("log", "0.1.1").publish(); + + p.uncomment_root_manifest(); + p.cargo("check").run(); +} + +#[cargo_test] +fn change_package_version() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a-foo" + version = "0.2.0-alpha" + authors = [] + + [dependencies] + bar = { path = "bar", version = "0.2.0-alpha" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0-alpha")) + .file("bar/src/lib.rs", "") + .file( + "Cargo.lock", + r#" + [[package]] + name = "foo" + version = "0.2.0" + dependencies = ["bar 0.2.0"] + + [[package]] + name = "bar" + version = "0.2.0" + "#, + ) + .build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn update_precise() { + Package::new("serde", "0.1.0").publish(); + Package::new("serde", "0.2.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + serde = "0.2" + foo = { path = "foo" } + "#, + ) + .file("src/lib.rs", "") + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + serde = "0.1" + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + Package::new("serde", "0.2.0").publish(); + + p.cargo("update -p serde:0.2.1 --precise 0.2.0") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] serde v0.2.1 -> v0.2.0 +", + ) + .run(); +} + +#[cargo_test] +fn update_precise_do_not_force_update_deps() { + Package::new("log", "0.1.0").publish(); + Package::new("serde", "0.2.1").dep("log", "0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + serde = "0.2" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + Package::new("log", "0.1.1").publish(); + Package::new("serde", "0.2.2").dep("log", "0.1").publish(); + + p.cargo("update -p serde:0.2.1 --precise 0.2.2") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] serde v0.2.1 -> v0.2.2 +", + ) + .run(); +} + +#[cargo_test] +fn update_aggressive() { + Package::new("log", "0.1.0").publish(); + Package::new("serde", "0.2.1").dep("log", "0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + serde = "0.2" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check").run(); + + Package::new("log", "0.1.1").publish(); + Package::new("serde", "0.2.2").dep("log", "0.1").publish(); + + p.cargo("update -p serde:0.2.1 --aggressive") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] log v0.1.0 -> v0.1.1 +[UPDATING] serde v0.2.1 -> v0.2.2 +", + ) + .run(); +} + +// cargo update should respect its arguments even without a lockfile. +// See issue "Running cargo update without a Cargo.lock ignores arguments" +// at . +#[cargo_test] +fn update_precise_first_run() { + Package::new("serde", "0.1.0").publish(); + Package::new("serde", "0.2.0").publish(); + Package::new("serde", "0.2.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + + [dependencies] + serde = "0.2" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("update -p serde --precise 0.2.0") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] serde v0.2.1 -> v0.2.0 +", + ) + .run(); + + // Assert `cargo metadata` shows serde 0.2.0 + p.cargo("metadata") + .with_json( + r#"{ + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [ + { + "features": [], + "kind": null, + "name": "serde", + "optional": false, + "registry": null, + "rename": null, + "req": "^0.2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "target": null, + "uses_default_features": true + } + ], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "bar 0.0.1 (path+file://[..]/foo)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/Cargo.toml", + "metadata": null, + "publish": null, + "name": "bar", + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "test": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "bar", + "src_path": "[..]/foo/src/lib.rs" + } + ], + "version": "0.0.1" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/home/.cargo/registry/src/-[..]/serde-0.2.0/Cargo.toml", + "metadata": null, + "publish": null, + "name": "serde", + "readme": null, + "repository": null, + "rust_version": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "serde", + "src_path": "[..]/home/.cargo/registry/src/-[..]/serde-0.2.0/src/lib.rs", + "test": true + } + ], + "version": "0.2.0" + } + ], + "resolve": { + "nodes": [ + { + "dependencies": [ + "serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "dep_kinds": [ + { + "kind": null, + "target": null + } + ], + "name": "serde", + "pkg": "serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" + } + ], + "features": [], + "id": "bar 0.0.1 (path+file://[..]/foo)" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" + } + ], + "root": "bar 0.0.1 (path+file://[..]/foo)" + }, + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_members": [ + "bar 0.0.1 (path+file://[..]/foo)" + ], + "workspace_root": "[..]/foo", + "metadata": null +}"#, + ) + .run(); + + p.cargo("update -p serde --precise 0.2.0") + .with_stderr( + "\ +[UPDATING] `[..]` index +", + ) + .run(); +} + +#[cargo_test] +fn preserve_top_comment() { + let p = project().file("src/lib.rs", "").build(); + + p.cargo("update").run(); + + let lockfile = p.read_lockfile(); + assert!(lockfile.starts_with("# This file is automatically @generated by Cargo.\n# It is not intended for manual editing.\n")); + + let mut lines = lockfile.lines().collect::>(); + lines.insert(2, "# some other comment"); + let mut lockfile = lines.join("\n"); + lockfile.push('\n'); // .lines/.join loses the last newline + println!("saving Cargo.lock contents:\n{}", lockfile); + + p.change_file("Cargo.lock", &lockfile); + + p.cargo("update").run(); + + let lockfile2 = p.read_lockfile(); + println!("loaded Cargo.lock contents:\n{}", lockfile2); + + assert_eq!(lockfile, lockfile2); +} + +#[cargo_test] +fn dry_run_update() { + Package::new("log", "0.1.0").publish(); + Package::new("serde", "0.1.0").dep("log", "0.1").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [dependencies] + serde = "0.1" + log = "0.1" + foo = { path = "foo" } + "#, + ) + .file("src/lib.rs", "") + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + serde = "0.1" + "#, + ) + .file("foo/src/lib.rs", "") + .build(); + + p.cargo("check").run(); + let old_lockfile = p.read_lockfile(); + + Package::new("log", "0.1.1").publish(); + Package::new("serde", "0.1.1").dep("log", "0.1").publish(); + + p.cargo("update -p serde --dry-run") + .with_stderr( + "\ +[UPDATING] `[..]` index +[UPDATING] serde v0.1.0 -> v0.1.1 +[WARNING] not updating lockfile due to dry run +", + ) + .run(); + let new_lockfile = p.read_lockfile(); + assert_eq!(old_lockfile, new_lockfile) +} + +#[cargo_test] +fn workspace_only() { + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("generate-lockfile").run(); + let lock1 = p.read_lockfile(); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + authors = [] + version = "0.0.2" + "#, + ); + p.cargo("update --workspace").run(); + let lock2 = p.read_lockfile(); + + assert_ne!(lock1, lock2); + assert!(lock1.contains("0.0.1")); + assert!(lock2.contains("0.0.2")); + assert!(!lock1.contains("0.0.2")); + assert!(!lock2.contains("0.0.1")); +} + +#[cargo_test] +fn precise_with_build_metadata() { + // +foo syntax shouldn't be necessary with --precise + Package::new("bar", "0.1.0+extra-stuff.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("generate-lockfile").run(); + Package::new("bar", "0.1.1+extra-stuff.1").publish(); + Package::new("bar", "0.1.2+extra-stuff.2").publish(); + + p.cargo("update -p bar --precise 0.1") + .with_status(101) + .with_stderr( + "\ +error: invalid version format for precise version `0.1` + +Caused by: + unexpected end of input while parsing minor version number +", + ) + .run(); + + p.cargo("update -p bar --precise 0.1.1+does-not-match") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +error: no matching package named `bar` found +location searched: registry `crates-io` +required by package `foo v0.1.0 ([ROOT]/foo)` +", + ) + .run(); + + p.cargo("update -p bar --precise 0.1.1") + .with_stderr( + "\ +[UPDATING] [..] index +[UPDATING] bar v0.1.0+extra-stuff.0 -> v0.1.1+extra-stuff.1 +", + ) + .run(); + + Package::new("bar", "0.1.3").publish(); + p.cargo("update -p bar --precise 0.1.3+foo") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +error: no matching package named `bar` found +location searched: registry `crates-io` +required by package `foo v0.1.0 ([ROOT]/foo)` +", + ) + .run(); + + p.cargo("update -p bar --precise 0.1.3") + .with_stderr( + "\ +[UPDATING] [..] index +[UPDATING] bar v0.1.1+extra-stuff.1 -> v0.1.3 +", + ) + .run(); +} diff --git a/tests/testsuite/vendor.rs b/tests/testsuite/vendor.rs new file mode 100644 index 0000000..2b179fe --- /dev/null +++ b/tests/testsuite/vendor.rs @@ -0,0 +1,1153 @@ +//! Tests for the `cargo vendor` command. +//! +//! Note that every test here uses `--respect-source-config` so that the +//! "fake" crates.io is used. Otherwise `vendor` would download the crates.io +//! index from the network. + +use std::fs; + +use cargo_test_support::git; +use cargo_test_support::registry::{self, Package, RegistryBuilder}; +use cargo_test_support::{basic_lib_manifest, basic_manifest, paths, project, Project}; + +#[cargo_test] +fn vendor_simple() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + log = "0.3.5" + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("log", "0.3.5").publish(); + + p.cargo("vendor --respect-source-config").run(); + let lock = p.read_file("vendor/log/Cargo.toml"); + assert!(lock.contains("version = \"0.3.5\"")); + + add_vendor_config(&p); + p.cargo("check").run(); +} + +#[cargo_test] +fn vendor_sample_config() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + log = "0.3.5" + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("log", "0.3.5").publish(); + + p.cargo("vendor --respect-source-config") + .with_stdout( + r#"[source.crates-io] +replace-with = "vendored-sources" + +[source.vendored-sources] +directory = "vendor" +"#, + ) + .run(); +} + +#[cargo_test] +fn vendor_sample_config_alt_registry() { + let registry = RegistryBuilder::new().alternative().http_index().build(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + log = { version = "0.3.5", registry = "alternative" } + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("log", "0.3.5").alternative(true).publish(); + + p.cargo("vendor --respect-source-config -Z sparse-registry") + .masquerade_as_nightly_cargo(&["sparse-registry"]) + .with_stdout(format!( + r#"[source."{0}"] +registry = "{0}" +replace-with = "vendored-sources" + +[source.vendored-sources] +directory = "vendor" +"#, + registry.index_url() + )) + .run(); +} + +#[cargo_test] +fn vendor_path_specified() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + log = "0.3.5" + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("log", "0.3.5").publish(); + + let path = if cfg!(windows) { + r#"deps\.vendor"# + } else { + "deps/.vendor" + }; + + let output = p + .cargo("vendor --respect-source-config") + .arg(path) + .exec_with_output() + .unwrap(); + // Assert against original output to ensure that + // path is normalized by `ops::vendor` on Windows. + assert_eq!( + &String::from_utf8(output.stdout).unwrap(), + r#"[source.crates-io] +replace-with = "vendored-sources" + +[source.vendored-sources] +directory = "deps/.vendor" +"# + ); + + let lock = p.read_file("deps/.vendor/log/Cargo.toml"); + assert!(lock.contains("version = \"0.3.5\"")); +} + +fn add_vendor_config(p: &Project) { + p.change_file( + ".cargo/config", + r#" + [source.crates-io] + replace-with = 'vendor' + + [source.vendor] + directory = 'vendor' + "#, + ); +} + +#[cargo_test] +fn package_exclude() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("bar", "0.1.0") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + exclude = [".*", "!.include", "!.dotdir/include"] + "#, + ) + .file("src/lib.rs", "") + .file(".exclude", "") + .file(".include", "") + .file(".dotdir/exclude", "") + .file(".dotdir/include", "") + .publish(); + + p.cargo("vendor --respect-source-config").run(); + let csum = p.read_file("vendor/bar/.cargo-checksum.json"); + assert!(csum.contains(".include")); + assert!(!csum.contains(".exclude")); + assert!(!csum.contains(".dotdir/exclude")); + // Gitignore doesn't re-include a file in an excluded parent directory, + // even if negating it explicitly. + assert!(!csum.contains(".dotdir/include")); +} + +#[cargo_test] +fn two_versions() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = "0.8.0" + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + bitflags = "0.7.0" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + Package::new("bitflags", "0.7.0").publish(); + Package::new("bitflags", "0.8.0").publish(); + + p.cargo("vendor --respect-source-config").run(); + + let lock = p.read_file("vendor/bitflags/Cargo.toml"); + assert!(lock.contains("version = \"0.8.0\"")); + let lock = p.read_file("vendor/bitflags-0.7.0/Cargo.toml"); + assert!(lock.contains("version = \"0.7.0\"")); + + add_vendor_config(&p); + p.cargo("check").run(); +} + +#[cargo_test] +fn two_explicit_versions() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = "0.8.0" + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + bitflags = "0.7.0" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + Package::new("bitflags", "0.7.0").publish(); + Package::new("bitflags", "0.8.0").publish(); + + p.cargo("vendor --respect-source-config --versioned-dirs") + .run(); + + let lock = p.read_file("vendor/bitflags-0.8.0/Cargo.toml"); + assert!(lock.contains("version = \"0.8.0\"")); + let lock = p.read_file("vendor/bitflags-0.7.0/Cargo.toml"); + assert!(lock.contains("version = \"0.7.0\"")); + + add_vendor_config(&p); + p.cargo("check").run(); +} + +#[cargo_test] +fn help() { + let p = project().build(); + p.cargo("vendor -h").run(); +} + +#[cargo_test] +fn update_versions() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = "0.7.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("bitflags", "0.7.0").publish(); + Package::new("bitflags", "0.8.0").publish(); + + p.cargo("vendor --respect-source-config").run(); + + let lock = p.read_file("vendor/bitflags/Cargo.toml"); + assert!(lock.contains("version = \"0.7.0\"")); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = "0.8.0" + "#, + ); + p.cargo("vendor --respect-source-config").run(); + + let lock = p.read_file("vendor/bitflags/Cargo.toml"); + assert!(lock.contains("version = \"0.8.0\"")); +} + +#[cargo_test] +fn two_lockfiles() { + let p = project() + .no_manifest() + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = "=0.7.0" + "#, + ) + .file("foo/src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + bitflags = "=0.8.0" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + Package::new("bitflags", "0.7.0").publish(); + Package::new("bitflags", "0.8.0").publish(); + + p.cargo("vendor --respect-source-config -s bar/Cargo.toml --manifest-path foo/Cargo.toml") + .run(); + + let lock = p.read_file("vendor/bitflags/Cargo.toml"); + assert!(lock.contains("version = \"0.8.0\"")); + let lock = p.read_file("vendor/bitflags-0.7.0/Cargo.toml"); + assert!(lock.contains("version = \"0.7.0\"")); + + add_vendor_config(&p); + p.cargo("check").cwd("foo").run(); + p.cargo("check").cwd("bar").run(); +} + +#[cargo_test] +fn test_sync_argument() { + let p = project() + .no_manifest() + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = "=0.7.0" + "#, + ) + .file("foo/src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + bitflags = "=0.8.0" + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + + [dependencies] + bitflags = "=0.8.0" + "#, + ) + .file("baz/src/lib.rs", "") + .build(); + + Package::new("bitflags", "0.7.0").publish(); + Package::new("bitflags", "0.8.0").publish(); + + p.cargo("vendor --respect-source-config --manifest-path foo/Cargo.toml -s bar/Cargo.toml baz/Cargo.toml test_vendor") + .with_stderr("\ +error: unexpected argument 'test_vendor' found + +Usage: cargo[EXE] vendor [OPTIONS] [path] + +For more information, try '--help'.", + ) + .with_status(1) + .run(); + + p.cargo("vendor --respect-source-config --manifest-path foo/Cargo.toml -s bar/Cargo.toml -s baz/Cargo.toml test_vendor") + .run(); + + let lock = p.read_file("test_vendor/bitflags/Cargo.toml"); + assert!(lock.contains("version = \"0.8.0\"")); + let lock = p.read_file("test_vendor/bitflags-0.7.0/Cargo.toml"); + assert!(lock.contains("version = \"0.7.0\"")); +} + +#[cargo_test] +fn delete_old_crates() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bitflags = "=0.7.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("bitflags", "0.7.0").publish(); + Package::new("log", "0.3.5").publish(); + + p.cargo("vendor --respect-source-config").run(); + p.read_file("vendor/bitflags/Cargo.toml"); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + log = "=0.3.5" + "#, + ); + + p.cargo("vendor --respect-source-config").run(); + let lock = p.read_file("vendor/log/Cargo.toml"); + assert!(lock.contains("version = \"0.3.5\"")); + assert!(!p.root().join("vendor/bitflags/Cargo.toml").exists()); +} + +#[cargo_test] +fn ignore_files() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + url = "1.4.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("url", "1.4.1") + .file("src/lib.rs", "") + .file("foo.orig", "") + .file(".gitignore", "") + .file(".gitattributes", "") + .file("foo.rej", "") + .publish(); + + p.cargo("vendor --respect-source-config").run(); + let csum = p.read_file("vendor/url/.cargo-checksum.json"); + assert!(!csum.contains("foo.orig")); + assert!(!csum.contains(".gitignore")); + assert!(!csum.contains(".gitattributes")); + assert!(!csum.contains(".cargo-ok")); + assert!(!csum.contains("foo.rej")); +} + +#[cargo_test] +fn included_files_only() { + let git = git::new("a", |p| { + p.file("Cargo.toml", &basic_lib_manifest("a")) + .file("src/lib.rs", "") + .file(".gitignore", "a") + .file("a/b.md", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = {{ git = '{}' }} + "#, + git.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("vendor --respect-source-config").run(); + let csum = p.read_file("vendor/a/.cargo-checksum.json"); + assert!(!csum.contains("a/b.md")); +} + +#[cargo_test] +fn dependent_crates_in_crates() { + let git = git::new("a", |p| { + p.file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + b = { path = 'b' } + "#, + ) + .file("src/lib.rs", "") + .file("b/Cargo.toml", &basic_lib_manifest("b")) + .file("b/src/lib.rs", "") + }); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = {{ git = '{}' }} + "#, + git.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("vendor --respect-source-config").run(); + p.read_file("vendor/a/.cargo-checksum.json"); + p.read_file("vendor/b/.cargo-checksum.json"); +} + +#[cargo_test] +fn vendoring_git_crates() { + let git = git::new("git", |p| { + p.file("Cargo.toml", &basic_lib_manifest("serde_derive")) + .file("src/lib.rs", "") + .file("src/wut.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies.serde] + version = "0.5.0" + + [dependencies.serde_derive] + version = "0.5.0" + + [patch.crates-io] + serde_derive = {{ git = '{}' }} + "#, + git.url() + ), + ) + .file("src/lib.rs", "") + .build(); + Package::new("serde", "0.5.0") + .dep("serde_derive", "0.5") + .publish(); + Package::new("serde_derive", "0.5.0").publish(); + + p.cargo("vendor --respect-source-config").run(); + p.read_file("vendor/serde_derive/src/wut.rs"); + + add_vendor_config(&p); + p.cargo("check").run(); +} + +#[cargo_test] +fn git_simple() { + let git = git::new("git", |p| { + p.file("Cargo.toml", &basic_lib_manifest("a")) + .file("src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = {{ git = '{}' }} + "#, + git.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("vendor --respect-source-config").run(); + let csum = p.read_file("vendor/a/.cargo-checksum.json"); + assert!(csum.contains("\"package\":null")); +} + +#[cargo_test] +fn git_diff_rev() { + let (git_project, git_repo) = git::new_repo("git", |p| { + p.file("Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("src/lib.rs", "") + }); + let url = git_project.url(); + let ref_1 = "v0.1.0"; + let ref_2 = "v0.2.0"; + + git::tag(&git_repo, ref_1); + + git_project.change_file("Cargo.toml", &basic_manifest("a", "0.2.0")); + git::add(&git_repo); + git::commit(&git_repo); + git::tag(&git_repo, ref_2); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a_1 = {{ package = "a", git = '{url}', rev = '{ref_1}' }} + a_2 = {{ package = "a", git = '{url}', rev = '{ref_2}' }} + "# + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("vendor --respect-source-config") + .with_stdout( + r#"[source."git+file://[..]/git?rev=v0.1.0"] +git = [..] +rev = "v0.1.0" +replace-with = "vendored-sources" + +[source."git+file://[..]/git?rev=v0.2.0"] +git = [..] +rev = "v0.2.0" +replace-with = "vendored-sources" + +[source.vendored-sources] +directory = "vendor" +"#, + ) + .run(); +} + +#[cargo_test] +fn git_duplicate() { + let git = git::new("a", |p| { + p.file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + b = { path = 'b' } + "#, + ) + .file("src/lib.rs", "") + .file("b/Cargo.toml", &basic_lib_manifest("b")) + .file("b/src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = {{ git = '{}' }} + b = '0.5.0' + + "#, + git.url() + ), + ) + .file("src/lib.rs", "") + .build(); + Package::new("b", "0.5.0").publish(); + + p.cargo("vendor --respect-source-config") + .with_stderr( + "\ +[UPDATING] [..] +[UPDATING] [..] +[DOWNLOADING] [..] +[DOWNLOADED] [..] +error: failed to sync + +Caused by: + found duplicate version of package `b v0.5.0` vendored from two sources: + + source 1: [..] + source 2: [..] +", + ) + .with_status(101) + .run(); +} + +#[cargo_test] +fn git_complex() { + let git_b = git::new("git_b", |p| { + p.file( + "Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [dependencies] + dep_b = { path = 'dep_b' } + "#, + ) + .file("src/lib.rs", "") + .file("dep_b/Cargo.toml", &basic_lib_manifest("dep_b")) + .file("dep_b/src/lib.rs", "") + }); + + let git_a = git::new("git_a", |p| { + p.file( + "Cargo.toml", + &format!( + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + b = {{ git = '{}' }} + dep_a = {{ path = 'dep_a' }} + "#, + git_b.url() + ), + ) + .file("src/lib.rs", "") + .file("dep_a/Cargo.toml", &basic_lib_manifest("dep_a")) + .file("dep_a/src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = {{ git = '{}' }} + "#, + git_a.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + let output = p + .cargo("vendor --respect-source-config") + .exec_with_output() + .unwrap(); + let output = String::from_utf8(output.stdout).unwrap(); + p.change_file(".cargo/config", &output); + + p.cargo("check -v") + .with_stderr_contains("[..]foo/vendor/a/src/lib.rs[..]") + .with_stderr_contains("[..]foo/vendor/dep_a/src/lib.rs[..]") + .with_stderr_contains("[..]foo/vendor/b/src/lib.rs[..]") + .with_stderr_contains("[..]foo/vendor/dep_b/src/lib.rs[..]") + .run(); +} + +#[cargo_test] +fn depend_on_vendor_dir_not_deleted() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + libc = "0.2.30" + "#, + ) + .file("src/lib.rs", "") + .build(); + + Package::new("libc", "0.2.30").publish(); + + p.cargo("vendor --respect-source-config").run(); + assert!(p.root().join("vendor/libc").is_dir()); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + libc = "0.2.30" + + [patch.crates-io] + libc = { path = 'vendor/libc' } + "#, + ); + + p.cargo("vendor --respect-source-config").run(); + assert!(p.root().join("vendor/libc").is_dir()); +} + +#[cargo_test] +fn ignore_hidden() { + // Don't delete files starting with `.` + Package::new("bar", "0.1.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "1.0.0" + [dependencies] + bar = "0.1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("vendor --respect-source-config").run(); + // Add a `.git` directory. + let repo = git::init(&p.root().join("vendor")); + git::add(&repo); + git::commit(&repo); + assert!(p.root().join("vendor/.git").exists()); + // Vendor again, shouldn't change anything. + p.cargo("vendor --respect-source-config").run(); + // .git should not be removed. + assert!(p.root().join("vendor/.git").exists()); + // And just for good measure, make sure no files changed. + let mut opts = git2::StatusOptions::new(); + assert!(repo + .statuses(Some(&mut opts)) + .unwrap() + .iter() + .all(|status| status.status() == git2::Status::CURRENT)); +} + +#[cargo_test] +fn config_instructions_works() { + // Check that the config instructions work for all dependency kinds. + registry::alt_init(); + Package::new("dep", "0.1.0").publish(); + Package::new("altdep", "0.1.0").alternative(true).publish(); + let git_project = git::new("gitdep", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("gitdep")) + .file("src/lib.rs", "") + }); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = "0.1" + altdep = {{version="0.1", registry="alternative"}} + gitdep = {{git='{}'}} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .build(); + let output = p + .cargo("vendor --respect-source-config") + .exec_with_output() + .unwrap(); + let output = String::from_utf8(output.stdout).unwrap(); + p.change_file(".cargo/config", &output); + + p.cargo("check -v") + .with_stderr_contains("[..]foo/vendor/dep/src/lib.rs[..]") + .with_stderr_contains("[..]foo/vendor/altdep/src/lib.rs[..]") + .with_stderr_contains("[..]foo/vendor/gitdep/src/lib.rs[..]") + .run(); +} + +#[cargo_test] +fn git_crlf_preservation() { + // Check that newlines don't get changed when you vendor + // (will only fail if your system is setup with core.autocrlf=true on windows) + let input = "hello \nthere\nmy newline\nfriends"; + let git_project = git::new("git", |p| { + p.file("Cargo.toml", &basic_lib_manifest("a")) + .file("src/lib.rs", input) + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = {{ git = '{}' }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + fs::write( + paths::home().join(".gitconfig"), + r#" + [core] + autocrlf = true + "#, + ) + .unwrap(); + + p.cargo("vendor --respect-source-config").run(); + let output = p.read_file("vendor/a/src/lib.rs"); + assert_eq!(input, output); +} + +#[cargo_test] +#[cfg(unix)] +fn vendor_preserves_permissions() { + use std::os::unix::fs::MetadataExt; + + Package::new("bar", "1.0.0") + .file_with_mode("example.sh", 0o755, "#!/bin/sh") + .file("src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("vendor --respect-source-config").run(); + + let metadata = fs::metadata(p.root().join("vendor/bar/src/lib.rs")).unwrap(); + assert_eq!(metadata.mode() & 0o777, 0o644); + let metadata = fs::metadata(p.root().join("vendor/bar/example.sh")).unwrap(); + assert_eq!(metadata.mode() & 0o777, 0o755); +} + +#[cargo_test] +fn no_remote_dependency_no_vendor() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("vendor") + .with_stderr("There is no dependency to vendor in this project.") + .run(); + assert!(!p.root().join("vendor").exists()); +} + +#[cargo_test] +fn vendor_crate_with_ws_inherit() { + let git = git::new("ws", |p| { + p.file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + [workspace.package] + version = "0.1.0" + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version.workspace = true + "#, + ) + .file("bar/src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {{ git = '{}' }} + "#, + git.url() + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("vendor --respect-source-config").run(); + p.change_file( + ".cargo/config", + &format!( + r#" + [source."{}"] + git = "{}" + replace-with = "vendor" + + [source.vendor] + directory = "vendor" + "#, + git.url(), + git.url() + ), + ); + + p.cargo("check -v") + .with_stderr_contains("[..]foo/vendor/bar/src/lib.rs[..]") + .run(); +} diff --git a/tests/testsuite/verify_project.rs b/tests/testsuite/verify_project.rs new file mode 100644 index 0000000..216808f --- /dev/null +++ b/tests/testsuite/verify_project.rs @@ -0,0 +1,73 @@ +//! Tests for the `cargo verify-project` command. + +use cargo_test_support::{basic_bin_manifest, main_file, project}; + +fn verify_project_success_output() -> String { + r#"{"success":"true"}"#.into() +} + +#[cargo_test] +fn cargo_verify_project_path_to_cargo_toml_relative() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("verify-project --manifest-path foo/Cargo.toml") + .cwd(p.root().parent().unwrap()) + .with_stdout(verify_project_success_output()) + .run(); +} + +#[cargo_test] +fn cargo_verify_project_path_to_cargo_toml_absolute() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("verify-project --manifest-path") + .arg(p.root().join("Cargo.toml")) + .cwd(p.root().parent().unwrap()) + .with_stdout(verify_project_success_output()) + .run(); +} + +#[cargo_test] +fn cargo_verify_project_cwd() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("verify-project") + .with_stdout(verify_project_success_output()) + .run(); +} + +#[cargo_test] +fn cargo_verify_project_honours_unstable_features() { + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["test-dummy-unstable"] + + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("verify-project") + .masquerade_as_nightly_cargo(&["test-dummy-unstable"]) + .with_stdout(verify_project_success_output()) + .run(); + + p.cargo("verify-project") + .with_status(1) + .with_json(r#"{"invalid":"failed to parse manifest at `[CWD]/Cargo.toml`"}"#) + .run(); +} diff --git a/tests/testsuite/version.rs b/tests/testsuite/version.rs new file mode 100644 index 0000000..f880c75 --- /dev/null +++ b/tests/testsuite/version.rs @@ -0,0 +1,54 @@ +//! Tests for displaying the cargo version. + +use cargo_test_support::{cargo_process, project}; + +#[cargo_test] +fn simple() { + let p = project().build(); + + p.cargo("version") + .with_stdout(&format!("cargo {}\n", cargo::version())) + .run(); + + p.cargo("--version") + .with_stdout(&format!("cargo {}\n", cargo::version())) + .run(); +} + +#[cargo_test] +fn version_works_without_rustc() { + let p = project().build(); + p.cargo("version").env("PATH", "").run(); +} + +#[cargo_test] +fn version_works_with_bad_config() { + let p = project().file(".cargo/config", "this is not toml").build(); + p.cargo("version").run(); +} + +#[cargo_test] +fn version_works_with_bad_target_dir() { + let p = project() + .file( + ".cargo/config", + r#" + [build] + target-dir = 4 + "#, + ) + .build(); + p.cargo("version").run(); +} + +#[cargo_test] +fn verbose() { + // This is mainly to check that it doesn't explode. + cargo_process("-vV") + .with_stdout_contains(&format!("cargo {}", cargo::version())) + .with_stdout_contains("host: [..]") + .with_stdout_contains("libgit2: [..]") + .with_stdout_contains("libcurl: [..]") + .with_stdout_contains("os: [..]") + .run(); +} diff --git a/tests/testsuite/warn_on_failure.rs b/tests/testsuite/warn_on_failure.rs new file mode 100644 index 0000000..19cb018 --- /dev/null +++ b/tests/testsuite/warn_on_failure.rs @@ -0,0 +1,111 @@ +//! Tests for whether or not warnings are displayed for build scripts. + +use cargo_test_support::registry::Package; +use cargo_test_support::{project, Project}; + +static WARNING1: &str = "Hello! I'm a warning. :)"; +static WARNING2: &str = "And one more!"; + +fn make_lib(lib_src: &str) { + Package::new("bar", "0.0.1") + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + authors = [] + version = "0.0.1" + build = "build.rs" + "#, + ) + .file( + "build.rs", + &format!( + r#" + fn main() {{ + use std::io::Write; + println!("cargo:warning={{}}", "{}"); + println!("hidden stdout"); + write!(&mut ::std::io::stderr(), "hidden stderr"); + println!("cargo:warning={{}}", "{}"); + }} + "#, + WARNING1, WARNING2 + ), + ) + .file("src/lib.rs", &format!("fn f() {{ {} }}", lib_src)) + .publish(); +} + +fn make_upstream(main_src: &str) -> Project { + project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("src/main.rs", &format!("fn main() {{ {} }}", main_src)) + .build() +} + +#[cargo_test] +fn no_warning_on_success() { + make_lib(""); + let upstream = make_upstream(""); + upstream + .cargo("build") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] bar v0.0.1 ([..]) +[COMPILING] bar v0.0.1 +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn no_warning_on_bin_failure() { + make_lib(""); + let upstream = make_upstream("hi()"); + upstream + .cargo("build") + .with_status(101) + .with_stdout_does_not_contain("hidden stdout") + .with_stderr_does_not_contain("hidden stderr") + .with_stderr_does_not_contain(&format!("[WARNING] {}", WARNING1)) + .with_stderr_does_not_contain(&format!("[WARNING] {}", WARNING2)) + .with_stderr_contains("[UPDATING] `[..]` index") + .with_stderr_contains("[DOWNLOADED] bar v0.0.1 ([..])") + .with_stderr_contains("[COMPILING] bar v0.0.1") + .with_stderr_contains("[COMPILING] foo v0.0.1 ([..])") + .run(); +} + +#[cargo_test] +fn warning_on_lib_failure() { + make_lib("err()"); + let upstream = make_upstream(""); + upstream + .cargo("build") + .with_status(101) + .with_stdout_does_not_contain("hidden stdout") + .with_stderr_does_not_contain("hidden stderr") + .with_stderr_does_not_contain("[COMPILING] foo v0.0.1 ([..])") + .with_stderr_contains("[UPDATING] `[..]` index") + .with_stderr_contains("[DOWNLOADED] bar v0.0.1 ([..])") + .with_stderr_contains("[COMPILING] bar v0.0.1") + .with_stderr_contains(&format!("[WARNING] {}", WARNING1)) + .with_stderr_contains(&format!("[WARNING] {}", WARNING2)) + .run(); +} diff --git a/tests/testsuite/weak_dep_features.rs b/tests/testsuite/weak_dep_features.rs new file mode 100644 index 0000000..dfc1e6b --- /dev/null +++ b/tests/testsuite/weak_dep_features.rs @@ -0,0 +1,629 @@ +//! Tests for weak-dep-features. + +use super::features2::switch_to_resolver_2; +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::registry::{Dependency, Package, RegistryBuilder}; +use cargo_test_support::{project, publish}; +use std::fmt::Write; + +// Helper to create lib.rs files that check features. +fn require(enabled_features: &[&str], disabled_features: &[&str]) -> String { + let mut s = String::new(); + for feature in enabled_features { + writeln!(s, "#[cfg(not(feature=\"{feature}\"))] compile_error!(\"expected feature {feature} to be enabled\");", + feature=feature).unwrap(); + } + for feature in disabled_features { + writeln!(s, "#[cfg(feature=\"{feature}\")] compile_error!(\"did not expect feature {feature} to be enabled\");", + feature=feature).unwrap(); + } + s +} + +#[cargo_test] +fn simple() { + Package::new("bar", "1.0.0") + .feature("feat", &[]) + .file("src/lib.rs", &require(&["feat"], &[])) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", optional = true } + + [features] + f1 = ["bar?/feat"] + "#, + ) + .file("src/lib.rs", &require(&["f1"], &[])) + .build(); + + // It's a bit unfortunate that this has to download `bar`, but avoiding + // that is extremely difficult. + p.cargo("check --features f1") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 [..] +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("check --features f1,bar") + .with_stderr( + "\ +[CHECKING] bar v1.0.0 +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn deferred() { + // A complex chain that requires deferring enabling the feature due to + // another dependency getting enabled. + Package::new("bar", "1.0.0") + .feature("feat", &[]) + .file("src/lib.rs", &require(&["feat"], &[])) + .publish(); + Package::new("dep", "1.0.0") + .add_dep(Dependency::new("bar", "1.0").optional(true)) + .feature("feat", &["bar?/feat"]) + .publish(); + Package::new("bar_activator", "1.0.0") + .feature_dep("dep", "1.0", &["bar"]) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = { version = "1.0", features = ["feat"] } + bar_activator = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] dep v1.0.0 [..] +[DOWNLOADED] bar_activator v1.0.0 [..] +[DOWNLOADED] bar v1.0.0 [..] +[CHECKING] bar v1.0.0 +[CHECKING] dep v1.0.0 +[CHECKING] bar_activator v1.0.0 +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn not_optional_dep() { + // Attempt to use dep_name?/feat where dep_name is not optional. + Package::new("dep", "1.0.0").feature("feat", &[]).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + dep = "1.0" + + [features] + feat = ["dep?/feat"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr("\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + feature `feat` includes `dep?/feat` with a `?`, but `dep` is not an optional dependency + A non-optional dependency of the same name is defined; consider removing the `?` or changing the dependency to be optional +") + .run(); +} + +#[cargo_test] +fn optional_cli_syntax() { + // --features bar?/feat + Package::new("bar", "1.0.0") + .feature("feat", &[]) + .file("src/lib.rs", &require(&["feat"], &[])) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", optional = true } + "#, + ) + .file("src/lib.rs", "") + .build(); + + // Does not build bar. + p.cargo("check --features bar?/feat") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 [..] +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + // Builds bar. + p.cargo("check --features bar?/feat,bar") + .with_stderr( + "\ +[CHECKING] bar v1.0.0 +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + eprintln!("check V2 resolver"); + switch_to_resolver_2(&p); + p.build_dir().rm_rf(); + // Does not build bar. + p.cargo("check --features bar?/feat") + .with_stderr( + "\ +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + // Builds bar. + p.cargo("check --features bar?/feat,bar") + .with_stderr( + "\ +[CHECKING] bar v1.0.0 +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn required_features() { + // required-features doesn't allow ? + Package::new("bar", "1.0.0").feature("feat", &[]).publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", optional = true } + + [[bin]] + name = "foo" + required-features = ["bar?/feat"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] +[ERROR] invalid feature `bar?/feat` in required-features of target `foo`: \ +optional dependency with `?` is not allowed in required-features +", + ) + .run(); +} + +#[cargo_test] +fn weak_with_host_decouple() { + // weak-dep-features with new resolver + // + // foo v0.1.0 + // └── common v1.0.0 + // └── bar v1.0.0 <-- does not have `feat` enabled + // [build-dependencies] + // └── bar_activator v1.0.0 + // └── common v1.0.0 + // └── bar v1.0.0 <-- does have `feat` enabled + Package::new("bar", "1.0.0") + .feature("feat", &[]) + .file( + "src/lib.rs", + r#" + pub fn feat() -> bool { + cfg!(feature = "feat") + } + "#, + ) + .publish(); + + Package::new("common", "1.0.0") + .add_dep(Dependency::new("bar", "1.0").optional(true)) + .feature("feat", &["bar?/feat"]) + .file( + "src/lib.rs", + r#" + #[cfg(feature = "bar")] + pub fn feat() -> bool { bar::feat() } + #[cfg(not(feature = "bar"))] + pub fn feat() -> bool { false } + "#, + ) + .publish(); + + Package::new("bar_activator", "1.0.0") + .feature_dep("common", "1.0", &["bar", "feat"]) + .file( + "src/lib.rs", + r#" + pub fn feat() -> bool { + common::feat() + } + "#, + ) + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "2" + + [dependencies] + common = { version = "1.0", features = ["feat"] } + + [build-dependencies] + bar_activator = "1.0" + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + assert!(!common::feat()); + } + "#, + ) + .file( + "build.rs", + r#" + fn main() { + assert!(bar_activator::feat()); + } + "#, + ) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] [..] +[DOWNLOADED] [..] +[DOWNLOADED] [..] +[COMPILING] bar v1.0.0 +[COMPILING] common v1.0.0 +[COMPILING] bar_activator v1.0.0 +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +[RUNNING] `target/debug/foo[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn weak_namespaced() { + // Behavior with a dep: dependency. + Package::new("bar", "1.0.0") + .feature("feat", &[]) + .file("src/lib.rs", &require(&["feat"], &[])) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", optional = true } + + [features] + f1 = ["bar?/feat"] + f2 = ["dep:bar"] + "#, + ) + .file("src/lib.rs", &require(&["f1"], &["f2", "bar"])) + .build(); + + p.cargo("check --features f1") + .with_stderr( + "\ +[UPDATING] [..] +[DOWNLOADING] crates ... +[DOWNLOADED] bar v1.0.0 [..] +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); + + p.cargo("tree -f") + .arg("{p} feats:{f}") + .with_stdout("foo v0.1.0 ([ROOT]/foo) feats:") + .run(); + + p.cargo("tree --features f1 -f") + .arg("{p} feats:{f}") + .with_stdout("foo v0.1.0 ([ROOT]/foo) feats:f1") + .run(); + + p.cargo("tree --features f1,f2 -f") + .arg("{p} feats:{f}") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) feats:f1,f2 +└── bar v1.0.0 feats:feat +", + ) + .run(); + + // "bar" remains not-a-feature + p.change_file("src/lib.rs", &require(&["f1", "f2"], &["bar"])); + + p.cargo("check --features f1,f2") + .with_stderr( + "\ +[CHECKING] bar v1.0.0 +[CHECKING] foo v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn tree() { + Package::new("bar", "1.0.0") + .feature("feat", &[]) + .file("src/lib.rs", &require(&["feat"], &[])) + .publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { version = "1.0", optional = true } + + [features] + f1 = ["bar?/feat"] + "#, + ) + .file("src/lib.rs", &require(&["f1"], &[])) + .build(); + + p.cargo("tree --features f1") + .with_stdout("foo v0.1.0 ([ROOT]/foo)") + .run(); + + p.cargo("tree --features f1,bar") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +└── bar v1.0.0 +", + ) + .run(); + + p.cargo("tree --features f1,bar -e features") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) +└── bar feature \"default\" + └── bar v1.0.0 +", + ) + .run(); + + p.cargo("tree --features f1,bar -e features -i bar") + .with_stdout( + "\ +bar v1.0.0 +├── bar feature \"default\" +│ └── foo v0.1.0 ([ROOT]/foo) +│ ├── foo feature \"bar\" (command-line) +│ ├── foo feature \"default\" (command-line) +│ └── foo feature \"f1\" (command-line) +└── bar feature \"feat\" + └── foo feature \"f1\" (command-line) +", + ) + .run(); + + p.cargo("tree -e features --features bar?/feat") + .with_stdout("foo v0.1.0 ([ROOT]/foo)") + .run(); + + // This is a little strange in that it produces no output. + // Maybe `cargo tree` should print a note about why? + p.cargo("tree -e features -i bar --features bar?/feat") + .with_stdout("") + .run(); + + p.cargo("tree -e features -i bar --features bar?/feat,bar") + .with_stdout( + "\ +bar v1.0.0 +├── bar feature \"default\" +│ └── foo v0.1.0 ([ROOT]/foo) +│ ├── foo feature \"bar\" (command-line) +│ └── foo feature \"default\" (command-line) +└── bar feature \"feat\" (command-line) +", + ) + .run(); +} + +#[cargo_test] +fn publish() { + let registry = RegistryBuilder::new().http_api().http_index().build(); + + // Publish behavior with /? syntax. + Package::new("bar", "1.0.0").feature("feat", &[]).publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + description = "foo" + license = "MIT" + homepage = "https://example.com/" + + [dependencies] + bar = { version = "1.0", optional = true } + + [features] + feat1 = [] + feat2 = ["bar?/feat"] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish") + .replace_crates_io(registry.index_url()) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.1.0 [..] +[VERIFYING] foo v0.1.0 [..] +[UPDATING] [..] +[COMPILING] foo v0.1.0 [..] +[FINISHED] [..] +[PACKAGED] [..] +[UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [ + { + "default_features": true, + "features": [], + "kind": "normal", + "name": "bar", + "optional": true, + "target": null, + "version_req": "^1.0" + } + ], + "description": "foo", + "documentation": null, + "features": { + "feat1": [], + "feat2": ["bar?/feat"] + }, + "homepage": "https://example.com/", + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": null, + "vers": "0.1.0" + } + "#, + "foo-0.1.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[( + "Cargo.toml", + &format!( + r#"{} +[package] +name = "foo" +version = "0.1.0" +description = "foo" +homepage = "https://example.com/" +license = "MIT" + +[dependencies.bar] +version = "1.0" +optional = true + +[features] +feat1 = [] +feat2 = ["bar?/feat"] +"#, + cargo::core::package::MANIFEST_PREAMBLE + ), + )], + ); +} diff --git a/tests/testsuite/workspaces.rs b/tests/testsuite/workspaces.rs new file mode 100644 index 0000000..c6698f7 --- /dev/null +++ b/tests/testsuite/workspaces.rs @@ -0,0 +1,2531 @@ +//! Tests for workspaces. + +use cargo_test_support::registry::Package; +use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project, sleep_ms}; +use std::env; +use std::fs; + +#[cargo_test] +fn simple_explicit() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = ".." + "#, + ) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + assert!(!p.bin("bar").is_file()); + + p.cargo("build").cwd("bar").run(); + assert!(p.bin("foo").is_file()); + assert!(p.bin("bar").is_file()); + + assert!(p.root().join("Cargo.lock").is_file()); + assert!(!p.root().join("bar/Cargo.lock").is_file()); +} + +#[cargo_test] +fn simple_explicit_default_members() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + default-members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = ".." + "#, + ) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("build").run(); + assert!(p.bin("bar").is_file()); + assert!(!p.bin("foo").is_file()); +} + +#[cargo_test] +fn non_virtual_default_members_build_other_member() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = [".", "bar", "baz"] + default-members = ["baz"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check") + .with_stderr( + "[CHECKING] baz v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); + + p.cargo("check --manifest-path bar/Cargo.toml") + .with_stderr( + "[CHECKING] bar v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn non_virtual_default_members_build_root_project() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + default-members = ["."] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("check") + .with_stderr( + "[CHECKING] foo v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn inferred_root() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + assert!(!p.bin("bar").is_file()); + + p.cargo("build").cwd("bar").run(); + assert!(p.bin("foo").is_file()); + assert!(p.bin("bar").is_file()); + + assert!(p.root().join("Cargo.lock").is_file()); + assert!(!p.root().join("bar/Cargo.lock").is_file()); +} + +#[cargo_test] +fn inferred_path_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", ""); + let p = p.build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + assert!(!p.bin("bar").is_file()); + + p.cargo("build").cwd("bar").run(); + assert!(p.bin("foo").is_file()); + assert!(p.bin("bar").is_file()); + + assert!(p.root().join("Cargo.lock").is_file()); + assert!(!p.root().join("bar/Cargo.lock").is_file()); +} + +#[cargo_test] +fn transitive_path_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + baz = { path = "../baz" } + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/main.rs", "fn main() {}") + .file("baz/src/lib.rs", ""); + let p = p.build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + assert!(!p.bin("bar").is_file()); + assert!(!p.bin("baz").is_file()); + + p.cargo("build").cwd("bar").run(); + assert!(p.bin("foo").is_file()); + assert!(p.bin("bar").is_file()); + assert!(!p.bin("baz").is_file()); + + p.cargo("build").cwd("baz").run(); + assert!(p.bin("foo").is_file()); + assert!(p.bin("bar").is_file()); + assert!(p.bin("baz").is_file()); + + assert!(p.root().join("Cargo.lock").is_file()); + assert!(!p.root().join("bar/Cargo.lock").is_file()); + assert!(!p.root().join("baz/Cargo.lock").is_file()); +} + +#[cargo_test] +fn parent_pointer_works() { + let p = project() + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "../bar" } + + [workspace] + "#, + ) + .file("foo/src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = "../foo" + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", ""); + let p = p.build(); + + p.cargo("build").cwd("foo").run(); + p.cargo("build").cwd("bar").run(); + assert!(p.root().join("foo/Cargo.lock").is_file()); + assert!(!p.root().join("bar/Cargo.lock").is_file()); +} + +#[cargo_test] +fn same_names_in_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + workspace = ".." + "#, + ) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: two packages named `foo` in this workspace: +- [..]Cargo.toml +- [..]Cargo.toml +", + ) + .run(); +} + +#[cargo_test] +fn parent_doesnt_point_to_child() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check") + .cwd("bar") + .with_status(101) + .with_stderr( + "\ +error: current package believes it's in a workspace when it's not: +current: [..]Cargo.toml +workspace: [..]Cargo.toml + +this may be fixable [..] +[..] +", + ) + .run(); +} + +#[cargo_test] +fn invalid_parent_pointer() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + workspace = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to read `[..]Cargo.toml` + +Caused by: + [..] +", + ) + .run(); +} + +#[cargo_test] +fn invalid_members() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["foo"] + "#, + ) + .file("src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to load manifest for workspace member `[..]/foo` + +Caused by: + failed to read `[..]foo/foo/Cargo.toml` + +Caused by: + [..] +", + ) + .run(); +} + +#[cargo_test] +fn bare_workspace_ok() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + "#, + ) + .file("src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn two_roots() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [workspace] + members = [".."] + "#, + ) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: multiple workspace roots found in the same workspace: + [..] + [..] +", + ) + .run(); +} + +#[cargo_test] +fn workspace_isnt_root() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + workspace = "bar" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check") + .with_status(101) + .with_stderr("error: root of a workspace inferred but wasn't a root: [..]") + .run(); +} + +#[cargo_test] +fn dangling_member() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = "../baz" + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + authors = [] + workspace = "../baz" + "#, + ) + .file("baz/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: package `[..]` is a member of the wrong workspace +expected: [..] +actual: [..] +", + ) + .run(); +} + +#[cargo_test] +fn cycle() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + workspace = "bar" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = ".." + "#, + ) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "[ERROR] root of a workspace inferred but wasn't a root: [..]/foo/bar/Cargo.toml", + ) + .run(); +} + +#[cargo_test] +fn share_dependencies() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + dep1 = "0.1" + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + dep1 = "< 0.1.5" + "#, + ) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + Package::new("dep1", "0.1.3").publish(); + Package::new("dep1", "0.1.8").publish(); + + p.cargo("check") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] dep1 v0.1.3 ([..]) +[CHECKING] dep1 v0.1.3 +[CHECKING] foo v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn fetch_fetches_all() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + dep1 = "*" + "#, + ) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + Package::new("dep1", "0.1.3").publish(); + + p.cargo("fetch") + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] dep1 v0.1.3 ([..]) +", + ) + .run(); +} + +#[cargo_test] +fn lock_works_for_everyone() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + dep2 = "0.1" + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [dependencies] + dep1 = "0.1" + "#, + ) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + Package::new("dep1", "0.1.0").publish(); + Package::new("dep2", "0.1.0").publish(); + + p.cargo("generate-lockfile") + .with_stderr("[UPDATING] `[..]` index") + .run(); + + Package::new("dep1", "0.1.1").publish(); + Package::new("dep2", "0.1.1").publish(); + + p.cargo("check") + .with_stderr( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] dep2 v0.1.0 ([..]) +[CHECKING] dep2 v0.1.0 +[CHECKING] foo v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.cargo("check") + .cwd("bar") + .with_stderr( + "\ +[DOWNLOADING] crates ... +[DOWNLOADED] dep1 v0.1.0 ([..]) +[CHECKING] dep1 v0.1.0 +[CHECKING] bar v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn virtual_works() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + p.cargo("build").cwd("bar").run(); + assert!(p.root().join("Cargo.lock").is_file()); + assert!(p.bin("bar").is_file()); + assert!(!p.root().join("bar/Cargo.lock").is_file()); +} + +#[cargo_test] +fn explicit_package_argument_works_with_virtual_manifest() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + p.cargo("build --package bar").run(); + assert!(p.root().join("Cargo.lock").is_file()); + assert!(p.bin("bar").is_file()); + assert!(!p.root().join("bar/Cargo.lock").is_file()); +} + +#[cargo_test] +fn virtual_misconfigure() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + p.cargo("check") + .cwd("bar") + .with_status(101) + .with_stderr( + "\ +error: current package believes it's in a workspace when it's not: +current: [CWD]/Cargo.toml +workspace: [..]Cargo.toml + +this may be fixable by adding `bar` to the `workspace.members` array of the \ +manifest located at: [..] +[..] +", + ) + .run(); +} + +#[cargo_test] +fn virtual_build_all_implied() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + p.cargo("check").run(); +} + +#[cargo_test] +fn virtual_default_members() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + default-members = ["bar"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}") + .file("baz/src/main.rs", "fn main() {}"); + let p = p.build(); + p.cargo("build").run(); + assert!(p.bin("bar").is_file()); + assert!(!p.bin("baz").is_file()); +} + +#[cargo_test] +fn virtual_default_member_is_not_a_member() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar"] + default-members = ["something-else"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: package `[..]something-else` is listed in workspace’s default-members \ +but is not a member. +", + ) + .run(); +} + +#[cargo_test] +fn virtual_default_members_build_other_member() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "baz"] + default-members = ["baz"] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("baz/src/lib.rs", "pub fn baz() {}") + .build(); + + p.cargo("check --manifest-path bar/Cargo.toml") + .with_stderr( + "[CHECKING] bar v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n", + ) + .run(); +} + +#[cargo_test] +fn virtual_build_no_members() { + let p = project().file( + "Cargo.toml", + r#" + [workspace] + "#, + ); + let p = p.build(); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: manifest path `[..]` contains no package: The manifest is virtual, \ +and the workspace has no members. +", + ) + .run(); +} + +#[cargo_test] +fn include_virtual() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "") + .file( + "bar/Cargo.toml", + r#" + [workspace] + "#, + ); + let p = p.build(); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: multiple workspace roots found in the same workspace: + [..] + [..] +", + ) + .run(); +} + +#[cargo_test] +fn members_include_path_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["p1"] + + [dependencies] + p3 = { path = "p3" } + "#, + ) + .file("src/lib.rs", "") + .file( + "p1/Cargo.toml", + r#" + [package] + name = "p1" + version = "0.1.0" + authors = [] + + [dependencies] + p2 = { path = "../p2" } + "#, + ) + .file("p1/src/lib.rs", "") + .file("p2/Cargo.toml", &basic_manifest("p2", "0.1.0")) + .file("p2/src/lib.rs", "") + .file("p3/Cargo.toml", &basic_manifest("p3", "0.1.0")) + .file("p3/src/lib.rs", ""); + let p = p.build(); + + p.cargo("check").cwd("p1").run(); + p.cargo("check").cwd("p2").run(); + p.cargo("check").cwd("p3").run(); + p.cargo("check").run(); + + assert!(p.root().join("target").is_dir()); + assert!(!p.root().join("p1/target").is_dir()); + assert!(!p.root().join("p2/target").is_dir()); + assert!(!p.root().join("p3/target").is_dir()); +} + +#[cargo_test] +fn new_warns_you_this_will_not_work() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + "#, + ) + .file("src/lib.rs", ""); + let p = p.build(); + + p.cargo("new --lib bar") + .with_stderr( + "\ +warning: compiling this new package may not work due to invalid workspace configuration + +current package believes it's in a workspace when it's not: +current: [..] +workspace: [..] + +this may be fixable by ensuring that this crate is depended on by the workspace \ +root: [..] +[..] +[CREATED] library `bar` package +", + ) + .run(); +} + +#[cargo_test] +fn new_warning_with_corrupt_ws() { + let p = project().file("Cargo.toml", "asdf").build(); + p.cargo("new bar") + .with_stderr( + "\ +[WARNING] compiling this new package may not work due to invalid workspace configuration + +failed to parse manifest at `[..]foo/Cargo.toml` + +Caused by: + could not parse input as TOML + +Caused by: + TOML parse error at line 1, column 5 + | + 1 | asdf + | ^ + expected `.`, `=` + Created binary (application) `bar` package +", + ) + .run(); +} + +#[cargo_test] +fn lock_doesnt_change_depending_on_crate() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ['baz'] + + [dependencies] + foo = "*" + "#, + ) + .file("src/lib.rs", "") + .file( + "baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + authors = [] + + [dependencies] + bar = "*" + "#, + ) + .file("baz/src/lib.rs", ""); + let p = p.build(); + + Package::new("foo", "1.0.0").publish(); + Package::new("bar", "1.0.0").publish(); + + p.cargo("check").run(); + + let lockfile = p.read_lockfile(); + + p.cargo("check").cwd("baz").run(); + + let lockfile2 = p.read_lockfile(); + + assert_eq!(lockfile, lockfile2); +} + +#[cargo_test] +fn rebuild_please() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ['lib', 'bin'] + "#, + ) + .file("lib/Cargo.toml", &basic_manifest("lib", "0.1.0")) + .file( + "lib/src/lib.rs", + r#" + pub fn foo() -> u32 { 0 } + "#, + ) + .file( + "bin/Cargo.toml", + r#" + [package] + name = "bin" + version = "0.1.0" + + [dependencies] + lib = { path = "../lib" } + "#, + ) + .file( + "bin/src/main.rs", + r#" + extern crate lib; + + fn main() { + assert_eq!(lib::foo(), 0); + } + "#, + ); + let p = p.build(); + + p.cargo("run").cwd("bin").run(); + + sleep_ms(1000); + + p.change_file("lib/src/lib.rs", "pub fn foo() -> u32 { 1 }"); + + p.cargo("build").cwd("lib").run(); + + p.cargo("run") + .cwd("bin") + .with_status(101) + .with_stderr_contains("[..]assertion[..]") + .run(); +} + +#[cargo_test] +fn workspace_in_git() { + let git_project = git::new("dep1", |project| { + project + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + }); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "lib" + version = "0.1.0" + + [dependencies.foo] + git = '{}' + "#, + git_project.url() + ), + ) + .file( + "src/lib.rs", + r#" + pub fn foo() -> u32 { 0 } + "#, + ); + let p = p.build(); + + p.cargo("check").run(); +} + +#[cargo_test] +fn lockfile_can_specify_nonexistent_members() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/main.rs", "fn main() {}") + .file( + "Cargo.lock", + r#" + [[package]] + name = "a" + version = "0.1.0" + + [[package]] + name = "b" + version = "0.1.0" + "#, + ); + + let p = p.build(); + + p.cargo("check").cwd("a").run(); +} + +#[cargo_test] +fn you_cannot_generate_lockfile_for_empty_workspaces() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + "#, + ) + .file("bar/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("update") + .with_status(101) + .with_stderr("error: you can't generate a lockfile for an empty workspace.") + .run(); +} + +#[cargo_test] +fn workspace_with_transitive_dev_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = ["mbrubeck@example.com"] + + [dependencies.bar] + path = "bar" + + [workspace] + "#, + ) + .file("src/main.rs", r#"fn main() {}"#) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + authors = ["mbrubeck@example.com"] + + [dev-dependencies.baz] + path = "../baz" + "#, + ) + .file( + "bar/src/lib.rs", + r#" + pub fn init() {} + + #[cfg(test)] + + #[test] + fn test() { + extern crate baz; + baz::do_stuff(); + } + "#, + ) + .file("baz/Cargo.toml", &basic_manifest("baz", "0.5.0")) + .file("baz/src/lib.rs", r#"pub fn do_stuff() {}"#); + let p = p.build(); + + p.cargo("test -p bar").run(); +} + +#[cargo_test] +fn error_if_parent_cargo_toml_is_invalid() { + let p = project() + .file("Cargo.toml", "Totally not a TOML file") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check") + .cwd("bar") + .with_status(101) + .with_stderr_contains("[ERROR] failed to parse manifest at `[..]`") + .run(); +} + +#[cargo_test] +fn relative_path_for_member_works() { + let p = project() + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["../bar"] + "#, + ) + .file("foo/src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = "../foo" + "#, + ) + .file("bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check").cwd("foo").run(); + p.cargo("check").cwd("bar").run(); +} + +#[cargo_test] +fn relative_path_for_root_works() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + + [dependencies] + subproj = { path = "./subproj" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("subproj/Cargo.toml", &basic_manifest("subproj", "0.1.0")) + .file("subproj/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check --manifest-path ./Cargo.toml").run(); + + p.cargo("check --manifest-path ../Cargo.toml") + .cwd("subproj") + .run(); +} + +#[cargo_test] +fn path_dep_outside_workspace_is_not_member() { + let p = project() + .no_manifest() + .file( + "ws/Cargo.toml", + r#" + [package] + name = "ws" + version = "0.1.0" + authors = [] + + [dependencies] + foo = { path = "../foo" } + + [workspace] + "#, + ) + .file("ws/src/lib.rs", "extern crate foo;") + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", ""); + let p = p.build(); + + p.cargo("check").cwd("ws").run(); +} + +#[cargo_test] +fn test_in_and_out_of_workspace() { + let p = project() + .no_manifest() + .file( + "ws/Cargo.toml", + r#" + [package] + name = "ws" + version = "0.1.0" + authors = [] + + [dependencies] + foo = { path = "../foo" } + + [workspace] + members = [ "../bar" ] + "#, + ) + .file("ws/src/lib.rs", "extern crate foo; pub fn f() { foo::f() }") + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "../bar" } + "#, + ) + .file( + "foo/src/lib.rs", + "extern crate bar; pub fn f() { bar::f() }", + ) + .file( + "bar/Cargo.toml", + r#" + [package] + workspace = "../ws" + name = "bar" + version = "0.1.0" + authors = [] + "#, + ) + .file("bar/src/lib.rs", "pub fn f() { }"); + let p = p.build(); + + p.cargo("check").cwd("ws").run(); + + assert!(p.root().join("ws/Cargo.lock").is_file()); + assert!(p.root().join("ws/target").is_dir()); + assert!(!p.root().join("foo/Cargo.lock").is_file()); + assert!(!p.root().join("foo/target").is_dir()); + assert!(!p.root().join("bar/Cargo.lock").is_file()); + assert!(!p.root().join("bar/target").is_dir()); + + p.cargo("check").cwd("foo").run(); + assert!(p.root().join("foo/Cargo.lock").is_file()); + assert!(p.root().join("foo/target").is_dir()); + assert!(!p.root().join("bar/Cargo.lock").is_file()); + assert!(!p.root().join("bar/target").is_dir()); +} + +#[cargo_test] +fn test_path_dependency_under_member() { + let p = project() + .file( + "ws/Cargo.toml", + r#" + [package] + name = "ws" + version = "0.1.0" + authors = [] + + [dependencies] + foo = { path = "../foo" } + + [workspace] + "#, + ) + .file("ws/src/lib.rs", "extern crate foo; pub fn f() { foo::f() }") + .file( + "foo/Cargo.toml", + r#" + [package] + workspace = "../ws" + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "./bar" } + "#, + ) + .file( + "foo/src/lib.rs", + "extern crate bar; pub fn f() { bar::f() }", + ) + .file("foo/bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("foo/bar/src/lib.rs", "pub fn f() { }"); + let p = p.build(); + + p.cargo("check").cwd("ws").run(); + + assert!(!p.root().join("foo/bar/Cargo.lock").is_file()); + assert!(!p.root().join("foo/bar/target").is_dir()); + + p.cargo("check").cwd("foo/bar").run(); + + assert!(!p.root().join("foo/bar/Cargo.lock").is_file()); + assert!(!p.root().join("foo/bar/target").is_dir()); +} + +#[cargo_test] +fn excluded_simple() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "ws" + version = "0.1.0" + authors = [] + + [workspace] + exclude = ["foo"] + "#, + ) + .file("src/lib.rs", "") + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", ""); + let p = p.build(); + + p.cargo("check").run(); + assert!(p.root().join("target").is_dir()); + p.cargo("check").cwd("foo").run(); + assert!(p.root().join("foo/target").is_dir()); +} + +#[cargo_test] +fn exclude_members_preferred() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "ws" + version = "0.1.0" + authors = [] + + [workspace] + members = ["foo/bar"] + exclude = ["foo"] + "#, + ) + .file("src/lib.rs", "") + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .file("foo/bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("foo/bar/src/lib.rs", ""); + let p = p.build(); + + p.cargo("check").run(); + assert!(p.root().join("target").is_dir()); + p.cargo("check").cwd("foo").run(); + assert!(p.root().join("foo/target").is_dir()); + p.cargo("check").cwd("foo/bar").run(); + assert!(!p.root().join("foo/bar/target").is_dir()); +} + +#[cargo_test] +fn exclude_but_also_depend() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "ws" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "foo/bar" } + + [workspace] + exclude = ["foo"] + "#, + ) + .file("src/lib.rs", "") + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .file("foo/bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("foo/bar/src/lib.rs", ""); + let p = p.build(); + + p.cargo("check").run(); + assert!(p.root().join("target").is_dir()); + p.cargo("check").cwd("foo").run(); + assert!(p.root().join("foo/target").is_dir()); + p.cargo("check").cwd("foo/bar").run(); + assert!(p.root().join("foo/bar/target").is_dir()); +} + +#[cargo_test] +fn excluded_default_members_still_must_be_members() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + default-members = ["foo", "bar"] + exclude = ["bar"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .file("bar/something.txt", ""); + let p = p.build(); + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: package `[..]bar` is listed in workspace’s default-members \ +but is not a member. +", + ) + .run(); +} + +#[cargo_test] +fn excluded_default_members_crate_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar/*"] + default-members = ["bar/*"] + exclude = ["bar/quux"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/main.rs", "fn main() {}") + .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("bar/baz/src/main.rs", "fn main() {}") + .file("bar/quux/Cargo.toml", &basic_manifest("quux", "0.1.0")) + .file("bar/quux/src/main.rs", "fn main() {}"); + + let p = p.build(); + p.cargo("build").run(); + + assert!(p.root().join("target").is_dir()); + assert!(!p.bin("foo").is_file()); + assert!(p.bin("baz").is_file()); + assert!(!p.bin("quux").exists()); + + p.cargo("build --workspace").run(); + assert!(p.root().join("target").is_dir()); + assert!(p.bin("foo").is_file()); + assert!(!p.bin("quux").exists()); + + p.cargo("build").cwd("bar/quux").run(); + assert!(p.root().join("bar/quux/target").is_dir()); +} + +#[cargo_test] +fn excluded_default_members_not_crate_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar/*"] + default-members = ["bar/*"] + exclude = ["bar/docs"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/main.rs", "fn main() {}") + .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("bar/baz/src/main.rs", "fn main() {}") + .file("bar/docs/readme.txt", "This folder is not a crate!"); + + let p = p.build(); + p.cargo("build").run(); + + assert!(!p.bin("foo").is_file()); + assert!(p.bin("baz").is_file()); + p.cargo("build --workspace").run(); + assert!(p.bin("foo").is_file()); +} + +#[cargo_test] +fn glob_syntax() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["crates/*"] + exclude = ["crates/qux"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "crates/bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = "../.." + "#, + ) + .file("crates/bar/src/main.rs", "fn main() {}") + .file( + "crates/baz/Cargo.toml", + r#" + [package] + name = "baz" + version = "0.1.0" + authors = [] + workspace = "../.." + "#, + ) + .file("crates/baz/src/main.rs", "fn main() {}") + .file( + "crates/qux/Cargo.toml", + r#" + [package] + name = "qux" + version = "0.1.0" + authors = [] + "#, + ) + .file("crates/qux/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + assert!(!p.bin("bar").is_file()); + assert!(!p.bin("baz").is_file()); + + p.cargo("build").cwd("crates/bar").run(); + assert!(p.bin("foo").is_file()); + assert!(p.bin("bar").is_file()); + + p.cargo("build").cwd("crates/baz").run(); + assert!(p.bin("foo").is_file()); + assert!(p.bin("baz").is_file()); + + p.cargo("build").cwd("crates/qux").run(); + assert!(!p.bin("qux").is_file()); + + assert!(p.root().join("Cargo.lock").is_file()); + assert!(!p.root().join("crates/bar/Cargo.lock").is_file()); + assert!(!p.root().join("crates/baz/Cargo.lock").is_file()); + assert!(p.root().join("crates/qux/Cargo.lock").is_file()); +} + +/*FIXME: This fails because of how workspace.exclude and workspace.members are working. +#[cargo_test] +fn glob_syntax_2() { + let p = project() + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["crates/b*"] + exclude = ["crates/q*"] + "#) + .file("src/main.rs", "fn main() {}") + .file("crates/bar/Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = "../.." + "#) + .file("crates/bar/src/main.rs", "fn main() {}") + .file("crates/baz/Cargo.toml", r#" + [package] + name = "baz" + version = "0.1.0" + authors = [] + workspace = "../.." + "#) + .file("crates/baz/src/main.rs", "fn main() {}") + .file("crates/qux/Cargo.toml", r#" + [package] + name = "qux" + version = "0.1.0" + authors = [] + "#) + .file("crates/qux/src/main.rs", "fn main() {}"); + p.build(); + + p.cargo("build").run(); + assert!(p.bin("foo").is_file()); + assert!(!p.bin("bar").is_file()); + assert!(!p.bin("baz").is_file()); + + p.cargo("build").cwd("crates/bar").run(); + assert!(p.bin("foo").is_file()); + assert!(p.bin("bar").is_file()); + + p.cargo("build").cwd("crates/baz").run(); + assert!(p.bin("foo").is_file()); + assert!(p.bin("baz").is_file()); + + p.cargo("build").cwd("crates/qux").run(); + assert!(!p.bin("qux").is_file()); + + assert!(p.root().join("Cargo.lock").is_file()); + assert!(!p.root().join("crates/bar/Cargo.lock").is_file()); + assert!(!p.root().join("crates/baz/Cargo.lock").is_file()); + assert!(p.root().join("crates/qux/Cargo.lock").is_file()); +} +*/ + +#[cargo_test] +fn glob_syntax_invalid_members() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["crates/*"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("crates/bar/src/main.rs", "fn main() {}"); + let p = p.build(); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to load manifest for workspace member `[..]/crates/bar` + +Caused by: + failed to read `[..]foo/crates/bar/Cargo.toml` + +Caused by: + [..] +", + ) + .run(); +} + +/// This is a freshness test for feature use with workspaces. +/// +/// `feat_lib` is used by `caller1` and `caller2`, but with different features enabled. +/// This test ensures that alternating building `caller1`, `caller2` doesn't force +/// recompile of `feat_lib`. +/// +/// Ideally, once we solve rust-lang/cargo#3620, then a single Cargo build at the top level +/// will be enough. +#[cargo_test] +fn dep_used_with_separate_features() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["feat_lib", "caller1", "caller2"] + "#, + ) + .file( + "feat_lib/Cargo.toml", + r#" + [package] + name = "feat_lib" + version = "0.1.0" + authors = [] + + [features] + myfeature = [] + "#, + ) + .file("feat_lib/src/lib.rs", "") + .file( + "caller1/Cargo.toml", + r#" + [package] + name = "caller1" + version = "0.1.0" + authors = [] + + [dependencies] + feat_lib = { path = "../feat_lib" } + "#, + ) + .file("caller1/src/main.rs", "fn main() {}") + .file("caller1/src/lib.rs", "") + .file( + "caller2/Cargo.toml", + r#" + [package] + name = "caller2" + version = "0.1.0" + authors = [] + + [dependencies] + feat_lib = { path = "../feat_lib", features = ["myfeature"] } + caller1 = { path = "../caller1" } + "#, + ) + .file("caller2/src/main.rs", "fn main() {}") + .file("caller2/src/lib.rs", ""); + let p = p.build(); + + // Build the entire workspace. + p.cargo("build --workspace") + .with_stderr( + "\ +[..]Compiling feat_lib v0.1.0 ([..]) +[..]Compiling caller1 v0.1.0 ([..]) +[..]Compiling caller2 v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + assert!(p.bin("caller1").is_file()); + assert!(p.bin("caller2").is_file()); + + // Build `caller1`. Should build the dep library. Because the features + // are different than the full workspace, it rebuilds. + // Ideally once we solve rust-lang/cargo#3620, then a single Cargo build at the top level + // will be enough. + p.cargo("build") + .cwd("caller1") + .with_stderr( + "\ +[..]Compiling feat_lib v0.1.0 ([..]) +[..]Compiling caller1 v0.1.0 ([..]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + // Alternate building `caller2`/`caller1` a few times, just to make sure + // features are being built separately. Should not rebuild anything. + p.cargo("build") + .cwd("caller2") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); + p.cargo("build") + .cwd("caller1") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); + p.cargo("build") + .cwd("caller2") + .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]") + .run(); +} + +#[cargo_test] +fn dont_recurse_out_of_cargo_home() { + let git_project = git::new("dep", |project| { + project + .file("Cargo.toml", &basic_manifest("dep", "0.1.0")) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + use std::env; + use std::path::Path; + use std::process::{self, Command}; + + fn main() { + let cargo = env::var_os("CARGO").unwrap(); + let cargo_manifest_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap(); + let output = Command::new(cargo) + .args(&["metadata", "--format-version", "1", "--manifest-path"]) + .arg(&Path::new(&cargo_manifest_dir).join("Cargo.toml")) + .output() + .unwrap(); + if !output.status.success() { + eprintln!("{}", String::from_utf8(output.stderr).unwrap()); + process::exit(1); + } + } + "#, + ) + }); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies.dep] + git = "{}" + + [workspace] + "#, + git_project.url() + ), + ) + .file("src/lib.rs", ""); + let p = p.build(); + + p.cargo("check") + .env("CARGO_HOME", p.root().join(".cargo")) + .run(); +} + +// FIXME: this fails because of how workspace.exclude and workspace.members are working. +/* +#[cargo_test] +fn include_and_exclude() { + let p = project() + .file("Cargo.toml", r#" + [workspace] + members = ["foo"] + exclude = ["foo/bar"] + "#) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .file("foo/bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("foo/bar/src/lib.rs", ""); + p.build(); + + p.cargo("build").cwd("foo").run(); + assert!(p.root().join("target").is_dir()); + assert!(!p.root().join("foo/target").is_dir()); + p.cargo("build").cwd("foo/bar").run(); + assert!(p.root().join("foo/bar/target").is_dir()); +} +*/ + +#[cargo_test] +fn cargo_home_at_root_works() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["a"] + "#, + ) + .file("src/lib.rs", "") + .file("a/Cargo.toml", &basic_manifest("a", "0.1.0")) + .file("a/src/lib.rs", ""); + let p = p.build(); + + p.cargo("check").run(); + p.cargo("check --frozen").env("CARGO_HOME", p.root()).run(); +} + +#[cargo_test] +fn relative_rustc() { + let p = project() + .file( + "src/main.rs", + r#" + use std::process::Command; + use std::env; + + fn main() { + let mut cmd = Command::new("rustc"); + for arg in env::args_os().skip(1) { + cmd.arg(arg); + } + std::process::exit(cmd.status().unwrap().code().unwrap()); + } + "#, + ) + .build(); + p.cargo("build").run(); + + let src = p + .root() + .join("target/debug/foo") + .with_extension(env::consts::EXE_EXTENSION); + + Package::new("a", "0.1.0").publish(); + + let p = project() + .at("lib") + .file( + "Cargo.toml", + r#" + [package] + name = "lib" + version = "0.1.0" + + [dependencies] + a = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + fs::copy(&src, p.root().join(src.file_name().unwrap())).unwrap(); + + let file = format!("./foo{}", env::consts::EXE_SUFFIX); + p.cargo("build").env("RUSTC", &file).run(); +} + +#[cargo_test] +fn ws_rustc_err() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + "#, + ) + .file("a/Cargo.toml", &basic_lib_manifest("a")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("rustc") + .with_status(101) + .with_stderr("[ERROR] [..]against an actual package[..]") + .run(); + + p.cargo("rustdoc") + .with_status(101) + .with_stderr("[ERROR] [..]against an actual package[..]") + .run(); +} + +#[cargo_test] +fn ws_err_unused() { + for key in &[ + "[lib]", + "[[bin]]", + "[[example]]", + "[[test]]", + "[[bench]]", + "[dependencies]", + "[dev-dependencies]", + "[build-dependencies]", + "[features]", + "[target]", + "[badges]", + ] { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [workspace] + members = ["a"] + + {} + "#, + key + ), + ) + .file("a/Cargo.toml", &basic_lib_manifest("a")) + .file("a/src/lib.rs", "") + .build(); + p.cargo("check") + .with_status(101) + .with_stderr(&format!( + "\ +[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + this virtual manifest specifies a {} section, which is not allowed +", + key + )) + .run(); + } +} + +#[cargo_test] +fn ws_warn_unused() { + for (key, name) in &[ + ("[profile.dev]\nopt-level = 1", "profiles"), + ("[replace]\n\"bar:0.1.0\" = { path = \"bar\" }", "replace"), + ("[patch.crates-io]\nbar = { path = \"bar\" }", "patch"), + ] { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + "#, + ) + .file( + "a/Cargo.toml", + &format!( + r#" + [package] + name = "a" + version = "0.1.0" + + {} + "#, + key + ), + ) + .file("a/src/lib.rs", "") + .build(); + p.cargo("check") + .with_stderr_contains(&format!( + "\ +[WARNING] {} for the non root package will be ignored, specify {} at the workspace root: +package: [..]/foo/a/Cargo.toml +workspace: [..]/foo/Cargo.toml +", + name, name + )) + .run(); + } +} + +#[cargo_test] +fn ws_warn_path() { + // Warnings include path to manifest. + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + cargo-features = ["edition"] + [package] + name = "foo" + version = "0.1.0" + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr_contains("[WARNING] [..]/foo/a/Cargo.toml: the cargo feature `edition`[..]") + .run(); +} + +#[cargo_test] +fn invalid_missing() { + // Make sure errors are not suppressed with -q. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + x = { path = 'x' } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -q") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to get `x` as a dependency of package `foo v0.1.0 [..]` + +Caused by: + failed to load source for dependency `x` + +Caused by: + Unable to update [..]/foo/x + +Caused by: + failed to read `[..]foo/x/Cargo.toml` + +Caused by: + [..] +", + ) + .run(); +} + +#[cargo_test] +fn member_dep_missing() { + // Make sure errors are not suppressed with -q. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + baz = { path = "baz" } + "#, + ) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -q") + .with_status(101) + .with_stderr( + "\ +[ERROR] failed to load manifest for workspace member `[..]/bar` + +Caused by: + failed to load manifest for dependency `baz` + +Caused by: + failed to read `[..]foo/bar/baz/Cargo.toml` + +Caused by: + [..] +", + ) + .run(); +} + +#[cargo_test] +fn simple_primary_package_env_var() { + let is_primary_package = r#" + #[test] + fn verify_primary_package() {{ + assert!(option_env!("CARGO_PRIMARY_PACKAGE").is_some()); + }} + "#; + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["bar"] + "#, + ) + .file("src/lib.rs", is_primary_package) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + workspace = ".." + "#, + ) + .file("bar/src/lib.rs", is_primary_package); + let p = p.build(); + + p.cargo("test").run(); + + // Again, this time selecting a specific crate + p.cargo("clean").run(); + p.cargo("test -p bar").run(); + + // Again, this time selecting all crates + p.cargo("clean").run(); + p.cargo("test --all").run(); +} + +#[cargo_test] +fn virtual_primary_package_env_var() { + let is_primary_package = r#" + #[test] + fn verify_primary_package() {{ + assert!(option_env!("CARGO_PRIMARY_PACKAGE").is_some()); + }} + "#; + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", is_primary_package) + .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0")) + .file("bar/src/lib.rs", is_primary_package); + let p = p.build(); + + p.cargo("test").run(); + + // Again, this time selecting a specific crate + p.cargo("clean").run(); + p.cargo("test -p foo").run(); +} + +#[cargo_test] +fn ensure_correct_workspace_when_nested() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#, + ) + .file("src/lib.rs", "") + .file( + "sub/Cargo.toml", + r#" + [workspace] + members = ["foo"] + "#, + ) + .file( + "sub/foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "../.."} + "#, + ) + .file("sub/foo/src/main.rs", "fn main() {}"); + let p = p.build(); + p.cargo("tree") + .cwd("sub/foo") + .with_stdout( + "\ +foo v0.1.0 ([..]/foo/sub/foo) +└── bar v0.1.0 ([..]/foo)\ + ", + ) + .run(); +} diff --git a/tests/testsuite/yank.rs b/tests/testsuite/yank.rs new file mode 100644 index 0000000..684a045 --- /dev/null +++ b/tests/testsuite/yank.rs @@ -0,0 +1,202 @@ +//! Tests for the `cargo yank` command. + +use std::fs; + +use cargo_test_support::paths::CargoPathExt; +use cargo_test_support::project; +use cargo_test_support::registry; + +fn setup(name: &str, version: &str) { + let dir = registry::api_path().join(format!("api/v1/crates/{}/{}", name, version)); + dir.mkdir_p(); + fs::write(dir.join("yank"), r#"{"ok": true}"#).unwrap(); +} + +#[cargo_test] +fn explicit_version() { + let registry = registry::init(); + setup("foo", "0.0.1"); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("yank --version 0.0.1") + .replace_crates_io(registry.index_url()) + .run(); + + p.cargo("yank --undo --version 0.0.1") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + " Updating crates.io index + Unyank foo@0.0.1 +error: failed to undo a yank from the registry at file:///[..] + +Caused by: + EOF while parsing a value at line 1 column 0", + ) + .run(); +} + +#[cargo_test] +fn explicit_version_with_asymmetric() { + let registry = registry::RegistryBuilder::new() + .http_api() + .token(cargo_test_support::registry::Token::rfc_key()) + .build(); + setup("foo", "0.0.1"); + + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + // The http_api server will check that the authorization is correct. + // If the authorization was not sent then we would get an unauthorized error. + p.cargo("yank --version 0.0.1") + .arg("-Zregistry-auth") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(registry.index_url()) + .run(); + + p.cargo("yank --undo --version 0.0.1") + .arg("-Zregistry-auth") + .masquerade_as_nightly_cargo(&["registry-auth"]) + .replace_crates_io(registry.index_url()) + .run(); +} + +#[cargo_test] +fn inline_version() { + let registry = registry::init(); + setup("foo", "0.0.1"); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("yank foo@0.0.1") + .replace_crates_io(registry.index_url()) + .run(); + + p.cargo("yank --undo foo@0.0.1") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr( + " Updating crates.io index + Unyank foo@0.0.1 +error: failed to undo a yank from the registry at file:///[..] + +Caused by: + EOF while parsing a value at line 1 column 0", + ) + .run(); +} + +#[cargo_test] +fn version_required() { + setup("foo", "0.0.1"); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("yank foo") + .with_status(101) + .with_stderr("error: `--version` is required") + .run(); +} + +#[cargo_test] +fn inline_version_without_name() { + setup("foo", "0.0.1"); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("yank @0.0.1") + .with_status(101) + .with_stderr("error: missing crate name for `@0.0.1`") + .run(); +} + +#[cargo_test] +fn inline_and_explicit_version() { + setup("foo", "0.0.1"); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("yank foo@0.0.1 --version 0.0.1") + .with_status(101) + .with_stderr("error: cannot specify both `@0.0.1` and `--version`") + .run(); +} -- cgit v1.2.3