From a649dfd2ba023263d9e786981050897a332a45f4 Mon Sep 17 00:00:00 2001 From: Maksim Tiushev Date: Thu, 4 Dec 2025 08:38:01 +0000 Subject: [PATCH] server: fix artifact handling reset on restart Fix a bug when server initialization didn't reset artifact handling, which caused stale artifacts to persist after server restarts. Closes #409 --- CHANGELOG.md | 2 + luatest/replica_set.lua | 6 +- luatest/runner.lua | 29 +++- luatest/server.lua | 8 +- luatest/test_instance.lua | 5 + test/artifacts/hooks_test.lua | 116 +++++++++------ test/collect_rs_artifacts_test.lua | 158 +++++++++++--------- test/collect_server_artifacts_test.lua | 107 +++++++++----- test/replica_set_test.lua | 197 ++++++++++++++++--------- test/server_test.lua | 128 +++++++++++++--- 10 files changed, 497 insertions(+), 259 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d008d25..fcada198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Fixed a bug when server initialization didn't reset artifact handling, + causing stale artifacts to persist after server restarts (gh-409). - Fixed a bug when the JUnit reporter generated invalid XML for parameterized tests with string arguments (gh-407). - Group and suite hooks must now be registered using the call-style diff --git a/luatest/replica_set.lua b/luatest/replica_set.lua index 6b1716f6..9d05975f 100644 --- a/luatest/replica_set.lua +++ b/luatest/replica_set.lua @@ -184,12 +184,10 @@ function ReplicaSet:stop() end end ---- Stop all servers in the replica set and save their artifacts if the test fails. +--- Stop all servers in the replica set. -- This function should be used only at the end of the test (`after_test`, -- `after_each`, `after_all` hooks) to terminate all server processes in --- the replica set. Besides process termination, it saves the contents of --- each server working directory to the `/artifacts` directory --- for further analysis if the test fails. +-- the replica set. function ReplicaSet:drop() for _, server in ipairs(self.servers) do server:drop() diff --git a/luatest/runner.lua b/luatest/runner.lua index 82c50ffe..b1098548 100644 --- a/luatest/runner.lua +++ b/luatest/runner.lua @@ -367,6 +367,26 @@ function Runner.mt:start_test(test) self.output:start_test(test) end +local function save_failed_test_artifacts(node) + if node.artifacts_saved then + return + end + + if not node.had_failure and node:is('success') then + return + end + + if utils.table_len(node.servers) == 0 then + return + end + + for _, server in pairs(node.servers) do + server:save_artifacts() + end + + node.artifacts_saved = true +end + function Runner.mt:update_status(node, err) -- "err" is expected to be a table / result from protected_call() if err.status == 'success' then @@ -377,11 +397,7 @@ function Runner.mt:update_status(node, err) elseif err.status == 'fail' or err.status == 'error' or err.status == 'skip' or err.status == 'xfail' or err.status == 'xsuccess' then node:update_status(err.status, err.message, err.trace) - if utils.table_len(node.servers) > 0 then - for _, server in pairs(node.servers) do - server:save_artifacts() - end - end + save_failed_test_artifacts(node) else error('No such status: ' .. pp.tostring(err.status)) end @@ -391,6 +407,7 @@ end function Runner.mt:end_test(node) node.duration = clock.time() - node.start_time node.start_time = nil + self.output:end_test(node) if node:is('error') or node:is('fail') or node:is('xsuccess') then @@ -479,7 +496,7 @@ end function Runner.mt:run_tests(tests_list) -- Make seed for ordering not affect other random numbers. math.randomseed(os.time()) - rawset(_G, 'current_test', {value = nil}) + rawset(_G, 'current_test', {runner = self, value = nil}) for _ = 1, self.exe_repeat_group or 1 do local last_group for _, test in ipairs(tests_list) do diff --git a/luatest/server.lua b/luatest/server.lua index 839afbc9..6a5ca7cd 100644 --- a/luatest/server.lua +++ b/luatest/server.lua @@ -531,7 +531,6 @@ local function wait_for_condition(cond_desc, server, func, ...) local deadline = clock.time() + WAIT_TIMEOUT while true do if not server.process:is_alive() then - server:save_artifacts() error(('Process is terminated when waiting for "%s" condition for server (alias: %s, workdir: %s, pid: %d)') :format(cond_desc, server.alias, fio.basename(server.workdir), server.process.pid)) end @@ -539,7 +538,6 @@ local function wait_for_condition(cond_desc, server, func, ...) return end if clock.time() > deadline then - server:save_artifacts() error(('Timed out to wait for "%s" condition for server (alias: %s, workdir: %s, pid: %d) within %ds') :format(cond_desc, server.alias, fio.basename(server.workdir), server.process.pid, WAIT_TIMEOUT)) end @@ -585,15 +583,11 @@ function Server:stop() end end ---- Stop the server and save its artifacts if the test fails. +-- Stop the server. -- This function should be used only at the end of the test (`after_test`, -- `after_each`, `after_all` hooks) to terminate the server process. --- Besides process termination, it saves the contents of the server --- working directory to the `/artifacts` directory for further --- analysis if the test fails. function Server:drop() self:stop() - self:save_artifacts() self.instance_id = nil self.instance_uuid = nil diff --git a/luatest/test_instance.lua b/luatest/test_instance.lua index a4db250e..460130ff 100644 --- a/luatest/test_instance.lua +++ b/luatest/test_instance.lua @@ -17,9 +17,14 @@ end function TestInstance.mt:initialize() self.status = 'success' self.servers = {} + self.had_failure = false + self.artifacts_saved = false end function TestInstance.mt:update_status(status, message, trace) + if status ~= 'success' then + self.had_failure = true + end self.status = status self.message = message self.trace = trace diff --git a/test/artifacts/hooks_test.lua b/test/artifacts/hooks_test.lua index 9b805b2a..278a19bc 100644 --- a/test/artifacts/hooks_test.lua +++ b/test/artifacts/hooks_test.lua @@ -1,9 +1,9 @@ local t = require('luatest') local utils = require('luatest.utils') local fio = require('fio') +local helper = require('test.helpers.general') local g = t.group() -local Server = t.Server local function is_server_in_test(server, test) for _, s in pairs(test.servers) do @@ -14,59 +14,81 @@ local function is_server_in_test(server, test) return false end -g.public = Server:new({alias = 'public'}) -g.public:start() +g.test_association_between_test_and_servers = function() + local artifacts_paths -g.before_all(function() - g.all = Server:new({alias = 'all9'}) - g.all:start() -end) + local status = helper.run_suite(function(lu2) + local cg = lu2.group() + local Server = lu2.Server -g.before_each(function() - g.each = Server:new({alias = 'each'}) - g.each:start() -end) + cg.public = Server:new({alias = 'public'}) + cg.public:start() -g.before_test('test_association_between_test_and_servers', function() - g.test = Server:new({alias = 'test'}) - g.test:start() -end) + cg.before_all(function() + cg.all = Server:new({alias = 'all9'}) + cg.all:start() + end) -g.test_association_between_test_and_servers = function() - g.internal = Server:new({alias = 'internal'}) - g.internal:start() + cg.before_each(function() + cg.each = Server:new({alias = 'each'}) + cg.each:start() + end) - local test = rawget(_G, 'current_test').value + cg.before_test('test_inner', function() + cg.test = Server:new({alias = 'test'}) + cg.test:start() + end) - -- test static association - t.assert(is_server_in_test(g.internal, test)) - t.assert(is_server_in_test(g.each, test)) - t.assert(is_server_in_test(g.test, test)) - t.assert_not(is_server_in_test(g.public, test)) + cg.test_inner = function() + cg.internal = Server:new({alias = 'internal'}) + cg.internal:start() - g.public:exec(function() return 1 + 1 end) - g.all:exec(function() return 1 + 1 end) + local test = rawget(_G, 'current_test').value - -- test dynamic association - t.assert(is_server_in_test(g.public, test)) - t.assert(is_server_in_test(g.all, test)) + -- test static association + lu2.assert(is_server_in_test(cg.internal, test)) + lu2.assert(is_server_in_test(cg.each, test)) + lu2.assert(is_server_in_test(cg.test, test)) + lu2.assert_not(is_server_in_test(cg.public, test)) - t.assert(utils.table_len(test.servers) == 5) -end + cg.public:exec(function() return 1 + 1 end) + cg.all:exec(function() return 1 + 1 end) + + -- test dynamic association + lu2.assert(is_server_in_test(cg.public, test)) + lu2.assert(is_server_in_test(cg.all, test)) + + lu2.assert(utils.table_len(test.servers) == 5) + + artifacts_paths = { + test = cg.test.artifacts, + each = cg.each.artifacts, + all = cg.all.artifacts, + public = cg.public.artifacts, + internal = cg.internal.artifacts, + } + + lu2.fail('trigger artifact saving') + end + + cg.after_test('test_inner', function() + cg.internal:drop() + cg.test:drop() + end) + + cg.after_each(function() + cg.each:drop() + end) + + cg.after_all(function() + cg.all:drop() + cg.public:drop() + end) + end, {'--no-clean'}) -g.after_test('test_association_between_test_and_servers', function() - g.internal:drop() - g.test:drop() - t.assert(fio.path.exists(g.test.artifacts)) -end) - -g.after_each(function() - g.each:drop() - t.assert(fio.path.exists(g.each.artifacts)) -end) - -g.after_all(function() - g.all:drop() - t.assert(fio.path.exists(g.all.artifacts)) - g.public:drop() -end) + t.assert_equals(status, 1) + + for _, path in pairs(artifacts_paths) do + t.assert(fio.path.exists(path)) + end +end diff --git a/test/collect_rs_artifacts_test.lua b/test/collect_rs_artifacts_test.lua index a785018c..795b457a 100644 --- a/test/collect_rs_artifacts_test.lua +++ b/test/collect_rs_artifacts_test.lua @@ -2,6 +2,7 @@ local fio = require('fio') local t = require('luatest') local utils = require('luatest.utils') local ReplicaSet = require('luatest.replica_set') +local helper = require('test.helpers.general') local g = t.group() local Server = t.Server @@ -12,25 +13,24 @@ local function build_specific_replica_set(alias_suffix) replication_timeout = 0.1, replication_connect_timeout = 1, replication_sync_lag = 0.01, - replication_connect_quorum = 3, + replication_connect_quorum = 2, } - local s1_alias = ('replica1-%s'):format(alias_suffix) - local s2_alias = ('replica2-%s'):format(alias_suffix) - local s3_alias = ('replica3-%s'):format(alias_suffix) - - box_cfg = utils.merge( - table.deepcopy(box_cfg), - { - replication ={ - Server.build_listen_uri(s1_alias, rs.id), - Server.build_listen_uri(s2_alias, rs.id), - Server.build_listen_uri(s3_alias, rs.id) - }}) - - rs:build_and_add_server({alias = s1_alias, box_cfg = box_cfg}) - rs:build_and_add_server({alias = s2_alias, box_cfg = box_cfg}) - rs:build_and_add_server({alias = s3_alias, box_cfg = box_cfg}) + local aliases = { + ('replica1-%s'):format(alias_suffix), + ('replica2-%s'):format(alias_suffix), + } + + box_cfg = utils.merge(table.deepcopy(box_cfg), { + replication = { + Server.build_listen_uri(aliases[1], rs.id), + Server.build_listen_uri(aliases[2], rs.id), + }, + }) + + for _, alias in ipairs(aliases) do + rs:build_and_add_server({alias = alias, box_cfg = box_cfg}) + end return rs end @@ -38,64 +38,82 @@ local function get_replica_set_artifacts_path(rs) return ('%s/artifacts/%s'):format(rs._server.vardir, rs.id) end -local function get_server_artifacts_path_by_alias(rs, position, alias_node) - local rs_artifacts = get_replica_set_artifacts_path(rs) - return ('%s/%s'):format( - rs_artifacts, - rs:get_server(('replica%s-%s'):format(position, alias_node)).id) -end - -local function assert_artifacts_paths(rs, alias_suffix) - t.assert_equals(fio.path.exists(get_replica_set_artifacts_path(rs)), true) - t.assert_equals(fio.path.is_dir(get_replica_set_artifacts_path(rs)), true) - - t.assert_equals( - fio.path.exists(get_server_artifacts_path_by_alias(rs, 1, alias_suffix)), true) - t.assert_equals( - fio.path.is_dir(get_server_artifacts_path_by_alias(rs, 1, alias_suffix)), true) +local function build_artifacts_paths(rs) + local paths = { + rs = get_replica_set_artifacts_path(rs), + servers = {}, + } - t.assert_equals( - fio.path.exists(get_server_artifacts_path_by_alias(rs, 2, alias_suffix)), true) - t.assert_equals( - fio.path.is_dir(get_server_artifacts_path_by_alias(rs, 2, alias_suffix)), true) + for _, server in ipairs(rs.servers) do + table.insert(paths.servers, ('%s/%s'):format(paths.rs, server.id)) + end - t.assert_equals( - fio.path.exists(get_server_artifacts_path_by_alias(rs, 3, alias_suffix)), true) - t.assert_equals( - fio.path.is_dir(get_server_artifacts_path_by_alias(rs, 3, alias_suffix)), true) + return paths end -g.before_all(function() - g.rs_all = build_specific_replica_set('all') +local function assert_artifacts_paths(paths) + t.assert_equals(fio.path.exists(paths.rs), true) + t.assert_equals(fio.path.is_dir(paths.rs), true) - g.rs_all:start() - g.rs_all:wait_for_fullmesh() -end) - -g.before_each(function() - g.rs_each = build_specific_replica_set('each') - - g.rs_each:start() - g.rs_each:wait_for_fullmesh() -end) - -g.before_test('test_foo', function() - g.rs_test = build_specific_replica_set('test') - - g.rs_test:start() - g.rs_test:wait_for_fullmesh() -end) + for _, server_path in ipairs(paths.servers) do + t.assert_equals(fio.path.exists(server_path), true) + t.assert_equals(fio.path.is_dir(server_path), true) + end +end g.test_foo = function() - local test = rawget(_G, 'current_test') - - test.status = 'fail' - g.rs_test:drop() - g.rs_each:drop() - g.rs_all:drop() - test.status = 'success' - - assert_artifacts_paths(g.rs_test, 'test') - assert_artifacts_paths(g.rs_each, 'each') - assert_artifacts_paths(g.rs_all, 'all') + local paths + + local status = helper.run_suite(function(lu2) + local cg = lu2.group() + + cg.before_all(function() + cg.rs_all = build_specific_replica_set('all') + cg.rs_all:start() + end) + + cg.before_each(function() + cg.rs_each = build_specific_replica_set('each') + cg.rs_each:start() + end) + + cg.before_test('test_failure', function() + cg.rs_test = build_specific_replica_set('test') + cg.rs_test:start() + end) + + cg.test_failure = function() + for _, rs in pairs({cg.rs_test, cg.rs_each, cg.rs_all}) do + for _, server in pairs(rs.servers) do + server:exec(function() return true end) + end + end + + paths = { + test = build_artifacts_paths(cg.rs_test), + each = build_artifacts_paths(cg.rs_each), + all = build_artifacts_paths(cg.rs_all), + } + + lu2.fail('trigger artifact saving') + end + + cg.after_test('test_failure', function() + cg.rs_test:drop() + end) + + cg.after_each(function() + cg.rs_each:drop() + end) + + cg.after_all(function() + cg.rs_all:drop() + end) + end, {'--no-clean'}) + + t.assert_equals(status, 1) + + assert_artifacts_paths(paths.test) + assert_artifacts_paths(paths.each) + assert_artifacts_paths(paths.all) end diff --git a/test/collect_server_artifacts_test.lua b/test/collect_server_artifacts_test.lua index 00e39ed9..be04cbb9 100644 --- a/test/collect_server_artifacts_test.lua +++ b/test/collect_server_artifacts_test.lua @@ -1,55 +1,82 @@ local fio = require('fio') local t = require('luatest') +local helper = require('test.helpers.general') local g = t.group() -local Server = t.Server - local function assert_artifacts_path(s) - t.assert_equals(fio.path.exists(s.artifacts), true) - t.assert_equals(fio.path.is_dir(s.artifacts), true) + t.assert(fio.path.exists(s)) + t.assert(fio.path.is_dir(s)) end -g.before_all(function() - g.s_all = Server:new({alias = 'all'}) - g.s_all2 = Server:new({alias = 'all2'}) +g.test_foo = function() + local artifacts_paths - g.s_all:start() - g.s_all2:start() -end) + local status = helper.run_suite(function(lu2) + local cg = lu2.group() + local Server = lu2.Server -g.before_each(function() - g.s_each = Server:new({alias = 'each'}) - g.s_each2 = Server:new({alias = 'each2'}) + cg.before_all(function() + cg.s_all = Server:new({alias = 'all'}) + cg.s_all2 = Server:new({alias = 'all2'}) - g.s_each:start() - g.s_each2:start() -end) + cg.s_all:start() + cg.s_all2:start() + end) -g.before_test('test_foo', function() - g.s_test = Server:new({alias = 'test'}) - g.s_test2 = Server:new({alias = 'test2'}) + cg.before_each(function() + cg.s_each = Server:new({alias = 'each'}) + cg.s_each2 = Server:new({alias = 'each2'}) - g.s_test:start() - g.s_test2:start() -end) + cg.s_each:start() + cg.s_each2:start() + end) -g.test_foo = function() - local test = rawget(_G, 'current_test') - - test.status = 'fail' - g.s_test:drop() - g.s_test2:drop() - g.s_each:drop() - g.s_each2:drop() - g.s_all:drop() - g.s_all2:drop() - test.status = 'success' - - assert_artifacts_path(g.s_test) - assert_artifacts_path(g.s_test2) - assert_artifacts_path(g.s_each) - assert_artifacts_path(g.s_each2) - assert_artifacts_path(g.s_all) - assert_artifacts_path(g.s_all2) + cg.before_test('test_failure', function() + cg.s_test = Server:new({alias = 'test'}) + cg.s_test2 = Server:new({alias = 'test2'}) + + cg.s_test:start() + cg.s_test2:start() + end) + + cg.test_failure = function() + for _, server in ipairs({cg.s_test, cg.s_test2, cg.s_each, + cg.s_each2, cg.s_all, cg.s_all2}) do + server:exec(function() return true end) + end + + artifacts_paths = { + cg.s_test.artifacts, + cg.s_test2.artifacts, + cg.s_each.artifacts, + cg.s_each2.artifacts, + cg.s_all.artifacts, + cg.s_all2.artifacts, + } + + lu2.fail('trigger artifact saving') + end + + cg.after_test('test_failure', function() + cg.s_test:drop() + cg.s_test2:drop() + end) + + cg.after_each(function() + cg.s_each:drop() + cg.s_each2:drop() + end) + + cg.after_all(function() + cg.s_all:drop() + cg.s_all2:drop() + end) + end, {'--no-clean'}) + + t.assert_equals(status, 1) + + for _, path in ipairs(artifacts_paths) do + assert_artifacts_path(path) + end end diff --git a/test/replica_set_test.lua b/test/replica_set_test.lua index 45ca3466..acd01c0e 100644 --- a/test/replica_set_test.lua +++ b/test/replica_set_test.lua @@ -1,93 +1,156 @@ local fio = require('fio') local t = require('luatest') +local helper = require('test.helpers.general') local ReplicaSet = require('luatest.replica_set') local g = t.group() local Server = t.Server +local deferred_artifact_checks = {} -g.before_each(function() - g.rs = ReplicaSet:new() - g.box_cfg = { +local function build_box_cfg(rs) + return { replication_timeout = 0.1, replication_connect_timeout = 10, replication_sync_lag = 0.01, replication_connect_quorum = 3, replication = { - Server.build_listen_uri('replica1', g.rs.id), - Server.build_listen_uri('replica2', g.rs.id), - Server.build_listen_uri('replica3', g.rs.id), + Server.build_listen_uri('replica1', rs.id), + Server.build_listen_uri('replica2', rs.id), + Server.build_listen_uri('replica3', rs.id), } } -end) - -g.before_test('test_save_rs_artifacts_when_test_failed', function() - g.rs:build_and_add_server({alias = 'replica1', box_cfg = g.box_cfg}) - g.rs:build_and_add_server({alias = 'replica2', box_cfg = g.box_cfg}) - g.rs:build_and_add_server({alias = 'replica3', box_cfg = g.box_cfg}) - g.rs:start() +end - g.rs_artifacts = ('%s/artifacts/%s'):format(Server.vardir, g.rs.id) - g.s1_artifacts = ('%s/%s'):format(g.rs_artifacts, g.rs:get_server('replica1').id) - g.s2_artifacts = ('%s/%s'):format(g.rs_artifacts, g.rs:get_server('replica2').id) - g.s3_artifacts = ('%s/%s'):format(g.rs_artifacts, g.rs:get_server('replica3').id) +g.before_each(function() + g.rs = ReplicaSet:new() + g.box_cfg = build_box_cfg(g.rs) end) g.test_save_rs_artifacts_when_test_failed = function() - local test = rawget(_G, 'current_test') - -- the test must be failed to save artifacts - test.status = 'fail' - g.rs:drop() - test.status = 'success' - - t.assert_equals(fio.path.exists(g.rs_artifacts), true) - t.assert_equals(fio.path.is_dir(g.rs_artifacts), true) - - t.assert_equals(fio.path.exists(g.s1_artifacts), true) - t.assert_equals(fio.path.is_dir(g.s1_artifacts), true) - - t.assert_equals(fio.path.exists(g.s2_artifacts), true) - t.assert_equals(fio.path.is_dir(g.s2_artifacts), true) - - t.assert_equals(fio.path.exists(g.s3_artifacts), true) - t.assert_equals(fio.path.is_dir(g.s3_artifacts), true) + local artifact_paths + + local status = helper.run_suite(function(luatest) + local cg = luatest.group() + + cg.before_test('test_failure', function() + cg.rs = ReplicaSet:new() + local box_cfg = build_box_cfg(cg.rs) + + cg.rs:build_and_add_server({alias = 'replica1', box_cfg = box_cfg}) + cg.rs:build_and_add_server({alias = 'replica2', box_cfg = box_cfg}) + cg.rs:build_and_add_server({alias = 'replica3', box_cfg = box_cfg}) + cg.rs:start() + + local rs_artifacts = ('%s/artifacts/%s'):format(Server.vardir, cg.rs.id) + artifact_paths = { + rs = rs_artifacts, + servers = { + ('%s/%s'):format(rs_artifacts, cg.rs:get_server('replica1').id), + ('%s/%s'):format(rs_artifacts, cg.rs:get_server('replica2').id), + ('%s/%s'):format(rs_artifacts, cg.rs:get_server('replica3').id), + }, + } + end) + + cg.test_failure = function() + for _, server in pairs(cg.rs.servers) do + server:exec(function() return true end) + end + + luatest.fail('trigger artifact saving') + end + + cg.after_test('test_failure', function() + cg.rs:drop() + end) + end, {'--no-clean'}) + + t.assert_equals(status, 1) + + table.insert(deferred_artifact_checks, function() + t.assert_equals(fio.path.exists(artifact_paths.rs), true) + t.assert_equals(fio.path.is_dir(artifact_paths.rs), true) + + for _, path in ipairs(artifact_paths.servers) do + t.assert_equals(fio.path.exists(path), true) + t.assert_equals(fio.path.is_dir(path), true) + end + end) end -g.before_test('test_save_rs_artifacts_when_server_workdir_passed', function() - local s1_workdir = ('%s/%s'):format(Server.vardir, os.tmpname()) - local s2_workdir = ('%s/%s'):format(Server.vardir, os.tmpname()) - local s3_workdir = ('%s/%s'):format(Server.vardir, os.tmpname()) - - g.rs:build_and_add_server({workdir = s1_workdir, alias = 'replica1', box_cfg = g.box_cfg}) - g.rs:build_and_add_server({workdir = s2_workdir, alias = 'replica2', box_cfg = g.box_cfg}) - g.rs:build_and_add_server({workdir = s3_workdir, alias = 'replica3', box_cfg = g.box_cfg}) - g.rs:start() - - g.rs_artifacts = ('%s/artifacts/%s'):format(Server.vardir, g.rs.id) - g.s1_artifacts = ('%s/%s'):format(g.rs_artifacts, g.rs:get_server('replica1').id) - g.s2_artifacts = ('%s/%s'):format(g.rs_artifacts, g.rs:get_server('replica2').id) - g.s3_artifacts = ('%s/%s'):format(g.rs_artifacts, g.rs:get_server('replica3').id) -end) - g.test_save_rs_artifacts_when_server_workdir_passed = function() - local test = rawget(_G, 'current_test') - -- the test must be failed to save artifacts - test.status = 'fail' - g.rs:drop() - test.status = 'success' - - t.assert_equals(fio.path.exists(g.rs_artifacts), true) - t.assert_equals(fio.path.is_dir(g.rs_artifacts), true) - - t.assert_equals(fio.path.exists(g.s1_artifacts), true) - t.assert_equals(fio.path.is_dir(g.s1_artifacts), true) - - t.assert_equals(fio.path.exists(g.s2_artifacts), true) - t.assert_equals(fio.path.is_dir(g.s2_artifacts), true) + local artifact_paths + + local status = helper.run_suite(function(luatest) + local cg = luatest.group() + + cg.before_test('test_failure', function() + cg.rs = ReplicaSet:new() + local box_cfg = build_box_cfg(cg.rs) + + cg.rs:build_and_add_server({ + workdir = ('%s/%s'):format(Server.vardir, os.tmpname()), + alias = 'replica1', + box_cfg = box_cfg, + }) + cg.rs:build_and_add_server({ + workdir = ('%s/%s'):format(Server.vardir, os.tmpname()), + alias = 'replica2', + box_cfg = box_cfg, + }) + cg.rs:build_and_add_server({ + workdir = ('%s/%s'):format(Server.vardir, os.tmpname()), + alias = 'replica3', + box_cfg = box_cfg, + }) + cg.rs:start() + + local rs_artifacts = ('%s/artifacts/%s'):format(Server.vardir, cg.rs.id) + artifact_paths = { + rs = rs_artifacts, + servers = { + ('%s/%s'):format(rs_artifacts, cg.rs:get_server('replica1').id), + ('%s/%s'):format(rs_artifacts, cg.rs:get_server('replica2').id), + ('%s/%s'):format(rs_artifacts, cg.rs:get_server('replica3').id), + }, + } + end) + + cg.test_failure = function() + for _, server in pairs(cg.rs.servers) do + server:exec(function() return true end) + end + + luatest.fail('trigger artifact saving') + end + + cg.after_test('test_failure', function() + cg.rs:drop() + end) + end, {'--no-clean'}) + + t.assert_equals(status, 1) + + table.insert(deferred_artifact_checks, function() + t.assert_equals(fio.path.exists(artifact_paths.rs), true) + t.assert_equals(fio.path.is_dir(artifact_paths.rs), true) + + for _, path in ipairs(artifact_paths.servers) do + t.assert_equals(fio.path.exists(path), true) + t.assert_equals(fio.path.is_dir(path), true) + end + end) +end - t.assert_equals(fio.path.exists(g.s3_artifacts), true) - t.assert_equals(fio.path.is_dir(g.s3_artifacts), true) +g.after_each(function() + g.rs:drop() +end) -end +g.after_all(function() + for _, check in ipairs(deferred_artifact_checks) do + check() + end +end) g.test_rs_no_socket_collision_with_custom_alias = function() local s1 = g.rs:build_server({alias = 'foo'}) diff --git a/test/server_test.lua b/test/server_test.lua index c7b7d8c8..e6e79b91 100644 --- a/test/server_test.lua +++ b/test/server_test.lua @@ -426,30 +426,51 @@ g.test_drop_server_if_process_is_dead = function() s:drop() end +local deferred_artifact_checks = {} + g.test_save_server_artifacts_when_test_failed = function() - local s1 = Server:new() -- empty config - local s2 = Server:new( - {workdir = ('%s/%s'):format(Server.vardir, os.tmpname())} - ) -- workdir passed + local artifact_paths - s1:start() - s2:start() + local result = helper.run_suite(function(lu2) + local cg = lu2.group() + local Server2 = lu2.Server - local s1_artifacts = ('%s/artifacts/%s'):format(s1.vardir, s1.id) - local s2_artifacts = ('%s/artifacts/%s'):format(s2.vardir, s2.id) - local test = rawget(_G, 'current_test') + cg.before_test('test_failure', function() + cg.s1 = Server2:new() -- empty config + cg.s2 = Server2:new( + {workdir = ('%s/%s'):format(Server2.vardir, os.tmpname())} + ) -- workdir passed - -- the test must be failed to save artifacts - test.status = 'fail' - s1:drop() - s2:drop() - test.status = 'success' + cg.s1:start() + cg.s2:start() + end) - t.assert_equals(fio.path.exists(s1_artifacts), true) - t.assert_equals(fio.path.is_dir(s1_artifacts), true) + cg.test_failure = function() + for _, s in ipairs({cg.s1, cg.s2}) do + s:exec(function() return true end) + end - t.assert_equals(fio.path.exists(s2_artifacts), true) - t.assert_equals(fio.path.is_dir(s2_artifacts), true) + artifact_paths = { + ('%s/artifacts/%s'):format(cg.s1.vardir, cg.s1.id), + ('%s/artifacts/%s'):format(cg.s2.vardir, cg.s2.id), + } + + lu2.fail('trigger artifact saving') + end + + cg.after_test('test_failure', function() + cg.s1:drop() + cg.s2:drop() + end) + end, {'--no-clean'}) + + t.assert_equals(result, 1) + table.insert(deferred_artifact_checks, function() + for _, path in ipairs(artifact_paths) do + t.assert_equals(fio.path.exists(path), true) + t.assert_equals(fio.path.is_dir(path), true) + end + end) end g.test_server_build_listen_uri = function() @@ -645,3 +666,74 @@ g.test_assertion_failure = function() server:connect_net_box() helper.assert_failure(server.exec, server, function() t.assert(false) end) end + +g.test_save_artifacts_after_restart_when_test_failed = function() + local log_path + + local result = helper.run_suite(function(lu2) + local cg = lu2.group() + local Server2 = lu2.Server + + cg.before_test('test_failure', function() + cg.s = Server2:new() + + cg.s:start() + cg.s:exec(function() + require('log').info('before_restart_artifacts_marker') + end) + + cg.s:restart() + cg.s:exec(function() + require('log').info('after_restart_artifacts_marker') + end) + + lu2.assert(cg.s:grep_log('after_restart_artifacts_marker')) + + log_path = fio.pathjoin( + cg.s.artifacts, cg.s.alias .. '.log' + ) + end) + + cg.test_failure = function() + lu2.fail('trigger artifact saving') + end + + cg.after_test('test_failure', function() + cg.s:drop() + end) + end, {'--no-clean'}) + + t.assert_equals(result, 1) + + local log_file = fio.open(log_path) + local log_stat = log_file:stat() + local log_content = log_file:read(log_stat.size) + log_file:close() + + t.assert_str_contains(log_content, 'before_restart_artifacts_marker') + t.assert_str_contains(log_content, 'after_restart_artifacts_marker') +end + +g.test_do_not_save_server_artifacts_when_test_succeeded = function() + local log_path + + local result = helper.run_suite(function(lu2) + local cg = lu2.group() + local Server2 = lu2.Server + + cg.test_success = function() + cg.s = Server2:new() + + log_path = fio.pathjoin( + cg.s.artifacts, cg.s.alias .. '.log' + ) + end + + cg.after_all(function() + cg.s:drop() + end) + end, {'--no-clean'}) + + t.assert_equals(result, 0) + t.assert_not(fio.path.exists(log_path)) +end