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)
|
||||
|
||||
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])
|
||||
assert.equals('sh', compile_call.cmd[1])
|
||||
assert.equals('-c', compile_call.cmd[2])
|
||||
assert.is_not_nil(string.find(compile_call.cmd[3], 'g\\+\\+ test\\.cpp %-o test\\.run'))
|
||||
assert.is_not_nil(string.find(compile_call.cmd[3], '2>&1'))
|
||||
end)
|
||||
|
||||
it('handles multiple substitutions in single argument', function()
|
||||
|
|
@ -100,7 +100,7 @@ describe('cp.execute', function()
|
|||
execute.compile_generic(language_config, substitutions)
|
||||
|
||||
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)
|
||||
|
||||
|
|
@ -131,8 +131,8 @@ describe('cp.execute', function()
|
|||
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'))
|
||||
assert.equals('sh', compile_call.cmd[1])
|
||||
assert.is_not_nil(string.find(compile_call.cmd[3], '%-std=c\\+\\+17'))
|
||||
end)
|
||||
|
||||
it('handles compilation errors gracefully', function()
|
||||
|
|
@ -266,9 +266,10 @@ describe('cp.execute', function()
|
|||
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'))
|
||||
assert.equals('sh', mkdir_call.cmd[1])
|
||||
assert.is_not_nil(string.find(mkdir_call.cmd[3], 'mkdir'))
|
||||
assert.is_not_nil(string.find(mkdir_call.cmd[3], 'build'))
|
||||
assert.is_not_nil(string.find(mkdir_call.cmd[3], 'io'))
|
||||
end)
|
||||
end)
|
||||
|
||||
|
|
@ -316,8 +317,8 @@ describe('cp.execute', function()
|
|||
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])
|
||||
assert.equals('sh', echo_call.cmd[1])
|
||||
assert.is_not_nil(string.find(echo_call.cmd[3], 'echo hello'))
|
||||
end)
|
||||
|
||||
it('handles multiple consecutive substitutions', function()
|
||||
|
|
@ -332,8 +333,152 @@ describe('cp.execute', function()
|
|||
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])
|
||||
assert.equals('sh', call.cmd[1])
|
||||
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)
|
||||
|
|
|
|||
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