Software /
code /
prosody
File
tests/test.lua @ 8791:8da11142fabf
muc: Allow clients to change multiple affiliations or roles at once (#345)
According to XEP-0045 sections 9.2, 9.5 and 9.8 affiliation lists and role
lists should allow mass-modification. Prosody however would just use the
first entry of the list and ignore the rest. This is fixed by introducing
a `for` loop to `set` stanzas of the respective `muc#admin` namespace.
In order for this loop to work, the error handling was changed a little.
Prosody no longer returns after the first error. Instead, an error reply
is sent for each malformed or otherwise wrong entry, but the loop keeps
going over the other entries. This may lead to multiple error messages
being sent for one client request. A notable exception from this is when
the XML Schema for `muc#admin` requests is violated. In that case the loop
is aborted with an error message to the client.
The change is a bit bigger than that in order to have the loop only for
`set` stanzas without changing the behaviour of the `get` stanzas. This is
now more in line with trunk, where there are separate methods for each
stanza type.
References: #345
author | Lennart Sauerbeck <devel@lennart.sauerbeck.org> |
---|---|
date | Sat, 18 Mar 2017 18:47:28 +0100 |
parent | 7507:11e8c605a591 |
child | 7510:8a0a50e8ef2d |
line wrap: on
line source
-- Prosody IM -- Copyright (C) 2008-2010 Matthew Wild -- Copyright (C) 2008-2010 Waqas Hussain -- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. -- local tests_passed = true; function run_all_tests() package.loaded["net.connlisteners"] = { get = function () return {} end }; dotest "util.jid" dotest "util.multitable" dotest "util.rfc6724" dotest "util.http" dotest "core.stanza_router" dotest "core.s2smanager" dotest "core.configmanager" dotest "util.ip" dotest "util.json" dotest "util.stanza" dotest "util.sasl.scram" dotest "util.cache" dotest "util.throttle" dotest "util.uuid" dotest "util.random" dotest "util.xml" dotest "util.xmppstream" dotest "util.queue" dotest "net.http.parser" dosingletest("test_sasl.lua", "latin1toutf8"); dosingletest("test_utf8.lua", "valid"); end local verbosity = tonumber(arg[1]) or 2; if os.getenv("WINDIR") then package.path = package.path..";..\\?.lua"; package.cpath = package.cpath..";..\\?.dll"; else package.path = package.path..";../?.lua"; package.cpath = package.cpath..";../?.so"; end local _realG = _G; require "util.import" local envloadfile = require "util.envload".envloadfile; local env_mt = { __index = function (t,k) return rawget(_realG, k) or print("WARNING: Attempt to access nil global '"..tostring(k).."'"); end }; function testlib_new_env(t) return setmetatable(t or {}, env_mt); end function assert_equal(a, b, message, level) if not (a == b) then error("\n assert_equal failed: "..tostring(a).." ~= "..tostring(b)..(message and ("\n Message: "..message) or ""), (level or 1) + 1); elseif verbosity >= 4 then print("assert_equal succeeded: "..tostring(a).." == "..tostring(b)); end end function assert_table(a, message, level) assert_equal(type(a), "table", message, (level or 1) + 1); end function assert_function(a, message, level) assert_equal(type(a), "function", message, (level or 1) + 1); end function assert_string(a, message, level) assert_equal(type(a), "string", message, (level or 1) + 1); end function assert_boolean(a, message) assert_equal(type(a), "boolean", message); end function assert_is(a, message) assert_equal(not not a, true, message); end function assert_is_not(a, message) assert_equal(not not a, false, message); end function dosingletest(testname, fname) local tests = setmetatable({}, { __index = _realG }); tests.__unit = testname; tests.__test = fname; local chunk, err = envloadfile(testname, tests); if not chunk then print("WARNING: ", "Failed to load tests for "..testname, err); return; end local success, err = pcall(chunk); if not success then print("WARNING: ", "Failed to initialise tests for "..testname, err); return; end if type(tests[fname]) ~= "function" then error(testname.." has no test '"..fname.."'", 0); end local line_hook, line_info = new_line_coverage_monitor(testname); debug.sethook(line_hook, "l") local success, ret = pcall(tests[fname]); debug.sethook(); if not success then tests_passed = false; print("TEST FAILED! Unit: ["..testname.."] Function: ["..fname.."]"); print(" Location: "..ret:gsub(":%s*\n", "\n")); line_info(fname, false, report_file); elseif verbosity >= 2 then print("TEST SUCCEEDED: ", testname, fname); print(string.format("TEST COVERED %d/%d lines", line_info(fname, true, report_file))); else line_info(name, success, report_file); end end function dotest(unitname) local _fakeG = setmetatable({}, {__index = _realG}); _fakeG._G = _fakeG; local tests = setmetatable({}, { __index = _fakeG }); tests.__unit = unitname; local chunk, err = envloadfile("test_"..unitname:gsub("%.", "_")..".lua", tests); if not chunk then print("WARNING: ", "Failed to load tests for "..unitname, err); return; end local success, err = pcall(chunk); if not success then print("WARNING: ", "Failed to initialise tests for "..unitname, err); return; end if tests.env then setmetatable(tests.env, { __index = _realG }); end local unit = setmetatable({}, { __index = setmetatable({ _G = tests.env or _fakeG }, { __index = tests.env or _fakeG }) }); local fn = "../"..unitname:gsub("%.", "/")..".lua"; local chunk, err = envloadfile(fn, unit); if not chunk then print("WARNING: ", "Failed to load module: "..unitname, err); return; end local oldmodule, old_M = _fakeG.module, _fakeG._M; _fakeG.module = function () setmetatable(unit, nil); unit._M = unit; end local success, ret = pcall(chunk); _fakeG.module, _fakeG._M = oldmodule, old_M; if not success then print("WARNING: ", "Failed to initialise module: "..unitname, ret); return; end if type(ret) == "table" then for k,v in pairs(ret) do unit[k] = v; end end for name, f in pairs(unit) do local test = rawget(tests, name); if type(f) ~= "function" then if verbosity >= 3 then print("INFO: ", "Skipping "..unitname.."."..name.." because it is not a function"); end elseif type(test) ~= "function" then if verbosity >= 1 then print("WARNING: ", unitname.."."..name.." has no test!"); end else if verbosity >= 4 then print("INFO: ", "Testing "..unitname.."."..name); end local line_hook, line_info = new_line_coverage_monitor(fn); debug.sethook(line_hook, "l") local success, ret = pcall(test, f, unit); debug.sethook(); if not success then tests_passed = false; print("TEST FAILED! Unit: ["..unitname.."] Function: ["..name.."]"); print(" Location: "..ret:gsub(":%s*\n", "\n")); line_info(name, false, report_file); elseif verbosity >= 2 then print("TEST SUCCEEDED: ", unitname, name); print(string.format("TEST COVERED %d/%d lines", line_info(name, true, report_file))); else line_info(name, success, report_file); end end end end function runtest(f, msg) if not f then print("SUBTEST NOT FOUND: "..(msg or "(no description)")); return; end local success, ret = pcall(f); if success and verbosity >= 2 then print("SUBTEST PASSED: "..(msg or "(no description)")); elseif (not success) and verbosity >= 0 then tests_passed = false; print("SUBTEST FAILED: "..(msg or "(no description)")); error(ret, 0); end end function new_line_coverage_monitor(file) local lines_hit, funcs_hit = {}, {}; local total_lines, covered_lines = 0, 0; for line in io.lines(file) do total_lines = total_lines + 1; end return function (event, line) -- Line hook if not lines_hit[line] then local info = debug.getinfo(2, "fSL") if not info.source:find(file) then return; end if not funcs_hit[info.func] and info.activelines then funcs_hit[info.func] = true; for line in pairs(info.activelines) do lines_hit[line] = false; -- Marks it as hittable, but not hit yet end end if lines_hit[line] == false then --print("New line hit: "..line.." in "..debug.getinfo(2, "S").source); lines_hit[line] = true; covered_lines = covered_lines + 1; end end end, function (test_name, success) -- Get info local fn = file:gsub("^%W*", ""); local total_active_lines = 0; local coverage_file = io.open("reports/coverage_"..fn:gsub("%W+", "_")..".report", "a+"); for line, active in pairs(lines_hit) do if active ~= nil then total_active_lines = total_active_lines + 1; end if coverage_file then if active == false then coverage_file:write(fn, "|", line, "|", name or "", "|miss\n"); else coverage_file:write(fn, "|", line, "|", name or "", "|", tostring(success), "\n"); end end end if coverage_file then coverage_file:close(); end return covered_lines, total_active_lines, lines_hit; end end run_all_tests() os.exit(tests_passed and 0 or 1);