Snap for 10453563 from 878f532dd57bdcbce44364b7eecb1811140e99b7 to mainline-permission-release

Change-Id: Ib03f886e2a58a8d8597759c763b1af608410be99
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 692ca11..0dcc2ff 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "8fbe34239c16af9cd253e36e9c2d3384f9b55f83"
+    "sha1": "13722854453a50002d72faa1d0960f70b68ceea4"
   },
   "path_in_vcs": ""
 }
\ No newline at end of file
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index dd380d3..aa13ec5 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -28,7 +28,7 @@
     name: Clippy [Linter]
     strategy:
       matrix:
-        os: [ubuntu-latest]
+        os: [ubuntu-latest, windows-latest, macos-latest]
     runs-on: ${{ matrix.os }}
     steps:
       - name: Setup | Checkout
@@ -46,7 +46,7 @@
         uses: actions-rs/cargo@v1
         with:
           command: clippy
-          args: --workspace --all-targets --all-features
+          args: --workspace --all-targets --all-features -- -Dwarnings
 
   # Ensure that the project could be successfully compiled
   cargo_check:
@@ -75,7 +75,7 @@
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest]
+        os: [ubuntu-latest, windows-latest, macos-latest]
         rust: [stable, nightly]
     steps:
       - name: Setup | Checkout
diff --git a/Android.bp b/Android.bp
index 3d469f9..22790af 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,14 +20,21 @@
 
 rust_library {
     name: "libwhich",
+    // has rustc warnings
     host_supported: true,
     crate_name: "which",
     cargo_env_compat: true,
-    cargo_pkg_version: "4.2.4",
+    cargo_pkg_version: "4.4.0",
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: [
         "libeither",
         "liblibc",
     ],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    product_available: true,
+    vendor_available: true,
 }
diff --git a/Cargo.toml b/Cargo.toml
index 850a29c..ff6894a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,27 +12,39 @@
 [package]
 edition = "2018"
 name = "which"
-version = "4.2.4"
+version = "4.4.0"
 authors = ["Harry Fei <tiziyuanfang@gmail.com>"]
 description = "A Rust equivalent of Unix command \"which\". Locate installed executable in cross platforms."
 documentation = "https://docs.rs/which/"
 readme = "README.md"
-keywords = ["which", "which-rs", "unix", "command"]
-categories = ["os", "filesystem"]
+keywords = [
+    "which",
+    "which-rs",
+    "unix",
+    "command",
+]
+categories = [
+    "os",
+    "filesystem",
+]
 license = "MIT"
 repository = "https://github.com/harryfei/which-rs.git"
+
 [package.metadata.docs.rs]
 all-features = true
+
 [dependencies.either]
-version = "1.6"
+version = "1.6.1"
 
 [dependencies.libc]
-version = "0.2.65"
+version = "0.2.121"
 
 [dependencies.regex]
-version = "1.5.4"
+version = "1.5.5"
 optional = true
-[dev-dependencies.tempdir]
-version = "0.3.7"
-[target."cfg(windows)".dependencies.lazy_static]
+
+[dev-dependencies.tempfile]
+version = "3.3.0"
+
+[target."cfg(windows)".dependencies.once_cell]
 version = "1"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index ec31c55..e05adb3 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "which"
-version = "4.2.4"
+version = "4.4.0"
 edition = "2018"
 authors = ["Harry Fei <tiziyuanfang@gmail.com>"]
 repository = "https://github.com/harryfei/which-rs.git"
@@ -12,15 +12,15 @@
 keywords = ["which", "which-rs", "unix", "command"]
 
 [dependencies]
-either = "1.6"
-libc = "0.2.65"
-regex = { version = "1.5.4", optional = true }
+either = "1.6.1"
+libc = "0.2.121"
+regex = { version = "1.5.5", optional = true }
 
 [target.'cfg(windows)'.dependencies]
-lazy_static = "1"
+once_cell = "1"
 
 [dev-dependencies]
-tempdir = "0.3.7"
+tempfile = "3.3.0"
 
 [package.metadata.docs.rs]
 all-features = true
diff --git a/METADATA b/METADATA
index 6a034c4..d2123d9 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/which
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
 name: "which"
 description: "A Rust equivalent of Unix command \"which\". Locate installed executable in cross platforms."
 third_party {
@@ -7,13 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/which/which-4.2.4.crate"
+    value: "https://static.crates.io/crates/which/which-4.4.0.crate"
   }
-  version: "4.2.4"
+  version: "4.4.0"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 3
-    day: 1
+    year: 2023
+    month: 2
+    day: 17
   }
 }
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e4ec3b3..23bbdf9 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -3,22 +3,12 @@
   "imports": [
     {
       "path": "external/rust/crates/libsqlite3-sys"
-    }
-  ],
-  "presubmit": [
-    {
-      "name": "keystore2_test"
     },
     {
-      "name": "legacykeystore_test"
-    }
-  ],
-  "presubmit-rust": [
-    {
-      "name": "keystore2_test"
+      "path": "system/security/keystore2"
     },
     {
-      "name": "legacykeystore_test"
+      "path": "system/security/keystore2/legacykeystore"
     }
   ]
 }
diff --git a/src/checker.rs b/src/checker.rs
index 62b78a2..0e92c6a 100644
--- a/src/checker.rs
+++ b/src/checker.rs
@@ -1,7 +1,5 @@
 use crate::finder::Checker;
 #[cfg(unix)]
-use libc;
-#[cfg(unix)]
 use std::ffi::CString;
 use std::fs;
 #[cfg(unix)]
@@ -20,7 +18,7 @@
     #[cfg(unix)]
     fn is_valid(&self, path: &Path) -> bool {
         CString::new(path.as_os_str().as_bytes())
-            .and_then(|c| Ok(unsafe { libc::access(c.as_ptr(), libc::X_OK) == 0 }))
+            .map(|c| unsafe { libc::access(c.as_ptr(), libc::X_OK) == 0 })
             .unwrap_or(false)
     }
 
diff --git a/src/finder.rs b/src/finder.rs
index 43b659d..858a224 100644
--- a/src/finder.rs
+++ b/src/finder.rs
@@ -9,7 +9,7 @@
 use std::borrow::Borrow;
 use std::env;
 use std::ffi::OsStr;
-#[cfg(feature = "regex")]
+#[cfg(any(feature = "regex", target_os = "windows"))]
 use std::fs;
 use std::iter;
 use std::path::{Path, PathBuf};
@@ -80,7 +80,9 @@
             }
         };
 
-        Ok(binary_path_candidates.filter(move |p| binary_checker.is_valid(p)))
+        Ok(binary_path_candidates
+            .filter(move |p| binary_checker.is_valid(p))
+            .map(correct_casing))
     }
 
     #[cfg(feature = "regex")]
@@ -94,6 +96,9 @@
         T: AsRef<OsStr>,
     {
         let p = paths.ok_or(Error::CannotFindBinaryPath)?;
+        // Collect needs to happen in order to not have to
+        // change the API to borrow on `paths`.
+        #[allow(clippy::needless_collect)]
         let paths: Vec<_> = env::split_paths(&p).collect();
 
         let matching_re = paths
@@ -148,29 +153,31 @@
     where
         P: IntoIterator<Item = PathBuf>,
     {
+        use once_cell::sync::Lazy;
+
         // Sample %PATHEXT%: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
         // PATH_EXTENSIONS is then [".COM", ".EXE", ".BAT", …].
         // (In one use of PATH_EXTENSIONS we skip the dot, but in the other we need it;
         // hence its retention.)
-        lazy_static! {
-            static ref PATH_EXTENSIONS: Vec<String> =
-                env::var("PATHEXT")
-                    .map(|pathext| {
-                        pathext.split(';')
-                            .filter_map(|s| {
-                                if s.as_bytes().first() == Some(&b'.') {
-                                    Some(s.to_owned())
-                                } else {
-                                    // Invalid segment; just ignore it.
-                                    None
-                                }
-                            })
-                            .collect()
-                    })
-                    // PATHEXT not being set or not being a proper Unicode string is exceedingly
-                    // improbable and would probably break Windows badly. Still, don't crash:
-                    .unwrap_or(vec![]);
-        }
+        static PATH_EXTENSIONS: Lazy<Vec<String>> = Lazy::new(|| {
+            env::var("PATHEXT")
+                .map(|pathext| {
+                    pathext
+                        .split(';')
+                        .filter_map(|s| {
+                            if s.as_bytes().first() == Some(&b'.') {
+                                Some(s.to_owned())
+                            } else {
+                                // Invalid segment; just ignore it.
+                                None
+                            }
+                        })
+                        .collect()
+                })
+                // PATHEXT not being set or not being a proper Unicode string is exceedingly
+                // improbable and would probably break Windows badly. Still, don't crash:
+                .unwrap_or_default()
+        });
 
         paths
             .into_iter()
@@ -179,20 +186,47 @@
                 if has_executable_extension(&p, &PATH_EXTENSIONS) {
                     Box::new(iter::once(p))
                 } else {
+                    let bare_file = p.extension().map(|_| p.clone());
                     // Appended paths with windows executable extensions.
-                    // e.g. path `c:/windows/bin` will expend to:
-                    // c:/windows/bin.COM
-                    // c:/windows/bin.EXE
-                    // c:/windows/bin.CMD
+                    // e.g. path `c:/windows/bin[.ext]` will expand to:
+                    // [c:/windows/bin.ext]
+                    // c:/windows/bin[.ext].COM
+                    // c:/windows/bin[.ext].EXE
+                    // c:/windows/bin[.ext].CMD
                     // ...
-                    Box::new(PATH_EXTENSIONS.iter().map(move |e| {
-                        // Append the extension.
-                        let mut p = p.clone().into_os_string();
-                        p.push(e);
+                    Box::new(
+                        bare_file
+                            .into_iter()
+                            .chain(PATH_EXTENSIONS.iter().map(move |e| {
+                                // Append the extension.
+                                let mut p = p.clone().into_os_string();
+                                p.push(e);
 
-                        PathBuf::from(p)
-                    }))
+                                PathBuf::from(p)
+                            })),
+                    )
                 }
             })
     }
 }
+
+#[cfg(target_os = "windows")]
+fn correct_casing(mut p: PathBuf) -> PathBuf {
+    if let (Some(parent), Some(file_name)) = (p.parent(), p.file_name()) {
+        if let Ok(iter) = fs::read_dir(parent) {
+            for e in iter.filter_map(std::result::Result::ok) {
+                if e.file_name().eq_ignore_ascii_case(file_name) {
+                    p.pop();
+                    p.push(e.file_name());
+                    break;
+                }
+            }
+        }
+    }
+    p
+}
+
+#[cfg(not(target_os = "windows"))]
+fn correct_casing(p: PathBuf) -> PathBuf {
+    p
+}
diff --git a/src/lib.rs b/src/lib.rs
index 2b27094..3e556eb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,10 +14,6 @@
 //!
 //! ```
 
-#[cfg(windows)]
-#[macro_use]
-extern crate lazy_static;
-
 mod checker;
 mod error;
 mod finder;
@@ -25,20 +21,18 @@
 mod helper;
 
 #[cfg(feature = "regex")]
-use regex::Regex;
-#[cfg(feature = "regex")]
 use std::borrow::Borrow;
 use std::env;
 use std::fmt;
 use std::path;
 
-use std::ffi::OsStr;
+use std::ffi::{OsStr, OsString};
 
 use crate::checker::{CompositeChecker, ExecutableChecker, ExistedChecker};
 pub use crate::error::*;
 use crate::finder::Finder;
 
-/// Find a exectable binary's path by name.
+/// Find an executable binary's path by name.
 ///
 /// If given an absolute path, returns it if the file exists and is executable.
 ///
@@ -63,7 +57,31 @@
     which_all(binary_name).and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
 }
 
-/// Find all binaries with `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
+/// Find an executable binary's path by name, ignoring `cwd`.
+///
+/// If given an absolute path, returns it if the file exists and is executable.
+///
+/// Does not resolve relative paths.
+///
+/// If given a string without path separators, looks for a file named
+/// `binary_name` at each directory in `$PATH` and if it finds an executable
+/// file there, returns it.
+///
+/// # Example
+///
+/// ```no_run
+/// use which::which;
+/// use std::path::PathBuf;
+///
+/// let result = which::which_global("rustc").unwrap();
+/// assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
+///
+/// ```
+pub fn which_global<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
+    which_all_global(binary_name).and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
+}
+
+/// Find all binaries with `binary_name` using `cwd` to resolve relative paths.
 pub fn which_all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = path::PathBuf>> {
     let cwd = env::current_dir().ok();
 
@@ -74,6 +92,22 @@
     finder.find(binary_name, env::var_os("PATH"), cwd, binary_checker)
 }
 
+/// Find all binaries with `binary_name` ignoring `cwd`.
+pub fn which_all_global<T: AsRef<OsStr>>(
+    binary_name: T,
+) -> Result<impl Iterator<Item = path::PathBuf>> {
+    let binary_checker = build_binary_checker();
+
+    let finder = Finder::new();
+
+    finder.find(
+        binary_name,
+        env::var_os("PATH"),
+        Option::<&Path>::None,
+        binary_checker,
+    )
+}
+
 /// Find all binaries matching a regular expression in a the system PATH.
 ///
 /// Only available when feature `regex` is enabled.
@@ -178,12 +212,183 @@
     finder.find(binary_name, paths, Some(cwd), binary_checker)
 }
 
+/// Find all binaries with `binary_name` in the path list `paths`, ignoring `cwd`.
+pub fn which_in_global<T, U>(
+    binary_name: T,
+    paths: Option<U>,
+) -> Result<impl Iterator<Item = path::PathBuf>>
+where
+    T: AsRef<OsStr>,
+    U: AsRef<OsStr>,
+{
+    let binary_checker = build_binary_checker();
+
+    let finder = Finder::new();
+
+    finder.find(binary_name, paths, Option::<&Path>::None, binary_checker)
+}
+
 fn build_binary_checker() -> CompositeChecker {
     CompositeChecker::new()
         .add_checker(Box::new(ExistedChecker::new()))
         .add_checker(Box::new(ExecutableChecker::new()))
 }
 
+/// A wrapper containing all functionality in this crate.
+pub struct WhichConfig {
+    cwd: Option<either::Either<bool, path::PathBuf>>,
+    custom_path_list: Option<OsString>,
+    binary_name: Option<OsString>,
+    #[cfg(feature = "regex")]
+    regex: Option<Regex>,
+}
+
+impl Default for WhichConfig {
+    fn default() -> Self {
+        Self {
+            cwd: Some(either::Either::Left(true)),
+            custom_path_list: None,
+            binary_name: None,
+            #[cfg(feature = "regex")]
+            regex: None,
+        }
+    }
+}
+
+#[cfg(feature = "regex")]
+type Regex = regex::Regex;
+
+#[cfg(not(feature = "regex"))]
+type Regex = ();
+
+impl WhichConfig {
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    /// Whether or not to use the current working directory. `true` by default.
+    ///
+    /// # Panics
+    ///
+    /// If regex was set previously, and you've just passed in `use_cwd: true`, this will panic.
+    pub fn system_cwd(mut self, use_cwd: bool) -> Self {
+        #[cfg(feature = "regex")]
+        if self.regex.is_some() && use_cwd {
+            panic!("which can't use regex and cwd at the same time!")
+        }
+        self.cwd = Some(either::Either::Left(use_cwd));
+        self
+    }
+
+    /// Sets a custom path for resolving relative paths.
+    ///
+    /// # Panics
+    ///
+    /// If regex was set previously, this will panic.
+    pub fn custom_cwd(mut self, cwd: path::PathBuf) -> Self {
+        #[cfg(feature = "regex")]
+        if self.regex.is_some() {
+            panic!("which can't use regex and cwd at the same time!")
+        }
+        self.cwd = Some(either::Either::Right(cwd));
+        self
+    }
+
+    /// Sets the path name regex to search for. You ***MUST*** call this, or [`Self::binary_name`] prior to searching.
+    ///
+    /// When `Regex` is disabled this function takes the unit type as a stand in. The parameter will change when
+    /// `Regex` is enabled.
+    ///
+    /// # Panics
+    ///
+    /// If the `regex` feature wasn't turned on for this crate this will always panic. Additionally if a
+    /// `cwd` (aka current working directory) or `binary_name` was set previously, this will panic, as those options
+    /// are incompatible with `regex`.
+    #[allow(unused_variables)]
+    pub fn regex(mut self, regex: Regex) -> Self {
+        #[cfg(not(feature = "regex"))]
+        {
+            panic!("which's regex feature was not enabled in your Cargo.toml!")
+        }
+        #[cfg(feature = "regex")]
+        {
+            if self.cwd != Some(either::Either::Left(false)) && self.cwd.is_some() {
+                panic!("which can't use regex and cwd at the same time!")
+            }
+            if self.binary_name.is_some() {
+                panic!("which can't use `binary_name` and `regex` at the same time!");
+            }
+            self.regex = Some(regex);
+            self
+        }
+    }
+
+    /// Sets the path name to search for. You ***MUST*** call this, or [`Self::regex`] prior to searching.
+    ///
+    /// # Panics
+    ///
+    /// If a `regex` was set previously this will panic as this is not compatible with `regex`.
+    pub fn binary_name(mut self, name: OsString) -> Self {
+        #[cfg(feature = "regex")]
+        if self.regex.is_some() {
+            panic!("which can't use `binary_name` and `regex` at the same time!");
+        }
+        self.binary_name = Some(name);
+        self
+    }
+
+    /// Uses the given string instead of the `PATH` env variable.
+    pub fn custom_path_list(mut self, custom_path_list: OsString) -> Self {
+        self.custom_path_list = Some(custom_path_list);
+        self
+    }
+
+    /// Uses the `PATH` env variable. Enabled by default.
+    pub fn system_path_list(mut self) -> Self {
+        self.custom_path_list = None;
+        self
+    }
+
+    /// Finishes configuring, runs the query and returns the first result.
+    pub fn first_result(self) -> Result<path::PathBuf> {
+        self.all_results()
+            .and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
+    }
+
+    /// Finishes configuring, runs the query and returns all results.
+    pub fn all_results(self) -> Result<impl Iterator<Item = path::PathBuf>> {
+        let binary_checker = build_binary_checker();
+
+        let finder = Finder::new();
+
+        let paths = self.custom_path_list.or_else(|| env::var_os("PATH"));
+
+        #[cfg(feature = "regex")]
+        if let Some(regex) = self.regex {
+            return finder
+                .find_re(regex, paths, binary_checker)
+                .map(|i| Box::new(i) as Box<dyn Iterator<Item = path::PathBuf>>);
+        }
+
+        let cwd = match self.cwd {
+            Some(either::Either::Left(false)) => None,
+            Some(either::Either::Right(custom)) => Some(custom),
+            None | Some(either::Either::Left(true)) => env::current_dir().ok(),
+        };
+
+        finder
+            .find(
+                self.binary_name.expect(
+                    "binary_name not set! You must set binary_name or regex before searching!",
+                ),
+                paths,
+                cwd,
+                binary_checker,
+            )
+            .map(|i| Box::new(i) as Box<dyn Iterator<Item = path::PathBuf>>)
+    }
+}
+
 /// An owned, immutable wrapper around a `PathBuf` containing the path of an executable.
 ///
 /// The constructed `PathBuf` is the output of `which` or `which_in`, but `which::Path` has the
@@ -194,7 +399,7 @@
 ///
 /// Since `which::Path` implements `Deref` for `std::path::Path`, all methods on `&std::path::Path`
 /// are also available to `&which::Path` values.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
 pub struct Path {
     inner: path::PathBuf,
 }
@@ -281,8 +486,6 @@
     }
 }
 
-impl Eq for Path {}
-
 impl PartialEq<path::PathBuf> for Path {
     fn eq(&self, other: &path::PathBuf) -> bool {
         self.inner == *other
@@ -308,7 +511,7 @@
 ///
 /// Since `CanonicalPath` implements `Deref` for `std::path::Path`, all methods on
 /// `&std::path::Path` are also available to `&CanonicalPath` values.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
 pub struct CanonicalPath {
     inner: path::PathBuf,
 }
@@ -415,8 +618,6 @@
     }
 }
 
-impl Eq for CanonicalPath {}
-
 impl PartialEq<path::PathBuf> for CanonicalPath {
     fn eq(&self, other: &path::PathBuf) -> bool {
         self.inner == *other
diff --git a/tests/basic.rs b/tests/basic.rs
index 897e912..32a1a28 100644
--- a/tests/basic.rs
+++ b/tests/basic.rs
@@ -1,14 +1,13 @@
-extern crate tempdir;
 extern crate which;
 
-#[cfg(feature = "regex")]
+#[cfg(all(unix, feature = "regex"))]
 use regex::Regex;
 use std::ffi::{OsStr, OsString};
 use std::fs;
 use std::io;
 use std::path::{Path, PathBuf};
 use std::{env, vec};
-use tempdir::TempDir;
+use tempfile::TempDir;
 
 struct TestFixture {
     /// Temp directory.
@@ -19,8 +18,8 @@
     pub bins: Vec<PathBuf>,
 }
 
-const SUBDIRS: &'static [&'static str] = &["a", "b", "c"];
-const BIN_NAME: &'static str = "bin";
+const SUBDIRS: &[&str] = &["a", "b", "c"];
+const BIN_NAME: &str = "bin";
 
 #[cfg(unix)]
 fn mk_bin(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf> {
@@ -55,7 +54,7 @@
     // tmp/c/bin.exe
     // tmp/c/bin.cmd
     pub fn new() -> TestFixture {
-        let tempdir = TempDir::new("which_tests").unwrap();
+        let tempdir = tempfile::tempdir().unwrap();
         let mut builder = fs::DirBuilder::new();
         builder.recursive(true);
         let mut paths = vec![];
@@ -63,25 +62,29 @@
         for d in SUBDIRS.iter() {
             let p = tempdir.path().join(d);
             builder.create(&p).unwrap();
-            bins.push(mk_bin(&p, &BIN_NAME, "").unwrap());
-            bins.push(mk_bin(&p, &BIN_NAME, "exe").unwrap());
-            bins.push(mk_bin(&p, &BIN_NAME, "cmd").unwrap());
+            bins.push(mk_bin(&p, BIN_NAME, "").unwrap());
+            bins.push(mk_bin(&p, BIN_NAME, "exe").unwrap());
+            bins.push(mk_bin(&p, BIN_NAME, "cmd").unwrap());
             paths.push(p);
         }
+        let p = tempdir.path().join("win-bin");
+        builder.create(&p).unwrap();
+        bins.push(mk_bin(&p, "win-bin", "exe").unwrap());
+        paths.push(p);
         TestFixture {
-            tempdir: tempdir,
+            tempdir,
             paths: env::join_paths(paths).unwrap(),
-            bins: bins,
+            bins,
         }
     }
 
     #[allow(dead_code)]
     pub fn touch(&self, path: &str, extension: &str) -> io::Result<PathBuf> {
-        touch(self.tempdir.path(), &path, &extension)
+        touch(self.tempdir.path(), path, extension)
     }
 
     pub fn mk_bin(&self, path: &str, extension: &str) -> io::Result<PathBuf> {
-        mk_bin(self.tempdir.path(), &path, &extension)
+        mk_bin(self.tempdir.path(), path, extension)
     }
 }
 
@@ -89,11 +92,11 @@
     which::CanonicalPath::new_in(path, Some(f.paths.clone()), f.tempdir.path())
 }
 
-fn _which_all<T: AsRef<OsStr>>(
-    f: &TestFixture,
+fn _which_all<'a, T: AsRef<OsStr> + 'a>(
+    f: &'a TestFixture,
     path: T,
-) -> which::Result<impl Iterator<Item = which::Result<which::CanonicalPath>>> {
-    which::CanonicalPath::all_in(path, Some(f.paths.clone()), f.tempdir.path().to_path_buf())
+) -> which::Result<impl Iterator<Item = which::Result<which::CanonicalPath>> + '_> {
+    which::CanonicalPath::all_in(path, Some(f.paths.clone()), f.tempdir.path())
 }
 
 #[test]
@@ -165,10 +168,18 @@
 #[test]
 #[cfg(all(unix, feature = "regex"))]
 fn test_which_re_accepts_owned_and_borrow() {
-    which::which_re(Regex::new(r".").unwrap());
-    which::which_re(&Regex::new(r".").unwrap());
-    which::which_re_in(Regex::new(r".").unwrap(), Some("pth"));
-    which::which_re_in(&Regex::new(r".").unwrap(), Some("pth"));
+    which::which_re(Regex::new(r".").unwrap())
+        .unwrap()
+        .for_each(drop);
+    which::which_re(&Regex::new(r".").unwrap())
+        .unwrap()
+        .for_each(drop);
+    which::which_re_in(Regex::new(r".").unwrap(), Some("pth"))
+        .unwrap()
+        .for_each(drop);
+    which::which_re_in(&Regex::new(r".").unwrap(), Some("pth"))
+        .unwrap()
+        .for_each(drop);
 }
 
 #[test]
@@ -188,6 +199,17 @@
 }
 
 #[test]
+#[cfg(windows)]
+fn test_which_no_extension() {
+    let f = TestFixture::new();
+    let b = Path::new("win-bin");
+    let which_result = which::which_in(&b, Some(&f.paths), ".").unwrap();
+    // Make sure the extension is the correct case.
+    assert_eq!(which_result.extension(), f.bins[9].extension());
+    assert_eq!(fs::canonicalize(&which_result).unwrap(), f.bins[9])
+}
+
+#[test]
 fn test_which_not_found() {
     let f = TestFixture::new();
     assert!(_which(&f, "a").is_err());
@@ -214,6 +236,7 @@
         .collect::<Vec<_>>();
     #[cfg(windows)]
     {
+        expected.retain(|p| p.file_stem().unwrap() == BIN_NAME);
         expected.retain(|p| p.extension().map(|ext| ext == "exe" || ext == "cmd") == Some(true));
     }
     #[cfg(not(windows))]