11package.path = ' ./lib/?.lua;' .. package.path
22RUNTIME = {osType = ' windows' , archType = ' amd64' , pluginDirPath = " C:\\ Plugin's & %USERNAME% ! store" }
33OS_TYPE = ' windows'
4- local state = {commands = {}}
4+ local state = {commands = {}, closeCount = 0 }
5+ local function reset ()
6+ for key in pairs (state ) do state [key ] = nil end
7+ state .commands = {}
8+ state .closeCount = 0
9+ end
510package.preload .http = function () return {download_file = function () return nil end } end
611package.preload .html = function () return {} end
712package.preload .json = function () return {} end
@@ -14,13 +19,22 @@ os.execute = function(command)
1419 return # state .commands == state .failOn and 1 or 0
1520end
1621io .popen = function (command )
17- os.execute (command )
22+ local exitCode = os.execute (command )
1823 return {
1924 lines = function ()
20- local files , i = {' core.msi' , ' stdlib.msi' }, 0
21- return function () i = i + 1 ; return files [i ] end
25+ local files , i = state .files or {' core.msi' , ' stdlib.msi' }, 0
26+ return function ()
27+ i = i + 1
28+ if state .readFailure and i == 2 then error (' read failed' ) end
29+ return files [i ]
30+ end
31+ end ,
32+ close = function ()
33+ state .closeCount = state .closeCount + 1
34+ if state .closeFailure then error (' close failed' ) end
35+ -- GopherLua process pipes return a numeric exit status, including 0.
36+ return state .listExitCode or exitCode
2237 end ,
23- close = function () return true end ,
2438 }
2539end
2640io .open = function ()
@@ -32,17 +46,33 @@ require('util')
3246local path = " C:\\ Users\\ Mechael Jackson's & %USERNAME% !\\ python-3.14.5"
3347windowsInstallExe (path , ' https://example.invalid/python.exe' , ' 3.14.5' , ' python.exe' )
3448assert (# state .commands >= 7 , ' installer, enumeration, MSI, pip and aliases must be exercised' )
35- state . commands = {}
49+ reset ()
3650windowsInstallMsi (path , ' https://example.invalid/python.msi' , ' 3.4.4' , ' python.msi' )
3751assert (# state .commands == 2 , ' MSI and ensurepip must both run' )
3852for _ , failure in ipairs ({{1 , ' Extract failed' }, {3 , ' Install msi failed' }, {6 , ' executable alias' }}) do
39- state . commands = {}
53+ reset ()
4054 state .failOn = failure [1 ]
4155 local ok , err = pcall (function ()
4256 windowsInstallExe (path , ' https://example.invalid/python.exe' , ' 3.14.5' , ' python.exe' )
4357 end )
4458 assert (not ok and tostring (err ):find (failure [2 ], 1 , true ), tostring (err ))
4559end
60+ for _ , failure in ipairs ({
61+ {name = ' partial listing exits nonzero' , files = {' core.msi' }, exitCode = 1 , message = ' Failed to list installer packages' },
62+ {name = ' listing read failure' , readFailure = true , message = ' read failed' },
63+ {name = ' listing close failure' , closeFailure = true , message = ' close failed' },
64+ {name = ' no MSI packages' , files = {' readme.txt' }, message = ' No installer MSI packages' },
65+ }) do
66+ reset ()
67+ state .files = failure .files
68+ state .listExitCode = failure .exitCode
69+ state .readFailure = failure .readFailure
70+ state .closeFailure = failure .closeFailure
71+ local ok , err = pcall (windowsInstallExe , path , ' https://example.invalid/python.exe' , ' 3.14.5' , ' python.exe' )
72+ assert (not ok and tostring (err ):find (failure .message , 1 , true ), failure .name .. ' : ' .. tostring (err ))
73+ assert (state .closeCount == 1 , failure .name .. ' : listing handle must be closed' )
74+ assert (# state .commands == 2 , failure .name .. ' : no MSI or pip may run before enumeration succeeds' )
75+ end
4676local windows = require (' windows_command' )
4777assert (not pcall (windows .msi , ' C:\\ bad"path\\ python.msi' , path ), ' invalid Windows quote must fail' )
4878assert (not pcall (windows .native , ' bad\n program' , {}), ' control characters must fail' )
0 commit comments