11from __future__ import annotations
22
3+ import ctypes
34import getpass
45import json
56import os
1415from pathlib import Path
1516from plistlib import load as plist_load
1617from typing import TYPE_CHECKING
17- import ctypes
1818
1919import pytest
2020from conda .base .context import context
@@ -168,6 +168,7 @@ def _run_installer_exe(
168168 _check_installer_log (install_dir )
169169 return process
170170
171+
171172def _run_uninstaller_exe (
172173 install_dir : Path ,
173174 timeout : int = 420 ,
@@ -286,22 +287,24 @@ def _sentinel_file_checks(example_path, install_dir):
286287 f"{ install_dir } contents:\n " + "\n " .join (sorted (map (str , install_dir .iterdir ())))
287288 )
288289
290+
289291def is_admin () -> bool :
290292 try :
291293 return ctypes .windll .shell32 .IsUserAnAdmin ()
292294 except Exception :
293295 return False
294296
297+
295298def calculate_msi_install_path (installer : Path ) -> Path :
296- """ This is a temporary solution for now since we cannot choose the install location ourselves.
297- Installers are named <name>-<version>-Windows-x86_64.msi.
299+ """This is a temporary solution for now since we cannot choose the install location ourselves.
300+ Installers are named <name>-<version>-Windows-x86_64.msi.
298301 """
299- dir_name = installer .name .replace (' -Windows-x86_64.msi' , '' ).replace ('-' , ' ' )
302+ dir_name = installer .name .replace (" -Windows-x86_64.msi" , "" ).replace ("-" , " " )
300303 if is_admin ():
301- root_dir = Path (os .environ .get (' PROGRAMFILES' ) or r"C:\Program Files" )
304+ root_dir = Path (os .environ .get (" PROGRAMFILES" ) or r"C:\Program Files" )
302305 else :
303- local_dir = os .environ .get (' LOCALAPPDATA' ) or str (Path .home () / r"AppData\Local" )
304- root_dir = Path (local_dir ) / ' Programs'
306+ local_dir = os .environ .get (" LOCALAPPDATA" ) or str (Path .home () / r"AppData\Local" )
307+ root_dir = Path (local_dir ) / " Programs"
305308 return Path (root_dir ) / dir_name
306309
307310
@@ -313,9 +316,7 @@ def _run_installer_msi(
313316 check = True ,
314317 options : list | None = None ,
315318):
316- """
317- TODO
318- """
319+ """Runs specified MSI Installer via command line in silent mode. This is work in progress."""
319320 if not sys .platform .startswith ("win" ):
320321 raise ValueError ("Can only run .msi installers on Windows" )
321322 options = options or []
@@ -327,19 +328,20 @@ def _run_installer_msi(
327328 "/qn" ,
328329 ]
329330
330- log_path = Path (os .environ .get (' TEMP' )) / (install_dir .name + ".log" )
331+ log_path = Path (os .environ .get (" TEMP" )) / (install_dir .name + ".log" )
331332 cmd .extend (["/L*V" , str (log_path )])
332333 process = _execute (cmd , installer_input = installer_input , timeout = timeout , check = check )
333334 if check :
334- print ("post install checks for MSI Installers not yet implemented" )
335+ print ("A check for MSI Installers not yet implemented" )
335336 return process
336337
337- def _run_uninstaller_msi (installer : Path ,
338+
339+ def _run_uninstaller_msi (
340+ installer : Path ,
338341 install_dir : Path ,
339342 timeout : int = 420 ,
340343 check : bool = True ,
341344) -> subprocess .CompletedProcess | None :
342-
343345 cmd = [
344346 "msiexec.exe" ,
345347 "/x" ,
@@ -354,6 +356,7 @@ def _run_uninstaller_msi(installer: Path,
354356
355357 return process
356358
359+
357360def _run_installer (
358361 example_path : Path ,
359362 installer : Path ,
@@ -417,7 +420,7 @@ def _run_installer(
417420 _sentinel_file_checks (example_path , install_dir )
418421 if uninstall :
419422 if installer .suffix == ".msi" :
420- if request : # and ON_CI
423+ if request : # and ON_CI
421424 # We always need to do this currently since uninstall doesnt work fully
422425 request .addfinalizer (lambda : shutil .rmtree (str (install_dir ), ignore_errors = True ))
423426 _run_uninstaller_msi (installer , install_dir , timeout = timeout , check = check_subprocess )
@@ -442,9 +445,7 @@ def create_installer(
442445
443446 output_dir = workspace / "installer"
444447 output_dir .mkdir (parents = True , exist_ok = True )
445- cmd = [
446- * COV_CMD ,
447- "constructor" ]
448+ cmd = [* COV_CMD , "constructor" ]
448449 # This flag will (if enabled) create a lot of output upon test failures for .exe-installers.
449450 # If debugging generated NSIS templates, it can be worth to enable.
450451 if CONSTRUCTOR_VERBOSE :
@@ -478,7 +479,7 @@ def _sort_by_extension(path):
478479 install_dir = Path ("~" ).expanduser () / calculate_install_dir (
479480 input_dir / config_filename
480481 )
481- elif installer .suffix == ' .msi' :
482+ elif installer .suffix == " .msi" :
482483 install_dir = calculate_msi_install_path (installer )
483484 else :
484485 install_dir = (
@@ -573,7 +574,11 @@ def test_example_extra_envs(tmp_path, request):
573574
574575 if sys .platform .startswith ("win" ):
575576 if installer .suffix == ".msi" :
576- _run_uninstaller_msi (installer , install_dir , timeout = timeout , check = check_subprocess )
577+ if request :
578+ request .addfinalizer (
579+ lambda : shutil .rmtree (str (install_dir ), ignore_errors = True )
580+ )
581+ _run_uninstaller_msi (installer , install_dir )
577582 else :
578583 _run_uninstaller_exe (install_dir = install_dir )
579584
@@ -587,7 +592,7 @@ def test_example_extra_files(tmp_path, request):
587592 install_dir ,
588593 request = request ,
589594 check_sentinels = CONSTRUCTOR_VERBOSE ,
590- check_subprocess = CONSTRUCTOR_VERBOSE
595+ check_subprocess = CONSTRUCTOR_VERBOSE ,
591596 )
592597
593598
@@ -662,8 +667,12 @@ def test_example_miniforge(tmp_path, request, example):
662667 raise AssertionError ("Could not find Start Menu folder for miniforge" )
663668 _run_uninstaller_exe (install_dir )
664669 assert not list (start_menu_dir .glob ("Miniforge*.lnk" ))
665- elif installer .suffix == ' .msi' :
670+ elif installer .suffix == " .msi" :
666671 # TODO: Start menus
672+ if request :
673+ request .addfinalizer (
674+ lambda : shutil .rmtree (str (install_dir ), ignore_errors = True )
675+ )
667676 _run_uninstaller_msi (installer , install_dir )
668677 raise Exception ("Test needs to be implemented" )
669678
@@ -828,7 +837,11 @@ def test_example_shortcuts(tmp_path, request):
828837 break
829838 else :
830839 raise AssertionError ("No shortcuts found!" )
831- if installer .suffix == '.msi' :
840+ if installer .suffix == ".msi" :
841+ if request :
842+ request .addfinalizer (
843+ lambda : shutil .rmtree (str (install_dir ), ignore_errors = True )
844+ )
832845 _run_uninstaller_msi (installer , install_dir )
833846 else :
834847 _run_uninstaller_exe (install_dir )
@@ -973,8 +986,11 @@ def test_example_from_explicit(tmp_path, request):
973986
974987
975988def test_register_envs (tmp_path , request ):
989+ """Verify that 'register_envs: False' results in the environment not being registered."""
976990 input_path = _example_path ("register_envs" )
977991 for installer , install_dir in create_installer (input_path , tmp_path ):
992+ if installer .suffix == ".msi" :
993+ raise Exception ("Test for 'register_envs' not yet implemented for MSI" )
978994 _run_installer (input_path , installer , install_dir , request = request )
979995 environments_txt = Path ("~/.conda/environments.txt" ).expanduser ().read_text ()
980996 assert str (install_dir ) not in environments_txt
@@ -1035,6 +1051,7 @@ def test_cross_osx_building(tmp_path):
10351051 )
10361052
10371053
1054+ @pytest .mark .skipif (sys .platform .startswith ("win" ), reason = "Unix only" )
10381055def test_cross_build_example (tmp_path , platform_conda_exe ):
10391056 platform , conda_exe = platform_conda_exe
10401057 input_path = _example_path ("virtual_specs_ok" )
@@ -1050,6 +1067,7 @@ def test_cross_build_example(tmp_path, platform_conda_exe):
10501067
10511068
10521069def test_virtual_specs_failed (tmp_path , request ):
1070+ """Verify that virtual packages listed via 'virtual_specs' are satisfied."""
10531071 input_path = _example_path ("virtual_specs_failed" )
10541072 for installer , install_dir in create_installer (input_path , tmp_path ):
10551073 process = _run_installer (
@@ -1065,6 +1083,8 @@ def test_virtual_specs_failed(tmp_path, request):
10651083 with pytest .raises (AssertionError , match = "Failed to check virtual specs" ):
10661084 _check_installer_log (install_dir )
10671085 continue
1086+ elif installer .suffix == ".msi" :
1087+ raise Exception ("Test for 'virtual_specs' not yet implemented for MSI" )
10681088 elif installer .suffix == ".pkg" :
10691089 if not ON_CI :
10701090 continue
0 commit comments