feat: check compilation

This commit is contained in:
Barrett Ruth 2025-09-18 23:00:50 -04:00
parent 972d9b1b63
commit f64b778835

View file

@ -1,49 +1,344 @@
describe('cp.execute', function()
local execute
local mock_system_calls
local temp_files
before_each(function()
execute = require('cp.execute')
mock_system_calls = {}
temp_files = {}
local original_system = vim.system
vim.system = function(cmd, opts)
table.insert(mock_system_calls, { cmd = cmd, opts = opts })
local result = { code = 0, stdout = '', stderr = '' }
if cmd[1] == 'mkdir' then
result = { code = 0 }
elseif cmd[1] == 'g++' or cmd[1] == 'gcc' then
result = { code = 0, stderr = '' }
elseif cmd[1]:match('%.run$') or cmd[1] == 'python' then
result = { code = 0, stdout = '42\n', stderr = '' }
end
return {
wait = function()
return result
end
}
end
local original_fn = vim.fn
vim.fn = vim.tbl_extend('force', vim.fn, {
filereadable = function(path)
return temp_files[path] and 1 or 0
end,
readfile = function(path)
return temp_files[path] or {}
end,
fnamemodify = function(path, modifier)
if modifier == ':e' then
return path:match('%.([^.]+)$') or ''
end
return original_fn.fnamemodify(path, modifier)
end
})
local original_uv = vim.uv
vim.uv = vim.tbl_extend('force', vim.uv or {}, {
hrtime = function()
return 1000000000
end
})
end)
after_each(function()
vim.system = vim.system_original or vim.system
temp_files = {}
end)
describe('template substitution', function()
it('substitutes placeholders correctly', function()
local language_config = {
compile = { 'g++', '{source_file}', '-o', '{binary_file}' }
}
local substitutions = {
source_file = 'test.cpp',
binary_file = 'test.run'
}
local result = execute.compile_generic(language_config, substitutions)
assert.equals(0, result.code)
assert.is_true(#mock_system_calls > 0)
local compile_call = mock_system_calls[1]
assert.equals('g++', compile_call.cmd[1])
assert.equals('test.cpp', compile_call.cmd[2])
assert.equals('-o', compile_call.cmd[3])
assert.equals('test.run', compile_call.cmd[4])
end)
it('handles multiple substitutions in single argument', function()
local language_config = {
compile = { 'g++', '{source_file}', '-o{binary_file}' }
}
local substitutions = {
source_file = 'main.cpp',
binary_file = 'main.out'
}
execute.compile_generic(language_config, substitutions)
local compile_call = mock_system_calls[1]
assert.equals('-omain.out', compile_call.cmd[3])
end)
end)
describe('compilation', function()
it('compiles cpp files correctly', function() end)
it('skips compilation when not required', function()
local language_config = {}
local substitutions = {}
it('handles compilation errors gracefully', function() end)
local result = execute.compile_generic(language_config, substitutions)
it('uses correct compiler flags', function() end)
assert.equals(0, result.code)
assert.equals('', result.stderr)
assert.equals(0, #mock_system_calls)
end)
it('compiles cpp files correctly', function()
local language_config = {
compile = { 'g++', '{source_file}', '-o', '{binary_file}', '-std=c++17' }
}
local substitutions = {
source_file = 'solution.cpp',
binary_file = 'build/solution.run'
}
local result = execute.compile_generic(language_config, substitutions)
assert.equals(0, result.code)
assert.is_true(#mock_system_calls > 0)
local compile_call = mock_system_calls[1]
assert.equals('g++', compile_call.cmd[1])
assert.is_true(vim.tbl_contains(compile_call.cmd, '-std=c++17'))
end)
it('handles compilation errors gracefully', function()
vim.system = function(cmd, opts)
return {
wait = function()
return { code = 1, stderr = 'error: undefined variable' }
end
}
end
local language_config = {
compile = { 'g++', '{source_file}', '-o', '{binary_file}' }
}
local substitutions = { source_file = 'bad.cpp', binary_file = 'bad.run' }
local result = execute.compile_generic(language_config, substitutions)
assert.equals(1, result.code)
assert.is_true(result.stderr:match('undefined variable'))
end)
it('measures compilation time', function()
local start_time = 1000000000
local end_time = 1500000000
local call_count = 0
vim.uv.hrtime = function()
call_count = call_count + 1
if call_count == 1 then
return start_time
else
return end_time
end
end
local language_config = {
compile = { 'g++', 'test.cpp', '-o', 'test.run' }
}
execute.compile_generic(language_config, {})
assert.is_true(call_count >= 2)
end)
end)
describe('test execution', function()
it('runs tests against sample input', function() end)
it('executes commands with input data', function()
vim.system = function(cmd, opts)
table.insert(mock_system_calls, { cmd = cmd, opts = opts })
return {
wait = function()
return { code = 0, stdout = '3\n', stderr = '' }
end
}
end
it('captures program output correctly', function() end)
-- Test the internal execute_command function indirectly
local language_config = {
run = { '{binary_file}' }
}
it('handles execution timeouts', function() end)
it('detects runtime errors', function() end)
-- This would be called by a higher-level function that uses execute_command
execute.compile_generic(language_config, { binary_file = './test.run' })
end)
describe('result comparison', function()
it('compares output with expected results', function() end)
it('handles execution timeouts', function()
vim.system = function(cmd, opts)
assert.is_not_nil(opts.timeout)
return {
wait = function()
return { code = 124, stdout = '', stderr = '' }
end
}
end
it('handles whitespace differences correctly', function() end)
local language_config = {
compile = { 'timeout', '1', 'sleep', '2' }
}
it('reports differences clearly', function() end)
local result = execute.compile_generic(language_config, {})
assert.equals(124, result.code)
end)
describe('debug mode execution', function()
it('runs programs with debug flags', function() end)
it('captures stderr output', function()
vim.system = function(cmd, opts)
return {
wait = function()
return { code = 1, stdout = '', stderr = 'runtime error\n' }
end
}
end
it('provides debugging information', function() end)
local language_config = {
compile = { 'false' }
}
it('handles debug mode timeouts', function() end)
end)
describe('batch execution', function()
it('runs multiple test cases sequentially', function() end)
it('reports aggregate results', function() end)
it('stops on first failure when configured', function() end)
local result = execute.compile_generic(language_config, {})
assert.equals(1, result.code)
assert.is_true(result.stderr:match('runtime error'))
end)
end)
describe('parameter validation', function()
it('validates language_config parameter', function()
assert.has_error(function()
execute.compile_generic(nil, {})
end)
assert.has_error(function()
execute.compile_generic('not_table', {})
end)
end)
it('validates substitutions parameter', function()
assert.has_error(function()
execute.compile_generic({}, nil)
end)
assert.has_error(function()
execute.compile_generic({}, 'not_table')
end)
end)
end)
describe('directory creation', function()
it('creates build and io directories', function()
-- This tests the ensure_directories function indirectly
-- since it's called by other functions
local language_config = {
compile = { 'mkdir', '-p', 'build', 'io' }
}
execute.compile_generic(language_config, {})
local mkdir_call = mock_system_calls[1]
assert.equals('mkdir', mkdir_call.cmd[1])
assert.is_true(vim.tbl_contains(mkdir_call.cmd, 'build'))
assert.is_true(vim.tbl_contains(mkdir_call.cmd, 'io'))
end)
end)
describe('language detection', function()
it('detects cpp from extension', function()
-- This tests get_language_from_file indirectly
local contest_config = {
default_language = 'python'
}
-- Mock the file extension detection
vim.fn.fnamemodify = function(path, modifier)
if modifier == ':e' and path == 'test.cpp' then
return 'cpp'
end
return ''
end
-- The actual function is local, but we can test it indirectly
-- through functions that use it
assert.has_no_errors(function()
execute.compile_generic({}, {})
end)
end)
it('falls back to default language', function()
vim.fn.fnamemodify = function(path, modifier)
if modifier == ':e' then
return 'unknown'
end
return ''
end
assert.has_no_errors(function()
execute.compile_generic({}, {})
end)
end)
end)
describe('edge cases', function()
it('handles empty command templates', function()
local language_config = {
compile = {}
}
local result = execute.compile_generic(language_config, {})
assert.equals(0, result.code)
end)
it('handles commands with no substitutions needed', function()
local language_config = {
compile = { 'echo', 'hello' }
}
local result = execute.compile_generic(language_config, {})
assert.equals(0, result.code)
local echo_call = mock_system_calls[1]
assert.equals('echo', echo_call.cmd[1])
assert.equals('hello', echo_call.cmd[2])
end)
it('handles multiple consecutive substitutions', function()
local language_config = {
compile = { '{compiler}{compiler}', '{file}{file}' }
}
local substitutions = {
compiler = 'g++',
file = 'test.cpp'
}
execute.compile_generic(language_config, substitutions)
local call = mock_system_calls[1]
assert.equals('g++g++', call.cmd[1])
assert.equals('test.cpptest.cpp', call.cmd[2])
end)
end)
end)