@@ -396,4 +396,118 @@ mod tests {
396396
397397 assert ! ( result. is_none( ) ) ;
398398 }
399+
400+ #[ test]
401+ fn test_venv_try_from_without_prefix_derives_from_executable ( ) {
402+ let dir = tempdir ( ) . unwrap ( ) ;
403+ #[ cfg( windows) ]
404+ let bin_dir = dir. path ( ) . join ( "Scripts" ) ;
405+ #[ cfg( unix) ]
406+ let bin_dir = dir. path ( ) . join ( "bin" ) ;
407+ fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
408+
409+ let cfg_path = dir. path ( ) . join ( "pyvenv.cfg" ) ;
410+ let mut file = fs:: File :: create ( & cfg_path) . unwrap ( ) ;
411+ writeln ! ( file, "version = 3.12.0" ) . unwrap ( ) ;
412+
413+ #[ cfg( windows) ]
414+ let python_path = bin_dir. join ( "python.exe" ) ;
415+ #[ cfg( unix) ]
416+ let python_path = bin_dir. join ( "python" ) ;
417+ fs:: File :: create ( & python_path) . unwrap ( ) ;
418+
419+ // No prefix provided — should derive from executable parent's parent
420+ let env = PythonEnv :: new ( python_path, None , None ) ;
421+ let venv = Venv :: new ( ) ;
422+ let result = venv. try_from ( & env) ;
423+
424+ assert ! ( result. is_some( ) ) ;
425+ let py_env = result. unwrap ( ) ;
426+ assert_eq ! ( py_env. kind, Some ( PythonEnvironmentKind :: Venv ) ) ;
427+ assert ! ( py_env. prefix. is_some( ) ) ;
428+ }
429+
430+ #[ test]
431+ fn test_venv_try_from_with_version_in_env ( ) {
432+ let dir = tempdir ( ) . unwrap ( ) ;
433+ #[ cfg( windows) ]
434+ let bin_dir = dir. path ( ) . join ( "Scripts" ) ;
435+ #[ cfg( unix) ]
436+ let bin_dir = dir. path ( ) . join ( "bin" ) ;
437+ fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
438+
439+ let cfg_path = dir. path ( ) . join ( "pyvenv.cfg" ) ;
440+ let mut file = fs:: File :: create ( & cfg_path) . unwrap ( ) ;
441+ writeln ! ( file, "version = 3.11.0" ) . unwrap ( ) ;
442+
443+ #[ cfg( windows) ]
444+ let python_path = bin_dir. join ( "python.exe" ) ;
445+ #[ cfg( unix) ]
446+ let python_path = bin_dir. join ( "python" ) ;
447+ fs:: File :: create ( & python_path) . unwrap ( ) ;
448+
449+ // Version provided in PythonEnv should be used as-is
450+ let mut env = PythonEnv :: new ( python_path, Some ( dir. path ( ) . to_path_buf ( ) ) , None ) ;
451+ env. version = Some ( "3.11.5" . to_string ( ) ) ;
452+ let venv = Venv :: new ( ) ;
453+ let result = venv. try_from ( & env) ;
454+
455+ assert ! ( result. is_some( ) ) ;
456+ let py_env = result. unwrap ( ) ;
457+ assert_eq ! ( py_env. version, Some ( "3.11.5" . to_string( ) ) ) ;
458+ }
459+
460+ #[ test]
461+ fn test_try_environment_from_venv_dir_no_prompt ( ) {
462+ let dir = tempdir ( ) . unwrap ( ) ;
463+ #[ cfg( windows) ]
464+ let bin_dir = dir. path ( ) . join ( "Scripts" ) ;
465+ #[ cfg( unix) ]
466+ let bin_dir = dir. path ( ) . join ( "bin" ) ;
467+ fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
468+
469+ // pyvenv.cfg without prompt
470+ let cfg_path = dir. path ( ) . join ( "pyvenv.cfg" ) ;
471+ let mut file = fs:: File :: create ( & cfg_path) . unwrap ( ) ;
472+ writeln ! ( file, "version = 3.10.0" ) . unwrap ( ) ;
473+
474+ #[ cfg( windows) ]
475+ let python_path = bin_dir. join ( "python.exe" ) ;
476+ #[ cfg( unix) ]
477+ let python_path = bin_dir. join ( "python" ) ;
478+ fs:: File :: create ( & python_path) . unwrap ( ) ;
479+
480+ let result = try_environment_from_venv_dir ( dir. path ( ) ) . unwrap ( ) ;
481+
482+ assert_eq ! ( result. kind, Some ( PythonEnvironmentKind :: Venv ) ) ;
483+ assert ! ( result. name. is_none( ) ) ;
484+ assert_eq ! ( result. version, Some ( "3.10.0" . to_string( ) ) ) ;
485+ }
486+
487+ #[ test]
488+ fn test_is_venv_via_executable_parent ( ) {
489+ // Test that is_venv works when pyvenv.cfg is found via executable parent
490+ let dir = tempdir ( ) . unwrap ( ) ;
491+ #[ cfg( windows) ]
492+ let bin_dir = dir. path ( ) . join ( "Scripts" ) ;
493+ #[ cfg( unix) ]
494+ let bin_dir = dir. path ( ) . join ( "bin" ) ;
495+ fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
496+
497+ // Place pyvenv.cfg next to bin dir (parent of executable's parent = dir)
498+ let cfg_path = dir. path ( ) . join ( "pyvenv.cfg" ) ;
499+ let mut file = fs:: File :: create ( & cfg_path) . unwrap ( ) ;
500+ writeln ! ( file, "version = 3.10.0" ) . unwrap ( ) ;
501+
502+ #[ cfg( windows) ]
503+ let python_path = bin_dir. join ( "python.exe" ) ;
504+ #[ cfg( unix) ]
505+ let python_path = bin_dir. join ( "python" ) ;
506+ fs:: File :: create ( & python_path) . unwrap ( ) ;
507+
508+ // No prefix — relies on executable.parent() finding pyvenv.cfg in bin_dir
509+ // Actually pyvenv.cfg is in dir, and PyVenvCfg::find checks the path and parent
510+ let env = PythonEnv :: new ( python_path, None , None ) ;
511+ assert ! ( is_venv( & env) ) ;
512+ }
399513}
0 commit comments