local root = vim.uv.cwd() local settings = ( vim.env.PI_CODING_AGENT_DIR or ( vim.uv.os_homedir() .. '/.pi/agent' ) ) .. '/settings.json' local settings_content = function() local handle = assert( io.open( settings, 'rb' ), 'global Pi settings unavailable: ' .. settings ) local content = handle:read( '*a' ) handle:close() return content end local settings_before = settings_content() local directory = vim.fn.tempname() vim.fn.mkdir( directory, 'p' ) local address = vim.fn.has 'win32' == 1 and ( '\\\\.\\pipe\\pivi-e2e-' .. vim.fn.getpid() ) or ( directory .. '/nvim.sock' ) local server = vim.system( { vim.v.progpath, '--headless', '--noplugin', '-u', root .. '/tests/minimal_init.lua', '--listen', address, }, { cwd = root, env = { NVIM_LOG_FILE = directory .. '/nvim.log', }, text = true, } ) local channel local result local ok, failure = xpcall( function() assert( vim.wait( 20000, function() local connected, value = pcall( vim.fn.sockconnect, 'pipe', address, { rpc = true, } ) if connected and value ~= 0 then channel = value; return true end return false end, 10 ), 'child Neovim startup deadline' ) result = vim.rpcrequest( channel, 'nvim_exec_lua', [[ local root, directory = ... vim.g.pivi_model = 'pivi-e2e/faux-1' vim.g.pivi_arguments = { '-e', root .. '/tests/e2e/provider.ts', '--session-dir', directory .. '/sessions', '--offline' } vim.env.PI_CODING_AGENT_SESSION_DIR = directory .. '/sessions' vim.g.pivi_start_insert = false vim.cmd.cd(directory) _G.PIVI_E2E_CHILDREN = {} package.preload['bugabinga.pivi.rpc'] = function() local rpc = dofile(root .. '/lua/bugabinga/pivi/rpc.lua') local start = rpc.start rpc.start = function(options) local events = {} local on_event = options.on_event options.on_event = function(event) table.insert(events, event) if on_event then on_event(event) end end local child = start(options) child.events = events table.insert(_G.PIVI_E2E_CHILDREN, child) child.sent = {} local request = child.request child.request = function(command, callback) table.insert(child.sent, vim.deepcopy(command)) return request(command, callback) end return child end return rpc end return dofile(root .. '/tests/e2e/scenarios.lua')(directory) ]], { root, directory } ) end, debug.traceback ) if result then for _, item in ipairs( result ) do print( ( item.ok and 'ok ' or 'FAIL ' ) .. item.name .. ( item.error or '' ) ) end end if channel then local cleaned, stopped = pcall( vim.rpcrequest, channel, 'nvim_exec_lua', [[ local pivi = require 'bugabinga.pivi' local children = _G.PIVI_E2E_CHILDREN or {} for _, child in ipairs(children) do child.stop() end for _, name in ipairs(pivi.session.list()) do table.insert(children, pivi.session.get(name).client) end pivi.session.stop_all() assert(vim.wait(15000, function() for _, child in ipairs(children) do if child.is_running() then return false end end return true end, 10), 'Pi child shutdown deadline') for index, child in ipairs(children) do assert(#child.failures() == 0, 'owned RPC child ' .. index .. ': ' .. table.concat(child.failures(), '\n')) end return true ]], {} ) if not cleaned or not stopped then ok = false; failure = 'Pi child cleanup failed: ' .. tostring( stopped ) end pcall( vim.rpcrequest, channel, 'nvim_command', 'qa!' ) pcall( vim.fn.chanclose, channel ) end local exited = server:wait( 20000 ) if exited.code == 124 then server:kill( 'sigterm' ) ok = false failure = ( failure and ( failure .. '\n' ) or '' ) .. 'child Neovim shutdown deadline' end vim.fn.delete( directory, 'rf' ) local settings_ok, settings_after = pcall( settings_content ) if not settings_ok or settings_after ~= settings_before then ok = false failure = 'global Pi settings changed or became unreadable: ' .. settings end if not ok then io.stderr:write( tostring( failure ) .. '\n' .. ( exited.stderr or '' ) ); os.exit( 1 ) end local failed = vim.tbl_filter( function( item ) return not item.ok end, result ) print( ( '%d scenarios, %d passed, %d failed' ):format( #result, #result - #failed, #failed ) ) os.exit( #failed == 0 and 0 or 1 )