feat(test): test ansi colors with stderr/stdout merged output
This commit is contained in:
parent
b507dad4a7
commit
56c31b22b9
4 changed files with 176 additions and 130 deletions
|
|
@ -82,10 +82,10 @@ describe('cp.execute', function()
|
||||||
assert.is_true(#mock_system_calls > 0)
|
assert.is_true(#mock_system_calls > 0)
|
||||||
|
|
||||||
local compile_call = mock_system_calls[1]
|
local compile_call = mock_system_calls[1]
|
||||||
assert.equals('g++', compile_call.cmd[1])
|
assert.equals('sh', compile_call.cmd[1])
|
||||||
assert.equals('test.cpp', compile_call.cmd[2])
|
assert.equals('-c', compile_call.cmd[2])
|
||||||
assert.equals('-o', compile_call.cmd[3])
|
assert.is_not_nil(string.find(compile_call.cmd[3], 'g\\+\\+ test\\.cpp %-o test\\.run'))
|
||||||
assert.equals('test.run', compile_call.cmd[4])
|
assert.is_not_nil(string.find(compile_call.cmd[3], '2>&1'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('handles multiple substitutions in single argument', function()
|
it('handles multiple substitutions in single argument', function()
|
||||||
|
|
@ -100,7 +100,7 @@ describe('cp.execute', function()
|
||||||
execute.compile_generic(language_config, substitutions)
|
execute.compile_generic(language_config, substitutions)
|
||||||
|
|
||||||
local compile_call = mock_system_calls[1]
|
local compile_call = mock_system_calls[1]
|
||||||
assert.equals('-omain.out', compile_call.cmd[3])
|
assert.is_not_nil(string.find(compile_call.cmd[3], '%-omain\\.out'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
@ -131,8 +131,8 @@ describe('cp.execute', function()
|
||||||
assert.is_true(#mock_system_calls > 0)
|
assert.is_true(#mock_system_calls > 0)
|
||||||
|
|
||||||
local compile_call = mock_system_calls[1]
|
local compile_call = mock_system_calls[1]
|
||||||
assert.equals('g++', compile_call.cmd[1])
|
assert.equals('sh', compile_call.cmd[1])
|
||||||
assert.is_true(vim.tbl_contains(compile_call.cmd, '-std=c++17'))
|
assert.is_not_nil(string.find(compile_call.cmd[3], '%-std=c\\+\\+17'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('handles compilation errors gracefully', function()
|
it('handles compilation errors gracefully', function()
|
||||||
|
|
@ -266,9 +266,10 @@ describe('cp.execute', function()
|
||||||
execute.compile_generic(language_config, {})
|
execute.compile_generic(language_config, {})
|
||||||
|
|
||||||
local mkdir_call = mock_system_calls[1]
|
local mkdir_call = mock_system_calls[1]
|
||||||
assert.equals('mkdir', mkdir_call.cmd[1])
|
assert.equals('sh', mkdir_call.cmd[1])
|
||||||
assert.is_true(vim.tbl_contains(mkdir_call.cmd, 'build'))
|
assert.is_not_nil(string.find(mkdir_call.cmd[3], 'mkdir'))
|
||||||
assert.is_true(vim.tbl_contains(mkdir_call.cmd, 'io'))
|
assert.is_not_nil(string.find(mkdir_call.cmd[3], 'build'))
|
||||||
|
assert.is_not_nil(string.find(mkdir_call.cmd[3], 'io'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
@ -316,8 +317,8 @@ describe('cp.execute', function()
|
||||||
assert.equals(0, result.code)
|
assert.equals(0, result.code)
|
||||||
|
|
||||||
local echo_call = mock_system_calls[1]
|
local echo_call = mock_system_calls[1]
|
||||||
assert.equals('echo', echo_call.cmd[1])
|
assert.equals('sh', echo_call.cmd[1])
|
||||||
assert.equals('hello', echo_call.cmd[2])
|
assert.is_not_nil(string.find(echo_call.cmd[3], 'echo hello'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('handles multiple consecutive substitutions', function()
|
it('handles multiple consecutive substitutions', function()
|
||||||
|
|
@ -332,8 +333,152 @@ describe('cp.execute', function()
|
||||||
execute.compile_generic(language_config, substitutions)
|
execute.compile_generic(language_config, substitutions)
|
||||||
|
|
||||||
local call = mock_system_calls[1]
|
local call = mock_system_calls[1]
|
||||||
assert.equals('g++g++', call.cmd[1])
|
assert.equals('sh', call.cmd[1])
|
||||||
assert.equals('test.cpptest.cpp', call.cmd[2])
|
assert.is_not_nil(string.find(call.cmd[3], 'g\\+\\+g\\+\\+ test\\.cpptest\\.cpp'))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('stderr/stdout redirection', function()
|
||||||
|
it('should use stderr redirection (2>&1)', function()
|
||||||
|
local original_system = vim.system
|
||||||
|
local captured_command = nil
|
||||||
|
|
||||||
|
vim.system = function(cmd, opts)
|
||||||
|
captured_command = cmd
|
||||||
|
return {
|
||||||
|
wait = function()
|
||||||
|
return { code = 0, stdout = '', stderr = '' }
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local language_config = {
|
||||||
|
compile = { 'g++', '-std=c++17', '-o', '{binary}', '{source}' },
|
||||||
|
}
|
||||||
|
local substitutions = { source = 'test.cpp', binary = 'build/test', version = '17' }
|
||||||
|
execute.compile_generic(language_config, substitutions)
|
||||||
|
|
||||||
|
assert.is_not_nil(captured_command)
|
||||||
|
assert.equals('sh', captured_command[1])
|
||||||
|
assert.equals('-c', captured_command[2])
|
||||||
|
assert.is_not_nil(
|
||||||
|
string.find(captured_command[3], '2>&1'),
|
||||||
|
'Command should contain 2>&1 redirection'
|
||||||
|
)
|
||||||
|
|
||||||
|
vim.system = original_system
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('should return combined stdout+stderr in result', function()
|
||||||
|
local original_system = vim.system
|
||||||
|
local test_output = 'STDOUT: Hello\nSTDERR: Error message\n'
|
||||||
|
|
||||||
|
vim.system = function(cmd, opts)
|
||||||
|
return {
|
||||||
|
wait = function()
|
||||||
|
return { code = 1, stdout = test_output, stderr = '' }
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local language_config = {
|
||||||
|
compile = { 'g++', '-std=c++17', '-o', '{binary}', '{source}' },
|
||||||
|
}
|
||||||
|
local substitutions = { source = 'test.cpp', binary = 'build/test', version = '17' }
|
||||||
|
local result = execute.compile_generic(language_config, substitutions)
|
||||||
|
|
||||||
|
assert.equals(1, result.code)
|
||||||
|
assert.equals(test_output, result.stdout)
|
||||||
|
|
||||||
|
vim.system = original_system
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('integration tests', function()
|
||||||
|
local function compile_and_run_fixture(fixture_name)
|
||||||
|
local source_file = string.format('spec/fixtures/%s.cpp', fixture_name)
|
||||||
|
local binary_file = string.format('build/%s', fixture_name)
|
||||||
|
|
||||||
|
local language_config = {
|
||||||
|
compile = { 'g++', '-o', '{binary}', '{source}' },
|
||||||
|
test = { '{binary}' },
|
||||||
|
}
|
||||||
|
local substitutions = {
|
||||||
|
source = source_file,
|
||||||
|
binary = binary_file,
|
||||||
|
}
|
||||||
|
|
||||||
|
local compile_result = execute.compile_generic(language_config, substitutions)
|
||||||
|
|
||||||
|
if compile_result.code ~= 0 then
|
||||||
|
return compile_result
|
||||||
|
end
|
||||||
|
|
||||||
|
local start_time = vim.uv.hrtime()
|
||||||
|
local redirected_cmd = { 'sh', '-c', binary_file .. ' 2>&1' }
|
||||||
|
local result = vim.system(redirected_cmd, { timeout = 2000, text = false }):wait()
|
||||||
|
local execution_time = (vim.uv.hrtime() - start_time) / 1000000
|
||||||
|
|
||||||
|
local ansi = require('cp.ansi')
|
||||||
|
return {
|
||||||
|
stdout = ansi.bytes_to_string(result.stdout or ''),
|
||||||
|
stderr = ansi.bytes_to_string(result.stderr or ''),
|
||||||
|
code = result.code or 0,
|
||||||
|
time_ms = execution_time,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it('captures interleaved stderr/stdout with ANSI colors', function()
|
||||||
|
local result = compile_and_run_fixture('interleaved')
|
||||||
|
|
||||||
|
assert.equals(0, result.code)
|
||||||
|
|
||||||
|
local combined_output = result.stdout
|
||||||
|
assert.is_not_nil(string.find(combined_output, 'stdout:'))
|
||||||
|
assert.is_not_nil(string.find(combined_output, 'stderr:'))
|
||||||
|
assert.is_not_nil(string.find(combined_output, 'plain stdout'))
|
||||||
|
|
||||||
|
local ansi = require('cp.ansi')
|
||||||
|
local parsed = ansi.parse_ansi_text(combined_output)
|
||||||
|
local clean_text = table.concat(parsed.lines, '\n')
|
||||||
|
|
||||||
|
assert.is_not_nil(string.find(clean_text, 'Success'))
|
||||||
|
assert.is_not_nil(string.find(clean_text, 'Warning'))
|
||||||
|
|
||||||
|
local has_green = false
|
||||||
|
local has_red = false
|
||||||
|
local has_bold = false
|
||||||
|
|
||||||
|
for _, highlight in ipairs(parsed.highlights) do
|
||||||
|
if string.find(highlight.highlight_group, 'Green') then
|
||||||
|
has_green = true
|
||||||
|
end
|
||||||
|
if string.find(highlight.highlight_group, 'Red') then
|
||||||
|
has_red = true
|
||||||
|
end
|
||||||
|
if string.find(highlight.highlight_group, 'Bold') then
|
||||||
|
has_bold = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert.is_true(has_green, 'Should have green highlights')
|
||||||
|
assert.is_true(has_red, 'Should have red highlights')
|
||||||
|
assert.is_true(has_bold, 'Should have bold highlights')
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('handles compilation failures with combined output', function()
|
||||||
|
local result = compile_and_run_fixture('syntax_error')
|
||||||
|
|
||||||
|
assert.is_not_equals(0, result.code)
|
||||||
|
|
||||||
|
local compile_output = result.stdout
|
||||||
|
assert.is_not_nil(string.find(compile_output, 'error'))
|
||||||
|
|
||||||
|
local ansi = require('cp.ansi')
|
||||||
|
local parsed = ansi.parse_ansi_text(compile_output)
|
||||||
|
local clean_text = table.concat(parsed.lines, '\n')
|
||||||
|
|
||||||
|
assert.is_not_nil(string.find(clean_text, 'syntax_error.cpp'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
9
spec/fixtures/interleaved.cpp
vendored
Normal file
9
spec/fixtures/interleaved.cpp
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "\033[32mstdout: \033[1mSuccess\033[0m" << std::endl;
|
||||||
|
std::cerr << "\033[31mstderr: \033[1mWarning\033[0m" << std::endl;
|
||||||
|
std::cout << "plain stdout" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
8
spec/fixtures/syntax_error.cpp
vendored
Normal file
8
spec/fixtures/syntax_error.cpp
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "this will never compile" << std::endl
|
||||||
|
// missing semicolon above
|
||||||
|
undefined_function();
|
||||||
|
return 0
|
||||||
|
// missing semicolon again
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
local execute = require('cp.execute')
|
|
||||||
|
|
||||||
describe('execute module', function()
|
|
||||||
local test_ctx
|
|
||||||
local test_config
|
|
||||||
|
|
||||||
before_each(function()
|
|
||||||
test_ctx = {
|
|
||||||
source_file = 'test.cpp',
|
|
||||||
binary_file = 'build/test',
|
|
||||||
input_file = 'io/test.cpin',
|
|
||||||
output_file = 'io/test.cpout',
|
|
||||||
}
|
|
||||||
|
|
||||||
test_config = {
|
|
||||||
default_language = 'cpp',
|
|
||||||
cpp = {
|
|
||||||
version = 17,
|
|
||||||
compile = { 'g++', '-std=c++17', '-o', '{binary}', '{source}' },
|
|
||||||
test = { '{binary}' },
|
|
||||||
executable = nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('compile_generic', function()
|
|
||||||
it('should use stderr redirection (2>&1)', function()
|
|
||||||
local original_system = vim.system
|
|
||||||
local captured_command = nil
|
|
||||||
|
|
||||||
vim.system = function(cmd, opts)
|
|
||||||
captured_command = cmd
|
|
||||||
return {
|
|
||||||
wait = function()
|
|
||||||
return { code = 0, stdout = '', stderr = '' }
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local substitutions = { source = 'test.cpp', binary = 'build/test', version = '17' }
|
|
||||||
execute.compile_generic(test_config.cpp, substitutions)
|
|
||||||
|
|
||||||
assert.is_not_nil(captured_command)
|
|
||||||
assert.equals('sh', captured_command[1])
|
|
||||||
assert.equals('-c', captured_command[2])
|
|
||||||
assert.is_true(
|
|
||||||
string.find(captured_command[3], '2>&1') ~= nil,
|
|
||||||
'Command should contain 2>&1 redirection'
|
|
||||||
)
|
|
||||||
|
|
||||||
vim.system = original_system
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('should return combined stdout+stderr in result', function()
|
|
||||||
local original_system = vim.system
|
|
||||||
local test_output = 'STDOUT: Hello\nSTDERR: Error message\n'
|
|
||||||
|
|
||||||
vim.system = function(cmd, opts)
|
|
||||||
return {
|
|
||||||
wait = function()
|
|
||||||
return { code = 1, stdout = test_output, stderr = '' }
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local substitutions = { source = 'test.cpp', binary = 'build/test', version = '17' }
|
|
||||||
local result = execute.compile_generic(test_config.cpp, substitutions)
|
|
||||||
|
|
||||||
assert.equals(1, result.code)
|
|
||||||
assert.equals(test_output, result.stdout)
|
|
||||||
|
|
||||||
vim.system = original_system
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('compile_problem', function()
|
|
||||||
it('should return combined output in stderr field for compatibility', function()
|
|
||||||
local original_system = vim.system
|
|
||||||
local test_error_output = 'test.cpp:1:1: error: expected declaration\n'
|
|
||||||
|
|
||||||
vim.system = function(cmd, opts)
|
|
||||||
return {
|
|
||||||
wait = function()
|
|
||||||
return { code = 1, stdout = test_error_output, stderr = '' }
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local result = execute.compile_problem(test_ctx, test_config, false)
|
|
||||||
|
|
||||||
assert.is_false(result.success)
|
|
||||||
assert.equals(test_error_output, result.output)
|
|
||||||
|
|
||||||
vim.system = original_system
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('should return success=true when compilation succeeds', function()
|
|
||||||
local original_system = vim.system
|
|
||||||
|
|
||||||
vim.system = function(cmd, opts)
|
|
||||||
return {
|
|
||||||
wait = function()
|
|
||||||
return { code = 0, stdout = '', stderr = '' }
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local result = execute.compile_problem(test_ctx, test_config, false)
|
|
||||||
|
|
||||||
assert.is_true(result.success)
|
|
||||||
assert.is_nil(result.output)
|
|
||||||
|
|
||||||
vim.system = original_system
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue