Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix duplicated PATH entries #2849

Merged
merged 4 commits into from
Jan 20, 2023
Merged

Conversation

chansuke
Copy link
Contributor

Fixes #2848

@rbtcollins
Copy link
Contributor

This is going to need a test case, I suggest a unit test of some sort close to the code, since the external contract is only changing in the manner of the duplication, and getting at this will be a little tricky.

@chansuke
Copy link
Contributor Author

@rbtcollins Thanks for your comment. I will add some unit test.

@chansuke
Copy link
Contributor Author

I am trying to add a unit test in tests/cli-misc.rs but it gives panicked at 'No process instance' error. How can I get the result of cmd.env(name, new_value); in the test case?

    let a = OsString::from("/home/a/.cargo/bin");
    let path_a = PathBuf::from(a);
    let b = OsString::from("/home/b/.cargo/bin");
    let path_b = PathBuf::from(b);
    let c = OsString::from("/home/c/.cargo/bin");
    let path_c = PathBuf::from(c);

    let path_entries = vec![path_a, path_b, path_c];
    let mut cmd = Command::new("test");
    // add duplicated path
    env_var::prepend_path("/home/c/.cargo/bin", path_entries, &mut cmd);
    // trying to get the result of `prepend_path()` with `get_envs()` because `prepend_path` itself doesn't return any value.
    let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect();
    // gives `panicked at 'No process instance'` error
    assert_eq!(envs, &[(OsStr::new("wip"), Some(OsStr::new("wip")))]);

@kinnison
Copy link
Contributor

Unit tests belong in the crate, not in tests/ which is where we have our integration tests.
There is the TestProcess struct in the currentprocess module which is provided for this purpose. You can see its use in a number of unit tests throughout the src/ tree. It should be fairly easy to deduce how to construct an appropriate test, but if you can't, then I'm sure Robert (or I) can help further.

@chansuke
Copy link
Contributor Author

I had a wrong understanding. I will try to fix it. Thanks so much for your comment.

@chansuke
Copy link
Contributor Author

I'm using cmd.get_envs(), an unstable function to check the result of prepend_path, but it fails clippy check on CI. Is there any workaround for this?

src/env_var.rs Outdated
@@ -27,6 +27,8 @@ pub fn prepend_path(name: &str, value: Vec<PathBuf>, cmd: &mut Command) {
if let Some(ref v) = old_value {
parts = value;
parts.extend(env::split_paths(v).collect::<Vec<_>>());
parts.sort();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorted PATH entries changes the semantic meaning of them: they are not a set, they are a sorted list, because 'bash' in a lower-index entry wins over 'bash' in a higher index entry.

src/env_var.rs Outdated

prepend_path("PATH", path_entries, &mut cmd);

let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one way you could avoid using this unstable method is to have the code you're testing depend on a trait defined in this module.

Implement it once for Command, and once for a trivial test struct (or use mockall), and then you can assert on what the set env is, rather than interacting with Command at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to reimplement the unit test but I'm stuck on this part. Which trait should I depend on?

a trait defined in this module.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define one:

trait ProcessEnvs {
    type Item;
    type Iter: Iterator;
    fn get_envs(&self) -> Self::Iter<Item=Self::Item>;
    fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
where
    K: AsRef<OsStr>,
    V: AsRef<OsStr>;
}

And then in your test you can just use a mock (e.g. with mockall) or a manually constructed test double. Change prepend/append _path to be generic over that trait, and provide an impl for Process.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub fn append_path(
    name: &str,
    value: Vec<PathBuf>,
    cmd: &mut dyn ProcessEnvs<Item = Command, Iter = vec::IntoIter<Command>>,
) {

I am stack on the trait ProcessEnvs cannot be made into an object error. I tried to separate the trait but it didn't work. This is the latest commit.

@rbtcollins
Copy link
Contributor

I think this will work but I have a couple of suggestions:

  • append_path should be changed likewise to prevent a regression in future.
  • value is itself a vec but not deduped
  • perhaps a helper function that takes two vecs and returns a unified vec taken in-order from vec 1 and vec 2 and with all duplicates not added to the output.

@chansuke
Copy link
Contributor Author

@rbtcollins Thanks so much for your suggestion. I will fix this.

@chansuke
Copy link
Contributor Author

chansuke commented Dec 1, 2021

Thanks for the support. I am stuck for about a month and would like to close it.

@chansuke chansuke closed this Dec 1, 2021
@rbtcollins
Copy link
Contributor

Sorry I didn't realise you're stuck. https://internals.rust-lang.org/t/generic-methods-over-object-safe-traits/6774 is the thing to read to understand this - and the reason you needed trait objects is because you didn't make prepend_path generic over an implementation of ProcessEnvs. I've got a fix - should be pushed up in a minute.

However the patch fails tests now :)

@rbtcollins rbtcollins reopened this Dec 1, 2021
@rbtcollins rbtcollins force-pushed the issue-2848 branch 5 times, most recently from bc6547a to 34b6878 Compare December 1, 2021 20:49
@rbtcollins
Copy link
Contributor

I've also rebased all the incremental commits together - there isn't enough here for a large commit history.
@chansuke do you want to finish this off, or should someone else do that?

@dmartin
Copy link
Contributor

dmartin commented Feb 7, 2022

@chansuke @rbtcollins Hi, I don't have much experience with rustup's internals nor the time to dig too deeply into them right now, but I have been affected by PyO3/pyo3#1708 and would like to help push this across the finish line if possible.

If the only thing blocking this PR from being merged is the failing test on Windows, I submitted a PR with a fix here (though I'm not sure whether it's considered okay to call .unwrap() as I did within tests). Let me know if I can do anything else to help get this merged in!

@chansuke
Copy link
Contributor Author

chansuke commented Feb 7, 2022

@rbtcollins @dmartin
sorry for the late reply I'd like to finish this.

@gussmith23
Copy link

Wondering what the status of this is -- seems to be blocking PyO3/pyo3#1708 (among other things)

@chansuke
Copy link
Contributor Author

chansuke commented Apr 5, 2022

I have requested the review to @rbtcollins

@klimburg
Copy link

klimburg commented Jan 6, 2023

I too have come here from PyO3/pyo3#1708

@rbtcollins / @chansuke what is needed to push this to completion? Is there someone else who can review?

chansuke and others added 4 commits January 21, 2023 02:30
- Unit test append_path and prepend_path
- Order is preserved, second and above instances of a given entry are
  dropped.

Co-Authored-By: Robert Collins <robertc@robertcollins.net>
Signed-off-by: Robert Collins <robertc@robertcollins.net>
Copy link
Contributor

@rbtcollins rbtcollins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow I had forgotten this. thank you. I'm sure we could code golf it more but the essentials are fine:

  • it fixes the bug
  • it has a test to prevent accidental recurrence

@rbtcollins
Copy link
Contributor

I'm quite confident the android failure is unrelated; will merge this.

@rbtcollins rbtcollins merged commit 46973d6 into rust-lang:master Jan 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deduplicate PATH entries added during build (e.g. ~/.cargo/bin)
6 participants