@@ -449,6 +449,89 @@ pub fn get_remove_path_methods() -> Result<Vec<PathUpdateMethod>> {
449449
450450#[ cfg( test) ]
451451mod tests {
452+ use std:: sync:: Mutex ;
453+
454+ use lazy_static:: lazy_static;
455+ use winreg:: enums:: { RegType , HKEY_CURRENT_USER , KEY_READ , KEY_WRITE } ;
456+ use winreg:: { RegKey , RegValue } ;
457+
458+ use crate :: currentprocess;
459+ use crate :: utils:: utils;
460+
461+ pub fn get_path ( ) -> Option < String > {
462+ let root = RegKey :: predef ( HKEY_CURRENT_USER ) ;
463+ let environment = root
464+ . open_subkey_with_flags ( "Environment" , KEY_READ | KEY_WRITE )
465+ . unwrap ( ) ;
466+ // XXX: copied from the mock support crate, but I am suspicous of this
467+ // code: This uses ok to allow signalling None for 'delete', but this
468+ // can fail e.g. with !(winerror::ERROR_BAD_FILE_TYPE) or other
469+ // failures; which will lead to attempting to delete the users path
470+ // rather than aborting the test suite.
471+ environment. get_value ( "PATH" ) . ok ( )
472+ }
473+
474+ pub fn restore_path ( p : Option < String > ) {
475+ let root = RegKey :: predef ( HKEY_CURRENT_USER ) ;
476+ let environment = root
477+ . open_subkey_with_flags ( "Environment" , KEY_READ | KEY_WRITE )
478+ . unwrap ( ) ;
479+ if let Some ( p) = p. as_ref ( ) {
480+ let reg_value = RegValue {
481+ bytes : utils:: string_to_winreg_bytes ( & p) ,
482+ vtype : RegType :: REG_EXPAND_SZ ,
483+ } ;
484+ environment. set_raw_value ( "PATH" , & reg_value) . unwrap ( ) ;
485+ } else {
486+ let _ = environment. delete_value ( "PATH" ) ;
487+ }
488+ }
489+
490+ fn with_registry_edits ( f : & dyn Fn ( ) ) {
491+ // Lock protects concurrent mutation of registry
492+ lazy_static ! {
493+ static ref LOCK : Mutex <( ) > = Mutex :: new( ( ) ) ;
494+ }
495+ let _g = LOCK . lock ( ) ;
496+
497+ // On windows these tests mess with the user's PATH. Save
498+ // and restore them here to keep from trashing things.
499+ let saved_path = get_path ( ) ;
500+ let _g = scopeguard:: guard ( saved_path, restore_path) ;
501+
502+ f ( ) ;
503+ }
504+
505+ #[ test]
506+ fn windows_uninstall_doesnt_mess_with_a_non_unicode_path ( ) {
507+ // This writes an error, so we want a sink for it.
508+ let tp = Box :: new ( currentprocess:: TestProcess :: default ( ) ) ;
509+ with_registry_edits ( & || {
510+ currentprocess:: with ( tp. clone ( ) , || {
511+ let root = RegKey :: predef ( HKEY_CURRENT_USER ) ;
512+ let environment = root
513+ . open_subkey_with_flags ( "Environment" , KEY_READ | KEY_WRITE )
514+ . unwrap ( ) ;
515+ let reg_value = RegValue {
516+ bytes : vec ! [
517+ 0x00 , 0xD8 , // leading surrogate
518+ 0x01 , 0x01 , // bogus trailing surrogate
519+ 0x00 , 0x00 ,
520+ ] , // null
521+ vtype : RegType :: REG_EXPAND_SZ ,
522+ } ;
523+ environment. set_raw_value ( "PATH" , & reg_value) . unwrap ( ) ;
524+ // Ok(None) signals no change to the PATH setting layer
525+ assert_eq ! ( None , super :: _path_without_cargo_home_bin( ) . unwrap( ) ) ;
526+ } )
527+ } ) ;
528+ assert_eq ! (
529+ r"warning: the registry key HKEY_CURRENT_USER\Environment\PATH does not contain valid Unicode. Not modifying the PATH variable
530+ " ,
531+ String :: from_utf8( tp. get_stderr( ) ) . unwrap( )
532+ ) ;
533+ }
534+
452535 #[ test]
453536 fn windows_uninstall_removes_semicolon_from_path_prefix ( ) {
454537 assert_eq ! (
0 commit comments