diff options
Diffstat (limited to 'tests/config')
60 files changed, 3561 insertions, 0 deletions
diff --git a/tests/config/basic.test.lua b/tests/config/basic.test.lua new file mode 100644 index 0000000..73356a4 --- /dev/null +++ b/tests/config/basic.test.lua @@ -0,0 +1,211 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local ffi = require('ffi') + +-- test if constants work properly +local function test_constants() + same(kres.class.IN, 1, 'class constants work') + same(kres.type.NS, 2, 'record type constants work') + same(kres.type.TYPE2, 2, 'unnamed record type constants work') + same(kres.type.BADTYPE, nil, 'non-existent type constants are checked') + same(kres.section.ANSWER, 0, 'section constants work') + same(kres.rcode.SERVFAIL, 2, 'rcode constants work') + same(kres.opcode.UPDATE, 5, 'opcode constants work') + -- Test inverse tables to convert constants to text + same(kres.tostring.class[1], 'IN', 'text class constants work') + same(kres.tostring.type[2], 'NS', 'text record type constants work') + same(kres.tostring.type[65535], 'TYPE65535', 'text record type undefined constants work') + same(kres.tostring.section[0], 'ANSWER', 'text section constants work') + same(kres.tostring.rcode[2], 'SERVFAIL', 'text rcode constants work') + same(kres.tostring.opcode[5], 'UPDATE', 'text opcode constants work') +end + +-- test globals +local function test_globals() + ok(mode('strict'), 'changing strictness mode') + boom(mode, {'badmode'}, 'changing to non-existent strictness mode') + same(reorder_RR(true), true, 'answer section reordering') + same(option('REORDER_RR', false), false, 'generic option call') + boom(option, {'REORDER_RR', 'potato'}, 'generic option call argument check') + boom(option, {'MARS_VACATION', false}, 'generic option check name') + same(table_print('crabdiary'), "'crabdiary'", 'table print works') + same(table_print({fakepizza=1}), "{\n ['fakepizza'] = 1,\n}", 'table print works on tables') +end + +-- test global API functions +local function test_global_functions() + boom(kres.parse_rdata, {'A 127.0.0.1'}, 'parse_rdata with non-table argument') + boom(kres.parse_rdata, {{1}}, 'parse_rdata with non-string data in arg table') + same(kres.parse_rdata({'A 127.0.0.1'}), {string.char(127, 0, 0, 1)}, 'parse_rdata with single record') + same(kres.parse_rdata({'A 127.0.0.1', 'A 127.0.0.2'}), + {string.char(127, 0, 0, 1), string.char(127, 0, 0, 2)}, 'parse_rdata with multiple records') +end + +-- test if dns library functions work +local function test_rrset_functions() + local rr = {owner = '\3com\0', ttl = 1, type = kres.type.TXT, rdata = '\5hello'} + local rr_text = tostring(kres.rr2str(rr)) + same(rr_text:gsub('%s+', ' '), 'com. 1 TXT "hello"', 'rrset to text works') + same(kres.dname2str(todname('com.')), 'com.', 'domain name conversion works') + -- test creating rrset + rr = kres.rrset(todname('com.'), kres.type.A, kres.class.IN, 66) + ok(ffi.istype(kres.rrset, rr), 'created an empty RR') + same(rr:owner(), '\3com\0', 'created RR has correct owner') + same(rr:class(), kres.class.IN, 'created RR has correct class') + same(rr:class(kres.class.CH), kres.class.CH, 'can set a different class') + same(rr:class(kres.class.IN), kres.class.IN, 'can restore a class') + same(rr.type, kres.type.A, 'created RR has correct type') + -- test adding rdata + same(rr:wire_size(), 0, 'empty RR wire size is zero') + ok(rr:add_rdata('\1\2\3\4', 4), 'adding RDATA works') + same(rr:wire_size(), 5 + 4 + 4 + 2 + 4, 'RR wire size works after adding RDATA') + -- test conversion to text + local expect = 'com. 66 A 1.2.3.4\n' + same(rr:txt_dump(), expect, 'RR to text works') + -- create a dummy rrsig + local rrsig = kres.rrset(todname('com.'), kres.type.RRSIG, kres.class.IN, 0) + rrsig:add_rdata('\0\1', 2) + same(rr:rdcount(), 1, 'add_rdata really added RDATA') + -- check rrsig matching + same(rr.type, rrsig:type_covered(), 'rrsig type covered matches covered RR type') + ok(rr:is_covered_by(rrsig), 'rrsig is covering a record') + -- test rrset merging + local copy = kres.rrset(rr:owner(), rr.type, kres.class.IN, 66) + ok(copy:add_rdata('\4\3\2\1', 4), 'adding second RDATA works') + ok(rr:merge_rdata(copy), 'merge_rdata works') + same(rr:rdcount(), 2, 'RDATA count is correct after merge_rdata') + expect = 'com. 66 A 1.2.3.4\n' .. + 'com. 66 A 4.3.2.1\n' + same(rr:txt_dump(), expect, 'merge_rdata actually merged RDATA') +end + +-- test dns library packet interface +local function test_packet_functions() + local pkt = kres.packet(512) + isnt(pkt, nil, 'creating packets works') + -- Test manipulating header + ok(pkt:rcode(kres.rcode.NOERROR), 'setting rcode works') + same(pkt:rcode(), 0, 'getting rcode works') + same(pkt:opcode(), 0, 'getting opcode works') + is(pkt:aa(), false, 'packet is created without AA') + is(pkt:ra(), false, 'packet is created without RA') + is(pkt:ad(), false, 'packet is created without AD') + ok(pkt:rd(true), 'setting RD bit works') + is(pkt:rd(), true, 'getting RD bit works') + ok(pkt:tc(true), 'setting TC bit works') + is(pkt:tc(), true, 'getting TC bit works') + ok(pkt:tc(false), 'disabling TC bit works') + is(pkt:tc(), false, 'getting TC bit after disable works') + is(pkt:cd(), false, 'getting CD bit works') + is(pkt:id(1234), 1234, 'setting MSGID works') + is(pkt:id(), 1234, 'getting MSGID works') + -- Test manipulating question + is(pkt:qname(), nil, 'reading name from empty question') + is(pkt:qtype(), 0, 'reading type from empty question') + is(pkt:qclass(), 0, 'reading class from empty question') + ok(pkt:question(todname('hello'), kres.class.IN, kres.type.A), 'setting question section works') + same(pkt:qname(), todname('hello'), 'reading QNAME works') + same(pkt:qtype(), kres.type.A, 'reading QTYPE works') + same(pkt:qclass(), kres.class.IN, 'reading QCLASS works') + -- Test manipulating sections + ok(pkt:begin(kres.section.ANSWER), 'switching sections works') + local res, err = pkt:put(nil, 0, 0, 0, '') + isnt(res, true, 'inserting nil entry doesnt work') + isnt(err.code, 0, 'error code is non-zero') + isnt(tostring(res), '', 'inserting nil returns invalid parameter') + ok(pkt:put(pkt:qname(), 900, pkt:qclass(), kres.type.A, '\1\2\3\4'), 'adding rrsets works') + boom(pkt.begin, {pkt, 10}, 'switching to invalid section doesnt work') + ok(pkt:begin(kres.section.ADDITIONAL), 'switching to different section works') + boom(pkt.begin, {pkt, 0}, 'rewinding sections doesnt work') + local before_insert = pkt:remaining_bytes() + ok(pkt:put(pkt:qname(), 900, pkt:qclass(), kres.type.A, '\4\3\2\1'), 'adding rrsets to different section works') + same(pkt:remaining_bytes(), before_insert - (2 + 4 + 4 + 2 + 4), 'remaining bytes count goes down with insertions') + -- Test conversions to text + like(pkt:tostring(), '->>HEADER<<-', 'packet to text works') + -- Test deserialization + local wire = pkt:towire() + same(#wire, 55, 'packet serialization works') + local parsed = kres.packet(#wire, wire) + isnt(parsed, nil, 'creating packet from wire works') + ok(parsed:parse(), 'parsing packet from wire works') + same(parsed:qname(), pkt:qname(), 'parsed packet has same QNAME') + same(parsed:qtype(), pkt:qtype(), 'parsed packet has same QTYPE') + same(parsed:qclass(), pkt:qclass(), 'parsed packet has same QCLASS') + same(parsed:opcode(), pkt:opcode(), 'parsed packet has same opcode') + same(parsed:rcode(), pkt:rcode(), 'parsed packet has same rcode') + same(parsed:rd(), pkt:rd(), 'parsed packet has same RD') + same(parsed:id(), pkt:id(), 'parsed packet has same MSGID') + same(parsed:qdcount(), pkt:qdcount(), 'parsed packet has same question count') + same(parsed:ancount(), pkt:ancount(), 'parsed packet has same answer count') + same(parsed:nscount(), pkt:nscount(), 'parsed packet has same authority count') + same(parsed:arcount(), pkt:arcount(), 'parsed packet has same additional count') + same(parsed:tostring(), pkt:tostring(), 'parsed packet is equal to source packet') + + -- Test adding RR sets directly + local copy = kres.packet(512) + copy:question(todname('hello'), kres.class.IN, kres.type.A) + copy:begin(kres.section.ANSWER) + local rr = kres.rrset(pkt:qname(), kres.type.A, kres.class.IN, 66) + rr:add_rdata('\4\3\2\1', 4) + ok(copy:put_rr(rr), 'adding RR sets directly works') + ok(copy:recycle(), 'recycling packet works') + + -- Test recycling of packets + -- Clear_payload keeps header + question intact + local cleared = kres.packet(#wire, wire) -- same as "parsed" above + ok(cleared:parse(), 'parsing packet from wire works') + ok(cleared:clear_payload(), 'clear_payload works') + same(cleared:id(), pkt:id(), 'cleared packet has same MSGID') + same(cleared:qr(), pkt:qr(), 'cleared packet has same QR') + same(cleared:opcode(), pkt:opcode(), 'cleared packet has same OPCODE') + same(cleared:aa(), pkt:aa(), 'cleared packet has same AA') + same(cleared:tc(), pkt:tc(), 'cleared packet has same TC') + same(cleared:rd(), pkt:rd(), 'cleared packet has same RD') + same(cleared:ra(), pkt:ra(), 'cleared packet has same RA') + same(cleared:ad(), pkt:ad(), 'cleared packet has same AD') + same(cleared:cd(), pkt:cd(), 'cleared packet has same CD') + same(cleared:rcode(), pkt:rcode(), 'cleared packet has same RCODE') + same(cleared:qdcount(), pkt:qdcount(), 'cleared packet has same question count') + same(cleared:ancount(), 0, 'cleared packet has no answers') + same(cleared:nscount(), 0, 'cleared packet has no authority') + same(cleared:arcount(), 0, 'cleared packet has no additional') + same(cleared:qname(), pkt:qname(), 'cleared packet has same QNAME') + same(cleared:qtype(), pkt:qtype(), 'cleared packet has same QTYPE') + same(cleared:qclass(), pkt:qclass(), 'cleared packet has same QCLASS') + + -- Recycle clears question as well + ok(pkt:recycle(), 'recycle() works') + is(pkt:ancount(), 0, 'recycle() clears records') + is(pkt:qname(), nil, 'recycle() clears question') + is(#pkt:towire(), 12, 'recycle() clears the packet wireformat') +end + +-- test JSON encode/decode functions +local function test_json_functions() + for msg, obj in pairs({ + ['number'] = 0, + ['string'] = 'ok', + ['list'] = {1, 2, 3}, + ['map'] = {foo='bar'}, + ['nest structure'] = {foo='bar', baz={1,2,3}}, + }) do + same(fromjson(tojson(obj)), obj, 'json test: ' .. msg) + end + + for _, str in ipairs({ + '{', '}', + '[', ']', + 'x,', + '[1,2,3,]', + }) do + boom(fromjson, {'{'}, 'json test: invalid \'' .. str .. '\'') + end +end + +return { + test_constants, + test_globals, + test_global_functions, + test_rrset_functions, + test_packet_functions, + test_json_functions, +} diff --git a/tests/config/cache.test.lua b/tests/config/cache.test.lua new file mode 100644 index 0000000..33b87db --- /dev/null +++ b/tests/config/cache.test.lua @@ -0,0 +1,67 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- test if cache module properties work +local function test_properties() + is(type(cache), 'table', 'cache module is loaded') + is(cache.count(), 0, 'cache is empty on startup') + local backends = cache.backends() + is(type(backends), 'table', 'cache provides a list of backends') + ok(backends['lmdb'], 'cache provides built-in lmdb backend') + is(cache.current_storage, 'lmdb://', 'cache starts with lmdb backend') + is(cache.current_size, 100 * MB, 'cache starts with default size limit') + is(cache.max_ttl(10), 10, 'allows setting maximum TTL') + is(cache.max_ttl(), 10, 'stored maximum TTL') + is(cache.min_ttl(1), 1, 'allows setting minimum TTL') + is(cache.min_ttl(), 1, 'stored minimum TTL') +end + +-- test if the stats work with reopening the cache and operations fail with closed cache +local function test_stats() + ok(cache.close(), 'cache can be closed') + boom(cache.open, {100 * MB, 'invalid://'}, 'cache cannot be opened with invalid backend') + + boom(cache.clear, {}, '.clear() does not work on closed cache') + boom(cache.count, {}, '.count() does not work on closed cache') + boom(cache.get, { 'key' }, '.get(...) does not work on closed cache') + + ok(cache.open(100 * MB), 'cache can be reopened') + local s = cache.stats() + is(type(s), 'table', 'stats returns a table') + -- Just checking the most useful fields + isnt(s.read and s.read_miss and s.write, nil, 'stats returns correct fields') +end + +-- test if cache can be resized or shrunk +local function test_resize() + ok(cache.open(200 * MB, 'lmdb://'), 'cache can be resized') + is(cache.current_size, 200 * MB, 'cache was resized') + ok(cache.open(50 * MB), 'cache can be shrunk') + is(cache.current_size, 50 * MB, 'cache was shrunk') +end + +-- test access to cache through context +local function test_context_cache() + local c = kres.context().cache + is(type(c), 'cdata', 'context has a cache object') + local s = c.stats + isnt(s.read and s.read_miss and s.write, 'context cache stats works') + -- insert A record into cache + local rdata = '\1\2\3\4' + local rr = kres.rrset('\3com\0', kres.type.A, kres.class.IN, 66) + rr:add_rdata(rdata, #rdata) + local s_write = s.write + ok(c:insert(rr, nil, 0, 0), 'cache insertion works (A)') + ok(c:commit(), 'cache commit works') + isnt(s.write, s_write, 'cache insertion increments counters') + -- insert NS record into cache + local rr_ns = kres.rrset('\3com\0', kres.type.NS, kres.class.IN, 66) + local rdata_ns = todname('c.gtld-servers.net') + ok(rr_ns:add_rdata(rdata_ns, #rdata_ns), 'adding rdata works') + ok(c:insert(rr_ns, nil, 0), 'cache insertion works (NS)') +end + +return { + test_properties, + test_stats, + test_resize, + test_context_cache, +} diff --git a/tests/config/doh2.test.lua b/tests/config/doh2.test.lua new file mode 100644 index 0000000..2360e7f --- /dev/null +++ b/tests/config/doh2.test.lua @@ -0,0 +1,524 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local basexx = require('basexx') +local ffi = require('ffi') + +local function gen_huge_answer(_, req) + local answer = req:ensure_answer() + ffi.C.kr_pkt_make_auth_header(answer) + + answer:rcode(kres.rcode.NOERROR) + + -- 64k answer + answer:begin(kres.section.ANSWER) + answer:put('\4test\0', 300, answer:qclass(), kres.type.URI, + '\0\0\0\0' .. string.rep('0', 65000)) + answer:put('\4test\0', 300, answer:qclass(), kres.type.URI, + '\0\0\0\0' .. 'done') + return kres.DONE +end + +local function gen_varying_ttls(_, req) + local qry = req:current() + local answer = req:ensure_answer() + ffi.C.kr_pkt_make_auth_header(answer) + + answer:rcode(kres.rcode.NOERROR) + + -- varying TTLs in ANSWER section + answer:begin(kres.section.ANSWER) + answer:put(qry.sname, 1800, answer:qclass(), kres.type.AAAA, + '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1') + answer:put(qry.sname, 900, answer:qclass(), kres.type.A, '\127\0\0\1') + answer:put(qry.sname, 20000, answer:qclass(), kres.type.NS, '\2ns\4test\0') + + -- shorter TTL than all other RRs + answer:begin(kres.section.AUTHORITY) + answer:put('\4test\0', 300, answer:qclass(), kres.type.SOA, + -- ns.test. nobody.invalid. 1 3600 1200 604800 10800 + '\2ns\4test\0\6nobody\7invalid\0\0\0\0\1\0\0\14\16\0\0\4\176\0\9\58\128\0\0\42\48') + return kres.DONE +end + +function parse_pkt(input, desc) + local wire = ffi.cast("void *", input) + local pkt = ffi.C.knot_pkt_new(wire, #input, nil); + assert(pkt, desc .. ': failed to create new packet') + + local result = ffi.C.knot_pkt_parse(pkt, 0) + ok(result == 0, desc .. ': knot_pkt_parse works on received answer') + return pkt +end + +local function check_ok(req, desc) + local headers, stream, errno = req:go(16) + if errno then + local errmsg = stream + nok(errmsg, desc .. ': ' .. errmsg) + return + end + same(tonumber(headers:get(':status')), 200, desc .. ': status 200') + same(headers:get('content-type'), 'application/dns-message', desc .. ': content-type') + local body = assert(stream:get_body_as_string()) + local pkt = parse_pkt(body, desc) + return headers, pkt +end + +local function check_err(req, exp_status, desc) + local headers, errmsg, errno = req:go(16) + if errno then + nok(errmsg, desc .. ': ' .. errmsg) + return + end + local got_status = headers:get(':status') + same(got_status, exp_status, desc) +end + +-- check prerequisites +local bound, port +local host = '127.0.0.1' +for _ = 1,10 do + port = math.random(30000, 39999) + bound = pcall(net.listen, host, port, { kind = 'doh2'}) + if bound then + break + end +end + +if not bound then + -- skipping doh2 tests (failure to bind may be caused by missing support during build) + os.exit(77) +else + policy.add(policy.suffix(policy.DROP, policy.todnames({'servfail.test.'}))) + policy.add(policy.suffix(policy.DENY, policy.todnames({'nxdomain.test.'}))) + policy.add(policy.suffix(gen_varying_ttls, policy.todnames({'noerror.test.'}))) + + local req_templ, uri_templ + local function start_server() + local request = require('http.request') + local ssl_ctx = require('openssl.ssl.context') + uri_templ = string.format('https://%s:%d/dns-query', host, port) + req_templ = assert(request.new_from_uri(uri_templ)) + req_templ.headers:upsert('content-type', 'application/dns-message') + req_templ.ctx = ssl_ctx.new() + req_templ.ctx:setVerify(ssl_ctx.VERIFY_NONE) + end + + + -- test a valid DNS query using POST + local function test_post_servfail() + local desc = 'valid POST query which ends with SERVFAIL' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- servfail.test. A + 'FZUBAAABAAAAAAAACHNlcnZmYWlsBHRlc3QAAAEAAQ==')) + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- uncacheable + same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + same(headers:get('access-control-allow-origin'), '*', desc .. ': CORS headers') + same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') + end + + local function test_post_noerror() + local desc = 'valid POST query which ends with NOERROR' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- noerror.test. A + 'vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')) + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- HTTP TTL is minimum from all RRs in the answer + same(headers:get('cache-control'), 'max-age=300', desc .. ': TTL 900') + same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode matches') + same(pkt:ancount(), 3, desc .. ': ANSWER is present') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + same(pkt:arcount(), 0, desc .. ': ADDITIONAL is empty') + end + + local function test_post_ignore_params_1() + local desc = 'valid POST ignores parameters (without ?dns)' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req.headers:upsert(':path', '/doh?something=asdf&aaa=bbb') + req:set_body(basexx.from_base64( -- noerror.test. A + 'vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')) + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- HTTP TTL is minimum from all RRs in the answer + same(headers:get('cache-control'), 'max-age=300', desc .. ': TTL 900') + same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode matches') + same(pkt:ancount(), 3, desc .. ': ANSWER is present') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + same(pkt:arcount(), 0, desc .. ': ADDITIONAL is empty') + end + + local function test_post_ignore_params_2() + local desc = 'valid POST ignores parameters (with ?dns)' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req.headers:upsert(':path', '/dns-query?dns=' -- servfail.test. A + .. 'FZUBAAABAAAAAAAACHNlcnZmYWlsBHRlc3QAAAEAAQ') + req:set_body(basexx.from_base64( -- noerror.test. A + 'vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')) + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- HTTP TTL is minimum from all RRs in the answer + same(headers:get('cache-control'), 'max-age=300', desc .. ': TTL 900') + same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode matches') + same(pkt:ancount(), 3, desc .. ': ANSWER is present') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + same(pkt:arcount(), 0, desc .. ': ADDITIONAL is empty') + end + + local function test_post_nxdomain() + local desc = 'valid POST query which ends with NXDOMAIN' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- nxdomain.test. A + 'viABAAABAAAAAAAACG54ZG9tYWluBHRlc3QAAAEAAQ==')) + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + same(headers:get('cache-control'), 'max-age=10800', desc .. ': TTL 10800') + same(pkt:rcode(), kres.rcode.NXDOMAIN, desc .. ': rcode matches') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + end + + -- RFC 8484 section 6 explicitly allows huge answers over HTTP + local function test_huge_answer() + policy.add(policy.suffix(gen_huge_answer, policy.todnames({'huge.test'}))) + local desc = 'POST query for a huge answer' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- huge.test. URI, no EDNS + 'HHwBAAABAAAAAAAABGh1Z2UEdGVzdAABAAAB')) + local _, pkt = check_ok(req, desc) + same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode NOERROR') + same(pkt:tc(), false, desc .. ': no TC bit') + same(pkt:ancount(), 2, desc .. ': ANSWER contains both RRs') + end + + -- test an invalid DNS query using POST + local function test_post_short_input() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'POST') + req:set_body(string.rep('0', 11)) -- 11 bytes < DNS msg header + check_err(req, '400', 'too short POST finishes with 400') + end + +-- local function test_post_long_input() +-- -- FIXME: This test is broken in Lua. The connection times out +-- -- for some reason, but sending a request like this with `curl` +-- -- or PowerShell's `Invoke-RestMethod` provides correct results. +-- +-- local req = assert(req_templ:clone()) +-- req.headers:upsert(':method', 'POST') +-- req:set_body(string.rep('s', 1025)) -- > DNS msg over UDP +-- check_err(req, '400', 'too long POST finishes with 400') +-- end + + local function test_post_unparseable_input() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'POST') + req:set_body(string.rep('\0', 1024)) -- garbage + check_err(req, '400', 'unparseable DNS message finishes with 400') + end + + local function test_post_unsupp_type() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'POST') + req.headers:upsert('content-type', 'application/dns+json') + req:set_body(string.rep('\0', 12)) -- valid message + check_err(req, '415', 'unsupported request content type finishes with 415') + end + + -- test a valid DNS query using GET + local function test_get_servfail() + local desc = 'valid GET query which ends with SERVFAIL' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' -- servfail.test. A + .. 'FZUBAAABAAAAAAAACHNlcnZmYWlsBHRlc3QAAAEAAQ') + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- uncacheable + same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') + end + + local function test_get_noerror() + local desc = 'valid GET query which ends with NOERROR' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' -- noerror.test. A + .. 'vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB') + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- HTTP TTL is minimum from all RRs in the answer + same(headers:get('cache-control'), 'max-age=300', desc .. ': TTL 900') + same(headers:get('access-control-allow-origin'), '*', desc .. ': CORS headers') + same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode matches') + same(pkt:ancount(), 3, desc .. ': ANSWER is present') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + same(pkt:arcount(), 0, desc .. ': ADDITIONAL is empty') + end + + local function test_get_nxdomain() + local desc = 'valid GET query which ends with NXDOMAIN' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' -- nxdomain.test. A + .. 'viABAAABAAAAAAAACG54ZG9tYWluBHRlc3QAAAEAAQ') + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + same(headers:get('cache-control'), 'max-age=10800', desc .. ': TTL 10800') + same(pkt:rcode(), kres.rcode.NXDOMAIN, desc .. ': rcode matches') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + end + + local function test_get_multiple_amps() + local desc = 'GET query with consecutive ampersands' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', + '/doh?other=something&another=something&&&&dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB') + check_ok(req, desc) + end + + local function test_get_other_params_before_dns() + local desc = 'GET query with other parameters before dns is valid' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', + '/doh?other=something&another=something&dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB') + check_ok(req, desc) + end + + local function test_get_other_params_after_dns() + local desc = 'GET query with other parameters after dns is valid' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', + '/doh?dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB&other=something&another=something') + check_ok(req, desc) + end + + local function test_get_other_params() + local desc = 'GET query with other parameters than dns on both sides is valid' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', + '/doh?other=something&dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB&another=something') + check_ok(req, desc) + end + + -- test an invalid DNS query using GET + local function test_get_long_input() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' .. basexx.to_url64(string.rep('\0', 1030))) + check_err(req, '400', 'too long GET finishes with 400') + end + + local function test_get_no_dns_param() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?notdns=' .. basexx.to_url64(string.rep('\0', 1024))) + check_err(req, '400', 'GET without dns parameter finishes with 400') + end + + local function test_get_unparseable() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh??dns=' .. basexx.to_url64(string.rep('\0', 1024))) + check_err(req, '400', 'unparseable GET finishes with 400') + end + + local function test_get_invalid_b64() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=thisisnotb64') + check_err(req, '400', 'GET with invalid base64 finishes with 400') + end + + local function test_get_invalid_chars() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' .. basexx.to_url64(string.rep('\0', 200)) .. '@#$%?!') + check_err(req, '400', 'GET with invalid characters in b64 finishes with 400') + end + + local function test_unsupp_method() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'PUT') + check_err(req, '501', 'unsupported method finishes with 501') + end + + local function test_dstaddr() + local triggered = false + local exp_dstaddr = ffi.gc(ffi.C.kr_straddr_socket(host, port, nil), ffi.C.free) + local function check_dstaddr(state, req) + triggered = true + same(ffi.C.kr_sockaddr_cmp(req.qsource.dst_addr, exp_dstaddr), 0, + 'request has correct server address') + return state + end + policy.add(policy.suffix(check_dstaddr, policy.todnames({'dstaddr.test'}))) + local desc = 'valid POST query has server address available in request' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- dstaddr.test. A + 'FnkBAAABAAAAAAAAB2RzdGFkZHIEdGVzdAAAAQAB')) + check_ok(req, desc) + ok(triggered, 'dstaddr policy was triggered') + end + + local function test_srcaddr() + modules.load('view') + assert(view) + local policy_refuse = policy.suffix(policy.REFUSE, policy.todnames({'srcaddr.test.knot-resolver.cz'})) + -- these netmasks would not work if the request did not contain IP addresses + view:addr('0.0.0.0/0', policy_refuse) + view:addr('::/0', policy_refuse) + + local desc = 'valid POST query has source address available in request' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- srcaddr.test.knot-resolver.cz TXT + 'QNQBAAABAAAAAAAAB3NyY2FkZHIEdGVzdA1rbm90LXJlc29sdmVyAmN6AAAQAAE')) + local _, pkt = check_ok(req, desc) + same(pkt:rcode(), kres.rcode.REFUSED, desc .. ': view module caught it') + + modules.unload('view') + end + + local function test_headers() + local get_desc = nil + + local function check_headers_zero_len(state, req) + same(tonumber(req.qsource.headers.len), 0, get_desc()) + return state + end + + local function check_headers_value(state, req) + local value = ffi.string(req.qsource.headers.at[0].value) + same(value, 'lua test config.doh2', get_desc()) + return state + end + + local function check_headers_more_values(state, req) + local checked = 0 + for i = 1, tonumber(req.qsource.headers.len) do + local name = ffi.string(req.qsource.headers.at[i - 1].name) + local value = ffi.string(req.qsource.headers.at[i - 1].value) + if (name == 'user-agent') then + same(value, 'lua test config.doh2', get_desc() .. ' - user-agent') + checked = checked + 1 + elseif (name == ':scheme') then + same(value, 'https', get_desc() .. ' - :scheme') + checked = checked + 1 + end + end + + same(checked, 2, get_desc() .. ' - two checked') + return state + end + + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' -- headers.test. A + .. 'AAABAAABAAAAAAAAB2hlYWRlcnMEdGVzdAAAAQAB') + req.headers:upsert('user-agent', 'lua test config.doh2') + req.headers:upsert(':scheme', 'https') + + get_desc = function() return 'exposed HTTP headers: no headers' end + rule_desc = policy.add(policy.all(check_headers_zero_len)) + check_ok(req, get_desc()) + get_desc = function() return 'exposed HTTP headers: empty string' end + net.doh_headers('') + check_ok(req, get_desc()) + get_desc = function() return 'exposed HTTP headers: empty table' end + net.doh_headers({''}) + check_ok(req, get_desc()) + policy.del(rule_desc.id) + + get_desc = function() return 'exposed HTTP headers: take just one string parameter' end + boom(net.doh_headers, {'user-agent', ':method'}, get_desc()) + get_desc = function() return 'exposed HTTP headers: take just one table parameter' end + boom(net.doh_headers, {{'user-agent'}, {':method'}}, get_desc()) + + get_desc = function() return 'exposed HTTP headers: take one header - as string' end + net.doh_headers('user-agent') + rule_desc = policy.add(policy.all(check_headers_value)) + check_ok(req, get_desc()) + get_desc = function() return 'exposed HTTP headers: take one header - as table' end + net.doh_headers({ 'user-agent' }) + check_ok(req, get_desc()) + policy.del(rule_desc.id) + + get_desc = function() return 'exposed HTTP headers: take more headers' end + net.doh_headers({ ':method', 'user-agent', ':path', ':scheme' }) + rule_desc = policy.add(policy.all(check_headers_more_values)) + check_ok(req, get_desc()) + policy.del(rule_desc.id) + + + end + +-- not implemented +-- local function test_post_unsupp_accept() +-- local req = assert(req_templ:clone()) +-- req.headers:upsert(':method', 'POST') +-- req.headers:upsert('accept', 'application/dns+json') +-- req:set_body(string.rep('\0', 12)) -- valid message +-- check_err(req, '406', 'unsupported Accept type finishes with 406') +-- end + + -- plan tests + local tests = { + start_server, + test_post_servfail, + test_post_noerror, + test_post_ignore_params_1, + test_post_ignore_params_2, + test_post_nxdomain, + test_huge_answer, + test_post_short_input, +-- test_post_long_input, -- FIXME see the test function + test_post_unparseable_input, + test_post_unsupp_type, + test_get_servfail, + test_get_noerror, + test_get_nxdomain, + test_get_multiple_amps, + test_get_other_params_before_dns, + test_get_other_params_after_dns, + test_get_other_params, + test_get_long_input, + test_get_no_dns_param, + test_get_unparseable, + test_get_invalid_b64, + test_get_invalid_chars, + test_unsupp_method, + test_dstaddr, + test_srcaddr, + test_headers + } + + return tests +end diff --git a/tests/config/lru.test.lua b/tests/config/lru.test.lua new file mode 100644 index 0000000..324d636 --- /dev/null +++ b/tests/config/lru.test.lua @@ -0,0 +1,84 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local ffi = require('ffi') + +-- Test LRU interface +local function test_lru() + local capacity = 1024 + local lru = kres.lru(capacity) + local dict = { + "catagmatic", "prevaricator", "statoscope", "workhand", "benzamide", + "alluvia", "fanciful", "bladish", "Tarsius", "unfast", "appropriative", + "seraphically", "monkeypod", "deflectometer", "tanglesome", "zodiacal", + "physiologically", "economizer", "forcepslike", "betrumpet", + "Danization", "broadthroat", "randir", "usherette", "nephropyosis", + "hematocyanin", "chrysohermidin", "uncave", "mirksome", "podophyllum", + "siphonognathous", "indoor", "featheriness", "forwardation", + "archruler", "soricoid", "Dailamite", "carmoisin", "controllability", + "unpragmatical", "childless", "transumpt", "productive", + "thyreotoxicosis", "oversorrow", "disshadow", "osse", "roar", + "pantomnesia", "talcer", "hydrorrhoea", "Satyridae", "undetesting", + "smoothbored", "widower", "sivathere", "pendle", "saltation", + "autopelagic", "campfight", "unexplained", "Macrorhamphosus", + "absconsa", "counterflory", "interdependent", "triact", "reconcentration", + "oversharpness", "sarcoenchondroma", "superstimulate", "assessory", + "pseudepiscopacy", "telescopically", "ventriloque", "politicaster", + "Caesalpiniaceae", "inopportunity", "Helion", "uncompatible", + "cephaloclasia", "oversearch", "Mahayanistic", "quarterspace", + "bacillogenic", "hamartite", "polytheistical", "unescapableness", + "Pterophorus", "cradlemaking", "Hippoboscidae", "overindustrialize", + "perishless", "cupidity", "semilichen", "gadge", "detrimental", + "misencourage", "toparchia", "lurchingly", "apocatastasis" + } + + -- Check that key insertion works + local inserted = 0 + for i, word in ipairs(dict) do + if lru:set(word, i) then + if lru:get(word) == i then + inserted = inserted + 1 + end + end + end + + is(inserted, #dict, 'all inserted keys can be retrieved') + + -- Check that using binary data as keys works + local badinserts = 0 + for i, word in ipairs(dict) do + local word_len = #word + word = ffi.cast('char *', word) + if lru:set(word, i, word_len) then + if lru:get(word, word_len) ~= i then + badinserts = badinserts + 1 + end + end + end + + is(badinserts, 0, 'insertion works for binary keys') + + -- Sanity check that non-existent keys cannot be retrieved + local missing = "not in lru" + is(lru:get(missing, #missing, false), nil, 'key that wasnt inserted cannot be retrieved') + + -- Test whether key eviction works and LRU is able to insert past the capacity + badinserts = 0 + for i = 0, capacity do + local word = dict[1] .. tostring(i) + if lru:set(word, i) then + if lru:get(word) ~= i then + badinserts = badinserts + 1 + end + end + end + + is(badinserts, 0, 'insertion works for more keys than LRU capacity') + + -- Delete and GC + lru = nil -- luacheck: ignore 311 + collectgarbage() + collectgarbage() +end + +return { + test_lru, +} diff --git a/tests/config/meson.build b/tests/config/meson.build new file mode 100644 index 0000000..a739222 --- /dev/null +++ b/tests/config/meson.build @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +config_tests += [ + ['basic', files('basic.test.lua'), ['skip_asan']], + ['cache', files('cache.test.lua'), ['skip_asan']], + ['net', files('net.test.lua'), ['config_net']], + ['doh2', files('doh2.test.lua')], + ['lru', files('lru.test.lua')], + ['tls', files('tls.test.lua')], + ['worker', files('worker.test.lua')], +] + + +run_configtest = find_program('../../scripts/test-config.sh') + + +foreach config_test : config_tests + # additional suites + extra_suites = config_test.length() >= 3 ? config_test[2] : [] + + # environment variables for test + conftest_env = environment() + conftest_env.prepend('PATH', sbin_dir) + conftest_env.set('KRESD_NO_LISTEN', '1') + conftest_env.set('SOURCE_PATH', meson.current_source_dir()) + conftest_env.set( + 'TEST_FILE', '@0@/@1@'.format(meson.source_root(), config_test[1][0])) + + test( + 'config.' + config_test[0], + run_configtest, + args: [ + '-c', files('test.cfg'), + '-n' + ], + env: conftest_env, + suite: [ + 'postinstall', + 'config', + ] + extra_suites, + ) +endforeach diff --git a/tests/config/net.test.lua b/tests/config/net.test.lua new file mode 100644 index 0000000..589bf61 --- /dev/null +++ b/tests/config/net.test.lua @@ -0,0 +1,67 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local kr_table_len = require('kluautil').kr_table_len + +local function test_env_no_listen() + -- config tests are executed with env variable KRESD_NO_LISTEN=1 + -- so net.list() should be an empty table + same(kr_table_len(net.list()), 0, + "env 'KRESD_NO_LISTEN=1' prevents kresd from listening") +end + +local function test_freebind() + if require('jit').os == 'OSX' then + return 77 -- freebind is not supported on macOS + end + boom(net.listen, {'192.0.2.1', 50049}, + 'net.listen() without freebind should fail') + -- TODO: same(kr_table_len(net.list()), 0, + -- "net.listen() failure does not modify output from net.list()") + ok(net.listen('192.0.2.1', 50049, { freebind=true }), + 'net.listen() with freebind succeeds') + local net_list = net.list() + -- same(list length == 2) + same(net_list[1].transport.protocol, 'udp', + 'net.listen({freebind = true}) without kind starts UDP listener') + same(net_list[2].transport.protocol, 'tcp', + 'net.listen({freebind = true}) without kind starts TCP listener') + same(net_list[1].transport.freebind, true, + 'net.listen({freebind = true}) enables FREEBIND for UDP listener') + same(net_list[2].transport.freebind, true, + 'net.listen({freebind = true}) enables FREEBIND for TCP listener') +end + +local function test_proxy_allowed() + same(net.proxy_allowed(), {}, 'net.proxy_allowed() empty by default') + net.proxy_allowed('172.22.0.1') + same(net.proxy_allowed(), {'172.22.0.1/32'}, 'net.proxy_allowed() single IPv4 host') + net.proxy_allowed({'172.22.0.1'}) + same(net.proxy_allowed(), {'172.22.0.1/32'}, 'net.proxy_allowed() single IPv4 host (as table)') + net.proxy_allowed('172.18.1.0/24') + same(net.proxy_allowed(), {'172.18.1.0/24'}, 'net.proxy_allowed() IPv4 net') + net.proxy_allowed({'172.22.0.1', '172.18.1.0/24'}) + same(net.proxy_allowed(), {'172.18.1.0/24', '172.22.0.1/32'}, 'net.proxy_allowed() multiple IPv4 args as table') + net.proxy_allowed({}) + same(net.proxy_allowed(), {}, 'net.proxy_allowed() clear table') + net.proxy_allowed({'::1'}) + same(net.proxy_allowed(), {'::1/128'}, 'net.proxy_allowed() single IPv6 host') + net.proxy_allowed({'2001:db8:cafe:beef::/64'}) + same(net.proxy_allowed(), {'2001:db8:cafe:beef::/64'}, 'net.proxy_allowed() IPv6 net') + net.proxy_allowed({'0.0.0.0/0', '::/0'}) + same(net.proxy_allowed(), {'0.0.0.0/0', '::/0'}, 'net.proxy_allowed() allow all IPv4 and IPv6') + net.proxy_allowed({'::1'}) + same(net.proxy_allowed(), {'::1/128'}, 'net.proxy_allowed() single IPv6 host after all (proper reset)') + boom(net.proxy_allowed, {'a'}, 'net.proxy_allowed() invalid string arg') + boom(net.proxy_allowed, {'127.0.0.'}, 'net.proxy_allowed() incomplete IPv4') + boom(net.proxy_allowed, {'256.0.0.0'}, 'net.proxy_allowed() invalid IPv4') + boom(net.proxy_allowed, {'xx::'}, 'net.proxy_allowed() invalid IPv6') + boom(net.proxy_allowed, {'127.0.0.1/33'}, 'net.proxy_allowed() IPv4 invalid netmask') + boom(net.proxy_allowed, {'127.0.0.1/-1'}, 'net.proxy_allowed() IPv4 negative netmask') + boom(net.proxy_allowed, {'fd::/132'}, 'net.proxy_allowed() IPv6 invalid netmask') + boom(net.proxy_allowed, {{'127.0.0.0/8', '::1/129'}}, 'net.proxy_allowed() single param invalid') +end + +return { + test_env_no_listen, + test_freebind, + test_proxy_allowed, +} diff --git a/tests/config/tapered/.travis.yml b/tests/config/tapered/.travis.yml new file mode 100644 index 0000000..77135af --- /dev/null +++ b/tests/config/tapered/.travis.yml @@ -0,0 +1,40 @@ +language: c + +sudo: false + +env: + global: + - LUAROCKS=2.4.3 + matrix: + - LUA=lua5.1 + - LUA=lua5.2 + - LUA=lua5.3 + - LUA=luajit # latest stable version (2.0.4) + - LUA=luajit2.0 # current head of 2.0 branch + - LUA=luajit2.1 # current head of 2.1 branch + +branches: + only: + - bugfix + - master + +before_install: + - source .travis/setenv_lua.sh + - lua -v + - luarocks install luacov + +script: + - cd test + - git clone https://github.com/sstephenson/bats.git + - ./bats/bin/bats runner.bash + +after_success: + - luacov + - cp -v luacov.report.out ../ + - cd .. + - bash <(curl -s https://codecov.io/bash) + +notifications: + email: + on_success: change + on_failure: always diff --git a/tests/config/tapered/.travis/platform.sh b/tests/config/tapered/.travis/platform.sh new file mode 100644 index 0000000..7259a7d --- /dev/null +++ b/tests/config/tapered/.travis/platform.sh @@ -0,0 +1,15 @@ +if [ -z "${PLATFORM:-}" ]; then + PLATFORM=$TRAVIS_OS_NAME; +fi + +if [ "$PLATFORM" == "osx" ]; then + PLATFORM="macosx"; +fi + +if [ -z "$PLATFORM" ]; then + if [ "$(uname)" == "Linux" ]; then + PLATFORM="linux"; + else + PLATFORM="macosx"; + fi; +fi diff --git a/tests/config/tapered/.travis/setenv_lua.sh b/tests/config/tapered/.travis/setenv_lua.sh new file mode 100644 index 0000000..8d8c825 --- /dev/null +++ b/tests/config/tapered/.travis/setenv_lua.sh @@ -0,0 +1,3 @@ +export PATH=${PATH}:$HOME/.lua:$HOME/.local/bin:${TRAVIS_BUILD_DIR}/install/luarocks/bin +bash .travis/setup_lua.sh +eval `$HOME/.lua/luarocks path` diff --git a/tests/config/tapered/.travis/setup_lua.sh b/tests/config/tapered/.travis/setup_lua.sh new file mode 100644 index 0000000..a72b6d2 --- /dev/null +++ b/tests/config/tapered/.travis/setup_lua.sh @@ -0,0 +1,122 @@ +#! /bin/bash + +# A script for setting up environment for travis-ci testing. +# Sets up Lua and Luarocks. +# LUA must be "lua5.1", "lua5.2" or "luajit". +# luajit2.0 - master v2.0 +# luajit2.1 - master v2.1 + +set -eufo pipefail + +LUAJIT_VERSION="2.0.4" +LUAJIT_BASE="LuaJIT-$LUAJIT_VERSION" + +source .travis/platform.sh + +LUA_HOME_DIR=$TRAVIS_BUILD_DIR/install/lua + +LR_HOME_DIR=$TRAVIS_BUILD_DIR/install/luarocks + +mkdir $HOME/.lua + +LUAJIT="no" + +if [ "$PLATFORM" == "macosx" ]; then + if [ "$LUA" == "luajit" ]; then + LUAJIT="yes"; + fi + if [ "$LUA" == "luajit2.0" ]; then + LUAJIT="yes"; + fi + if [ "$LUA" == "luajit2.1" ]; then + LUAJIT="yes"; + fi; +elif [ "$(expr substr $LUA 1 6)" == "luajit" ]; then + LUAJIT="yes"; +fi + +mkdir -p "$LUA_HOME_DIR" + +if [ "$LUAJIT" == "yes" ]; then + + if [ "$LUA" == "luajit" ]; then + curl --location https://github.com/LuaJIT/LuaJIT/archive/v$LUAJIT_VERSION.tar.gz | tar xz; + else + git clone https://github.com/LuaJIT/LuaJIT.git $LUAJIT_BASE; + fi + + cd $LUAJIT_BASE + + if [ "$LUA" == "luajit2.1" ]; then + git checkout v2.1; + # force the INSTALL_TNAME to be luajit + perl -i -pe 's/INSTALL_TNAME=.+/INSTALL_TNAME= luajit/' Makefile + fi + + make && make install PREFIX="$LUA_HOME_DIR" + + ln -s $LUA_HOME_DIR/bin/luajit $HOME/.lua/luajit + ln -s $LUA_HOME_DIR/bin/luajit $HOME/.lua/lua; + +else + + if [ "$LUA" == "lua5.1" ]; then + curl http://www.lua.org/ftp/lua-5.1.5.tar.gz | tar xz + cd lua-5.1.5; + elif [ "$LUA" == "lua5.2" ]; then + curl http://www.lua.org/ftp/lua-5.2.4.tar.gz | tar xz + cd lua-5.2.4; + elif [ "$LUA" == "lua5.3" ]; then + curl http://www.lua.org/ftp/lua-5.3.4.tar.gz | tar xz + cd lua-5.3.4; + fi + + # Build Lua without backwards compatibility for testing + perl -i -pe 's/-DLUA_COMPAT_(ALL|5_2)//' src/Makefile + make $PLATFORM + make INSTALL_TOP="$LUA_HOME_DIR" install; + + ln -s $LUA_HOME_DIR/bin/lua $HOME/.lua/lua + ln -s $LUA_HOME_DIR/bin/luac $HOME/.lua/luac; + +fi + +cd $TRAVIS_BUILD_DIR + +lua -v + +LUAROCKS_BASE=luarocks-$LUAROCKS + +curl --location http://luarocks.org/releases/$LUAROCKS_BASE.tar.gz | tar xz + +cd $LUAROCKS_BASE + +if [ "$LUA" == "luajit" ]; then + ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.0" --prefix="$LR_HOME_DIR"; +elif [ "$LUA" == "luajit2.0" ]; then + ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.0" --prefix="$LR_HOME_DIR"; +elif [ "$LUA" == "luajit2.1" ]; then + ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.1" --prefix="$LR_HOME_DIR"; +else + ./configure --with-lua="$LUA_HOME_DIR" --prefix="$LR_HOME_DIR" +fi + +make build && make install + +ln -s $LR_HOME_DIR/bin/luarocks $HOME/.lua/luarocks + +cd $TRAVIS_BUILD_DIR + +luarocks --version + +rm -rf $LUAROCKS_BASE + +if [ "$LUAJIT" == "yes" ]; then + rm -rf $LUAJIT_BASE; +elif [ "$LUA" == "lua5.1" ]; then + rm -rf lua-5.1.5; +elif [ "$LUA" == "lua5.2" ]; then + rm -rf lua-5.2.4; +elif [ "$LUA" == "lua5.3" ]; then + rm -rf lua-5.3.4; +fi diff --git a/tests/config/tapered/CHANGES.md b/tests/config/tapered/CHANGES.md new file mode 100644 index 0000000..63b19b0 --- /dev/null +++ b/tests/config/tapered/CHANGES.md @@ -0,0 +1,70 @@ +# tapered version history + +## *1.0-0* (July 10, 2015) + ++ Initial public release + +## *1.0-1* (July 10, 2015) + ++ Fix rockspec: the URL for Bitbucket was wrong. + +## *1.1-0* (July 13, 2015) + ++ Improve organization and coverage of tests ++ Refactor the `same` method ++ Remove `same_mt` ++ Meaningful exit statuses via `done` ++ Add CI via [drone.io][dio] + +[dio]: https://drone.io/bitbucket.org/telemachus/tapered/latest + +## *1.2.0-1* (July 19, 2015) + ++ Clean up code using luacheck and luacov ++ Small tweaks to README and CHANGES ++ Fix version number: the previous two digit number was a mistake, based on + a misunderstanding of LuaRocks conventions. This is an annoying switch, but + better now than later. And better to do it than to live with a versioning + pattern I dislike. + +## *1.2.1-1* (December 5, 2015) + ++ Test coverage stats are now thanks to [codecov][codecov]. ++ Latest stable Lua in the 5.3 series is 5.3.2, so we test against that now. + +[codecov]: https://codecov.io + +## *2.0.0-1* (May 1, 2016) + ++ The informational fields are now functions that return strings. This is to prevent them from violating Lua recommendations about variables such as `_VERSION`. (I've bumped the major version number since this is technically an API change, though for most users it will not require any changes on their end.) + +## *2.0.1-1* (May 2, 2016) + ++ Fix a typo in the documentation. ++ Adjust the `version()` return value to show only software version, not the rockspec version as well. + +## *2.1.0-1* (July 21, 2016) + ++ Update to test against Lua 5.3.3 + +## *2.2.0-1* (February 11, 2017) + ++ Update to test against Lua 5.3.4 ++ The repo is now housed on [Github](https://github.com/telemachus/tapered) ++ CI is now provided by [Travis.ci](https://travis-ci.org/telemachus/tapered) + +## *2.3.0-1* (October 15, 2017) + ++ Remove `setup()` and `teardown()` ++ Update to use luarocks 2.4.3 for testing on Travis + +Would you rather view the [documentation][d]? + +[d]: /README.md + +--- + +(c) 2012-2017 Peter Aronoff. BSD 3-Clause license; see [LICENSE.md][l] for +details. + +[l]: /LICENSE.md diff --git a/tests/config/tapered/LICENSE.md b/tests/config/tapered/LICENSE.md new file mode 100644 index 0000000..bcaacde --- /dev/null +++ b/tests/config/tapered/LICENSE.md @@ -0,0 +1,27 @@ +Copyright (c) 2012-2017, Peter Aronoff All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/config/tapered/README.md b/tests/config/tapered/README.md new file mode 100644 index 0000000..bafb2e7 --- /dev/null +++ b/tests/config/tapered/README.md @@ -0,0 +1,116 @@ +# tapered [![Build Status](https://travis-ci.org/telemachus/tapered.svg?branch=master)](https://travis-ci.org/telemachus/tapered) [![Coverage](https://codecov.io/gh/telemachus/tapered/branch/master/graph/badge.svg)](https://codecov.io/gh/telemachus/tapered) + +## Synopsis + +Very minimal tap testing for Lua. Arguably too minimal. + +## Assertions + +The `message` parameter is always optional. Brief messages help make test output +clearer to readers, but are not needed if the output goes straight to another +program for parsing. + ++ `ok(expression, [message])` Tests whether `expression` returns a truthy + value. + ++ `nok(expression, [message])` Tests whether `expression` returns a falsy + value. + ++ `is(actual, expected, [message])` Tests whether `actual` is equal to + `expected`. The test uses `==` internally. + ++ `isnt(actual, expected, [message])` Tests whether `actual` is not equal to + `actual`. The test uses `~=` internally. + ++ `same(actual, expected, [message])` Tests whether `actual` is a deep copy + of `expected`. The test uses an `__eq` metamethod if one is found. Useful + for comparing tables. + ++ `like(string, pattern, [message])` Tests whether `string` matches the given + `pattern`. + ++ `unlike(string, pattern, [message])` Tests whether `string` does not match + the given `pattern`. + ++ `pass([message])` A test that always passes. Useful as a quasi-skip with a + message. + ++ `fail([message])` A test that always fails. Useful as a quasi-TODO with a + message. + ++ `boom(function, args, [message])` Calls `function` with `args` as + parameters and checks to see if an exception is raised. Passes if an + exception is raised; fails otherwise. (The exception is swallowed.) The + `args` parameter expects a table. The table can be empty but not `nil`. + +## Helper method + +A method is available to show how many tests were run. (This output +is required for [TAP compliance][tap], which may matter in some cases.) + +[tap]: http://testanything.org/tap-specification.html + ++ `done([number])` Call this function (optionally) at the end of your test file. + It will print out a line in the form `1..n` where `n` is the total number + of tests run. This secures TAP compliance when needed. The call to `done` + is not otherwise required. If you don't care about TAP compliance, neither does + the library. If you pass the optional parameter to the method, it will check + whether the number of tests you expected matches the number of actual tests. + Thus, if can function like a traditional `plan` method. However, this method + should always be called *last* in your tap file, unlike `plan` methods which + normally start the test file. + + Another reason to use `done` is if you care about the exit status of the + tests. Many continuous integration tools rely on tests signalling success or + failure via their exit status. After `done` is called, the script will exit + with a status of 0, indicating success, if all tests passed. If some tests + failed, the script will exit with a status equal to the number of failed + tests, indicating failure. A script will also exit with an error status if + there is a mismatch between the actual number of tests run and the number + passed to `done` as a parameter. + +## Varia + +The module provides four informational functions that return strings. They +should be self-explanatory. + ++ `version() -- 2.3.0` + ++ `author() -- Peter Aronoff` + ++ `url() -- https://github.com/telemachus/tapered.git` + ++ `license() -- BSD 3-Clause` + +## Credits + +For the `same` method I took ideas and code from [Penlight][p], [Underscore][u], +[luassert][l], and [cwtest][cw]. I thank all the people who worked on those. + +Indirect inspirations include [knock][k], [Test::More][tm], and [bats][b]—not so +much for code as for ideas about testing and simplicity. + +Thanks in particular to [Pierre Chapuis][pchapuis] for help with ideas and +getting continuous integration for tapered. + +An anonymous email showed me that my setup and teardown methods had a logical +flaw. As a result, I've removed those methods. I appreciate the report. + +All the mistakes are mine. See [version history][c] for release details. + +[p]: https://github.com/stevedonovan/Penlight +[u]: https://github.com/mirven/underscore.lua +[l]: https://github.com/Olivine-Labs/luassert +[cw]: https://github.com/catwell/cwtest +[k]: https://github.com/chneukirchen/knock +[tm]: http://search.cpan.org/perldoc?Test::More +[b]: https://github.com/sstephenson/bats +[c]: /CHANGES.md +[pchapuis]: https://twitter.com/pchapuis + +--- + +(c) 2012-2017 Peter Aronoff. BSD 3-Clause license; see [LICENSE.md][li] for +details. + +[li]: /LICENSE.md diff --git a/tests/config/tapered/doc/changes.html b/tests/config/tapered/doc/changes.html new file mode 100644 index 0000000..a4f7034 --- /dev/null +++ b/tests/config/tapered/doc/changes.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="author" content="Peter Aronoff"> +<title>tapered version history</title> +<link rel="stylesheet" href="normalize.css" media="screen,projection"> +<link rel="stylesheet" href="screen.css" media="screen,projection"> +</head> +<body> +<h1>tapered version history</h1> + +<h2><em>1.0-0</em> (July 10, 2015)</h2> + +<ul> +<li>Initial public release</li> +</ul> + + +<h2><em>1.0-1</em> (July 10, 2015)</h2> + +<ul> +<li>Fix rockspec: the URL for Bitbucket was wrong.</li> +</ul> + + +<h2><em>1.1-0</em> (July 13, 2015)</h2> + +<ul> +<li>Improve organization and coverage of tests</li> +<li>Refactor the <code>same</code> method</li> +<li>Remove <code>same_mt</code></li> +<li>Meaningful exit statuses via <code>done</code></li> +<li>Add CI via <a href="https://drone.io/bitbucket.org/telemachus/tapered/latest">drone.io</a></li> +</ul> + + +<h2><em>1.2.0-1</em> (July 19, 2015)</h2> + +<ul> +<li>Clean up code using luacheck and luacov</li> +<li>Small tweaks to README and CHANGES</li> +<li>Fix version number: the previous two digit number was a mistake, based on +a misunderstanding of LuaRocks conventions. This is an annoying switch, but +better now than later. And better to do it than to live with a versioning +pattern I dislike.</li> +</ul> + + +<h2><em>1.2.1-1</em> (December 5, 2015)</h2> + +<ul> +<li>Test coverage stats are now thanks to <a href="https://codecov.io">codecov</a>.</li> +<li>Latest stable Lua in the 5.3 series is 5.3.2, so we test against that now.</li> +</ul> + + +<h2><em>2.0.0-1</em> (May 1, 2016)</h2> + +<ul> +<li>The informational fields are now functions that return strings. This is to prevent them from violating Lua recommendations about variables such as <code>_VERSION</code>. (I’ve bumped the major version number since this is technically an API change, though for most users it will not require any changes on their end.)</li> +</ul> + + +<h2><em>2.0.1-1</em> (May 2, 2016)</h2> + +<ul> +<li>Fix a typo in the documentation.</li> +<li>Adjust the <code>version()</code> return value to show only software version, not the rockspec version as well.</li> +</ul> + + +<h2><em>2.1.0-1</em> (July 21, 2016)</h2> + +<ul> +<li>Update to test against Lua 5.3.3</li> +</ul> + + +<h2><em>2.2.0-1</em> (February 11, 2017)</h2> + +<ul> +<li>Update to test against Lua 5.3.4</li> +<li>The repo is now housed on <a href="https://github.com/telemachus/tapered">Github</a></li> +<li>CI is now provided by <a href="https://travis-ci.org/telemachus/tapered">Travis.ci</a></li> +</ul> + + +<h2><em>2.3.0-1</em> (October 15, 2017)</h2> + +<ul> +<li>Remove <code>setup()</code> and <code>teardown()</code></li> +<li>Update to use luarocks 2.4.3 for testing on Travis</li> +</ul> + + +<p>Would you rather view the <a href="index.html">documentation</a>?</p> + +<hr /> + +<p>© 2012-2017 Peter Aronoff. BSD 3-Clause license; see <a href="license.html">the license</a> for +details.</p> +</body> +</html> diff --git a/tests/config/tapered/doc/index.html b/tests/config/tapered/doc/index.html new file mode 100644 index 0000000..69d6b24 --- /dev/null +++ b/tests/config/tapered/doc/index.html @@ -0,0 +1,111 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="author" content="Peter Aronoff"> +<title>tapered documentation</title> +<link rel="stylesheet" href="normalize.css" media="screen,projection"> +<link rel="stylesheet" href="screen.css" media="screen,projection"> +</head> +<body> +<h1>tapered <a href="https://travis-ci.org/telemachus/tapered"><img src="https://travis-ci.org/telemachus/tapered.svg?branch=master" alt="Build Status" /></a> <a href="https://codecov.io/gh/telemachus/tapered"><img src="https://codecov.io/gh/telemachus/tapered/branch/master/graph/badge.svg" alt="Coverage" /></a></h1> + +<h2>Synopsis</h2> + +<p>Very minimal tap testing for Lua. Arguably too minimal.</p> + +<h2>Assertions</h2> + +<p>The <code>message</code> parameter is always optional. Brief messages help make test output +clearer to readers, but are not needed if the output goes straight to another +program for parsing.</p> + +<ul> +<li><p><code>ok(expression, [message])</code> Tests whether <code>expression</code> returns a truthy +value.</p></li> +<li><p><code>nok(expression, [message])</code> Tests whether <code>expression</code> returns a falsy +value.</p></li> +<li><p><code>is(actual, expected, [message])</code> Tests whether <code>actual</code> is equal to +<code>expected</code>. The test uses <code>==</code> internally.</p></li> +<li><p><code>isnt(actual, expected, [message])</code> Tests whether <code>actual</code> is not equal to +<code>actual</code>. The test uses <code>~=</code> internally.</p></li> +<li><p><code>same(actual, expected, [message])</code> Tests whether <code>actual</code> is a deep copy +of <code>expected</code>. The test uses an <code>__eq</code> metamethod if one is found. Useful +for comparing tables.</p></li> +<li><p><code>like(string, pattern, [message])</code> Tests whether <code>string</code> matches the given +<code>pattern</code>.</p></li> +<li><p><code>unlike(string, pattern, [message])</code> Tests whether <code>string</code> does not match +the given <code>pattern</code>.</p></li> +<li><p><code>pass([message])</code> A test that always passes. Useful as a quasi-skip with a +message.</p></li> +<li><p><code>fail([message])</code> A test that always fails. Useful as a quasi-TODO with a +message.</p></li> +<li><p><code>boom(function, args, [message])</code> Calls <code>function</code> with <code>args</code> as +parameters and checks to see if an exception is raised. Passes if an +exception is raised; fails otherwise. (The exception is swallowed.) The +<code>args</code> parameter expects a table. The table can be empty but not <code>nil</code>.</p></li> +</ul> + + +<h2>Helper method</h2> + +<p>A method is available to show how many tests were run. (This output +is required for <a href="http://testanything.org/tap-specification.html">TAP compliance</a>, which may matter in some cases.)</p> + +<ul> +<li><p><code>done([number])</code> Call this function (optionally) at the end of your test file. +It will print out a line in the form <code>1..n</code> where <code>n</code> is the total number +of tests run. This secures TAP compliance when needed. The call to <code>done</code> +is not otherwise required. If you don’t care about TAP compliance, neither does +the library. If you pass the optional parameter to the method, it will check +whether the number of tests you expected matches the number of actual tests. +Thus, if can function like a traditional <code>plan</code> method. However, this method +should always be called <em>last</em> in your tap file, unlike <code>plan</code> methods which +normally start the test file.</p> + +<p>Another reason to use <code>done</code> is if you care about the exit status of the +tests. Many continuous integration tools rely on tests signalling success or +failure via their exit status. After <code>done</code> is called, the script will exit +with a status of 0, indicating success, if all tests passed. If some tests +failed, the script will exit with a status equal to the number of failed +tests, indicating failure. A script will also exit with an error status if +there is a mismatch between the actual number of tests run and the number +passed to <code>done</code> as a parameter.</p></li> +</ul> + + +<h2>Varia</h2> + +<p>The module provides four informational functions that return strings. They +should be self-explanatory.</p> + +<ul> +<li><p><code>version() -- 2.3.0</code></p></li> +<li><p><code>author() -- Peter Aronoff</code></p></li> +<li><p><code>url() -- https://github.com/telemachus/tapered.git</code></p></li> +<li><p><code>license() -- BSD 3-Clause</code></p></li> +</ul> + + +<h2>Credits</h2> + +<p>For the <code>same</code> method I took ideas and code from <a href="https://github.com/stevedonovan/Penlight">Penlight</a>, <a href="https://github.com/mirven/underscore.lua">Underscore</a>, +<a href="https://github.com/Olivine-Labs/luassert">luassert</a>, and <a href="https://github.com/catwell/cwtest">cwtest</a>. I thank all the people who worked on those.</p> + +<p>Indirect inspirations include <a href="https://github.com/chneukirchen/knock">knock</a>, <a href="http://search.cpan.org/perldoc?Test::More">Test::More</a>, and <a href="https://github.com/sstephenson/bats">bats</a>—not so +much for code as for ideas about testing and simplicity.</p> + +<p>Thanks in particular to <a href="https://twitter.com/pchapuis">Pierre Chapuis</a> for help with ideas and +getting continuous integration for tapered.</p> + +<p>An anonymous email showed me that my setup and teardown methods had a logical +flaw. As a result, I’ve removed those methods. I appreciate the report.</p> + +<p>All the mistakes are mine. See <a href="changes.html">version history</a> for release details.</p> + +<hr /> + +<p>© 2012-2017 Peter Aronoff. BSD 3-Clause license; see <a href="license.html">the license</a> for +details.</p> +</body> +</html> diff --git a/tests/config/tapered/doc/license.html b/tests/config/tapered/doc/license.html new file mode 100644 index 0000000..7c8ae8c --- /dev/null +++ b/tests/config/tapered/doc/license.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="author" content="Peter Aronoff"> +<title>tapered license</title> +<link rel="stylesheet" href="normalize.css" media="screen,projection"> +<link rel="stylesheet" href="screen.css" media="screen,projection"> +</head> +<body> +<p>Copyright © 2012-2017, Peter Aronoff All rights reserved.</p> + +<p>Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met:</p> + +<ol> +<li><p>Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer.</p></li> +<li><p>Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution.</p></li> +<li><p>Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission.</p></li> +</ol> + + +<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE.</p> +</body> +</html> diff --git a/tests/config/tapered/doc/normalize.css b/tests/config/tapered/doc/normalize.css new file mode 100644 index 0000000..9fc7ae4 --- /dev/null +++ b/tests/config/tapered/doc/normalize.css @@ -0,0 +1,439 @@ +/*! normalize.css 2011-08-12T17:28 UTC · http://github.com/necolas/normalize.css */ + +/* ============================================================================= + HTML5 display definitions + ========================================================================== */ + +/* + * Corrects block display not defined in IE6/7/8/9 & FF3 + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +/* + * Corrects inline-block display not defined in IE6/7/8/9 & FF3 + */ + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +/* + * Prevents modern browsers from displaying 'audio' without controls + */ + +audio:not([controls]) { + display: none; +} + +/* + * Addresses styling for 'hidden' attribute not present in IE7/8/9, FF3, S4 + * Known issue: no IE6 support + */ + +[hidden] { + display: none; +} + + +/* ============================================================================= + Base + ========================================================================== */ + +/* + * 1. Corrects text resizing oddly in IE6/7 when body font-size is set using em units + * http://clagnut.com/blog/348/#c790 + * 2. Keeps page centred in all browsers regardless of content height + * 3. Prevents iOS text size adjust after orientation change, without disabling user zoom + * www.456bereastreet.com/archive/201012/controlling_text_size_in_safari_for_ios_without_disabling_user_zoom/ + */ + +html { + font-size: 100%; /* 1 */ + overflow-y: scroll; /* 2 */ + -webkit-text-size-adjust: 100%; /* 3 */ + -ms-text-size-adjust: 100%; /* 3 */ +} + +/* + * Addresses margins handled incorrectly in IE6/7 + */ + +body { + margin: 0; +} + +/* + * Addresses font-family inconsistency between 'textarea' and other form elements. + */ + +body, +button, +input, +select, +textarea { + font-family: sans-serif; +} + + +/* ============================================================================= + Links + ========================================================================== */ + +a { + color: #00e; +} + +a:visited { + color: #551a8b; +} + +/* + * Addresses outline displayed oddly in Chrome + */ + +a:focus { + outline: thin dotted; +} + +/* + * Improves readability when focused and also mouse hovered in all browsers + * people.opera.com/patrickl/experiments/keyboard/test + */ + +a:hover, +a:active { + outline: 0; +} + + +/* ============================================================================= + Typography + ========================================================================== */ + +/* + * Addresses styling not present in IE7/8/9, S5, Chrome + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/* + * Addresses style set to 'bolder' in FF3/4, S4/5, Chrome +*/ + +b, +strong { + font-weight: bold; +} + +blockquote { + margin: 1em 40px; +} + +/* + * Addresses styling not present in S5, Chrome + */ + +dfn { + font-style: italic; +} + +/* + * Addresses styling not present in IE6/7/8/9 + */ + +mark { + background: #ff0; + color: #000; +} + +/* + * Corrects font family set oddly in IE6, S4/5, Chrome + * en.wikipedia.org/wiki/User:Davidgothberg/Test59 + */ + +pre, +code, +kbd, +samp { + font-family: monospace, serif; + _font-family: 'courier new', monospace; + font-size: 1em; +} + +/* + * Improves readability of pre-formatted text in all browsers + */ + +pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +/* + * 1. Addresses CSS quotes not supported in IE6/7 + * 2. Addresses quote property not supported in S4 + */ + +/* 1 */ + +q { + quotes: none; +} + +/* 2 */ + +q:before, +q:after { + content: ''; + content: none; +} + +small { + font-size: 75%; +} + +/* + * Prevents sub and sup affecting line-height in all browsers + * gist.github.com/413930 + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + + +/* ============================================================================= + Lists + ========================================================================== */ + +ul, +ol { + margin: 1em 0; + padding: 0 0 0 40px; +} + +dd { + margin: 0 0 0 40px; +} + +nav ul, +nav ol { + list-style: none; + list-style-image: none; +} + + +/* ============================================================================= + Embedded content + ========================================================================== */ + +/* + * 1. Removes border when inside 'a' element in IE6/7/8/9 + * 2. Improves image quality when scaled in IE7 + * code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ + */ + +img { + border: 0; /* 1 */ + -ms-interpolation-mode: bicubic; /* 2 */ +} + +/* + * Corrects overflow displayed oddly in IE9 + */ + +svg:not(:root) { + overflow: hidden; +} + + +/* ============================================================================= + Figures + ========================================================================== */ + +/* + * Addresses margin not present in IE6/7/8/9, S5, O11 + */ + +figure { + margin: 0; +} + + +/* ============================================================================= + Forms + ========================================================================== */ + +/* + * Corrects margin displayed oddly in IE6/7 + */ + +form { + margin: 0; +} + +/* + * Define consistent margin and padding + */ + +fieldset { + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/* + * 1. Corrects color not being inherited in IE6/7/8/9 + * 2. Corrects alignment displayed oddly in IE6/7 + */ + +legend { + border: 0; /* 1 */ + *margin-left: -7px; /* 2 */ +} + +/* + * 1. Corrects font size not being inherited in all browsers + * 2. Addresses margins set differently in IE6/7, F3/4, S5, Chrome + * 3. Improves appearance and consistency in all browsers + */ + +button, +input, +select, +textarea { + font-size: 100%; /* 1 */ + margin: 0; /* 2 */ + vertical-align: baseline; /* 3 */ + *vertical-align: middle; /* 3 */ +} + +/* + * 1. Addresses FF3/4 setting line-height using !important in the UA stylesheet + * 2. Corrects inner spacing displayed oddly in IE6/7 + */ + +button, +input { + line-height: normal; /* 1 */ + *overflow: visible; /* 2 */ +} + +/* + * Corrects overlap and whitespace issue for buttons and inputs in IE6/7 + * Known issue: reintroduces inner spacing + */ + +table button, +table input { + *overflow: auto; +} + +/* + * 1. Improves usability and consistency of cursor style between image-type 'input' and others + * 2. Corrects inability to style clickable 'input' types in iOS + */ + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; /* 1 */ + -webkit-appearance: button; /* 2 */ +} + +/* + * 1. Addresses box sizing set to content-box in IE8/9 + * 2. Addresses excess padding in IE8/9 + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/* + * 1. Addresses appearance set to searchfield in S5, Chrome + * 2. Addresses box sizing set to border-box in S5, Chrome (include -moz to future-proof) + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/* + * Corrects inner padding displayed oddly in S5, Chrome on OSX + */ + +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* + * Corrects inner padding and border displayed oddly in FF3/4 + * www.sitepen.com/blog/2008/05/14/the-devils-in-the-details-fixing-dojos-toolbar-buttons/ + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/* + * 1. Removes default vertical scrollbar in IE6/7/8/9 + * 2. Improves readability and alignment in all browsers + */ + +textarea { + overflow: auto; /* 1 */ + vertical-align: top; /* 2 */ +} + + +/* ============================================================================= + Tables + ========================================================================== */ + +/* + * Remove most spacing between table cells + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/tests/config/tapered/doc/screen.css b/tests/config/tapered/doc/screen.css new file mode 100644 index 0000000..2583183 --- /dev/null +++ b/tests/config/tapered/doc/screen.css @@ -0,0 +1,109 @@ +body { + margin: 10px auto; + width: 820px; + font-size: 18px; + font-family: "DejaVuSerifBook", "Palatino", "Palatino Linotype", serif; + color: #333; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "DejaVuSansBook", "Lucida Grande", "Helvetica Neue", sans-serif; + font-weight: bold; + line-height: 1.6; + margin-bottom: 5px; + color: #111; +} + +h1 { + margin-top: 10px; +} + +h2, h3, h4, h5, h6 { margin-top: 20px; } + +h1 { font-size: 28px; } + +h2 { font-size: 24px; border-bottom: solid 4px; } +h3 { font-size: 20px; } + +p { + line-height: 1.3; + margin: 12px auto; + +} + +code { + font-family: "DejaVuSansMono", Inconsolata, Consolas, Menlo, monospace; +} + +blockquote { + margin: 10px 30px 10px 20px; +} + +blockquote p { + line-height: 1.2; +} + +ul, ol, ul p, ol p { + margin-left: 0; + line-height: 1.3; +} + +ul ul, ol ol { + font-size: 100%; +} + +ul { + list-style-type: disc; +} + +ul ul { + list-style-type: circle; +} + +ul ul ul { + list-style-type: square; +} + +ul ul ul ul { + list-style-type: disc; +} + +h2 + p { + margin-top: 15px; +} + +a:link, a:visited { + text-decoration: underline; + color: #336891; +} + +a:hover, a:focus { + text-decoration: none; + color: #336891; +} + +pre { + margin: 30px 15px; + padding: 10px 5px 10px 15px; + border: solid black 5px; + border-left: solid black 30px; + font-size: 14px; + line-height: 1.3; + background: #FFF; + color: #000; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} + +ins { + text-decoration: none; +} + +ins:before { + content: "〈"; +} + +ins:after { + content: "〉"; +} diff --git a/tests/config/tapered/make-doc.bash b/tests/config/tapered/make-doc.bash new file mode 100755 index 0000000..4ceb65d --- /dev/null +++ b/tests/config/tapered/make-doc.bash @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +{ + printf "<!DOCTYPE html>\n" + printf "<html lang=\"en\">\n" + printf "<head>\n" + printf "<meta charset=\"utf-8\">\n" + printf "<meta name=\"author\" content=\"Peter Aronoff\">\n" + printf "<title>tapered documentation</title>\n" + printf "<link rel=\"stylesheet\" href=\"normalize.css\" " + printf "media=\"screen,projection\">\n" + printf "<link rel=\"stylesheet\" href=\"screen.css\" " + printf "media=\"screen,projection\">\n" + printf "</head>\n" + printf "<body>\n" + markdown README.md | sed -e 's/\/CHANGES.md/changes.html/' -e 's/\/LICENSE.md/license.html/' -e 's/LICENSE.md/the license/' + printf "</body>\n" + printf "</html>\n" +} > doc/index.html + +{ + printf "<!DOCTYPE html>\n" + printf "<html lang=\"en\">\n" + printf "<head>\n" + printf "<meta charset=\"utf-8\">\n" + printf "<meta name=\"author\" content=\"Peter Aronoff\">\n" + printf "<title>tapered version history</title>\n" + printf "<link rel=\"stylesheet\" href=\"normalize.css\" " + printf "media=\"screen,projection\">\n" + printf "<link rel=\"stylesheet\" href=\"screen.css\" " + printf "media=\"screen,projection\">\n" + printf "</head>\n" + printf "<body>\n" + markdown CHANGES.md | sed -e 's/\/README.md/index.html/' -e 's/\/LICENSE.md/license.html/' -e 's/LICENSE.md/the license/' + printf "</body>\n" + printf "</html>\n" +} > doc/changes.html + +{ + printf "<!DOCTYPE html>\n" + printf "<html lang=\"en\">\n" + printf "<head>\n" + printf "<meta charset=\"utf-8\">\n" + printf "<meta name=\"author\" content=\"Peter Aronoff\">\n" + printf "<title>tapered license</title>\n" + printf "<link rel=\"stylesheet\" href=\"normalize.css\" " + printf "media=\"screen,projection\">\n" + printf "<link rel=\"stylesheet\" href=\"screen.css\" " + printf "media=\"screen,projection\">\n" + printf "</head>\n" + printf "<body>\n" + markdown LICENSE.md + printf "</body>\n" + printf "</html>\n" +} > doc/license.html diff --git a/tests/config/tapered/src/tapered.lua b/tests/config/tapered/src/tapered.lua new file mode 100644 index 0000000..de648c2 --- /dev/null +++ b/tests/config/tapered/src/tapered.lua @@ -0,0 +1,205 @@ +-- vim: et:ts=2:sw=2 +-- Helper variables and functions +local get_info = debug.getinfo +local pcall = pcall +local slice = string.sub +local sprintf = string.format +local str_find = string.find +local tonumber = tonumber +-- Lua 5.3 moved unpack to table.unpack +local unpack = unpack or table.unpack +local write = io.write +local rawget = rawget +local getmetatable = getmetatable +local exit = os.exit +local krprint = require("krprint") + +---- Helper methods + +--- C-like printf method +local printf = function(fmt, ...) + write(sprintf(fmt, ...)) +end + +local printdiff = function(func_name, actual, expected) + printf("Assertion %s() failed for test below (marked 'not ok'):\n", func_name) + printf("Expected: %s\n", krprint.pprint(expected)) + printf("Got: %s\n", krprint.pprint(actual)) +end + +--- Compare potentially complex tables or objects +-- +-- Ideas here are taken from [Penlight][p], [Underscore][u], [cwtest][cw], and +-- [luassert][l]. +-- [p]: https://github.com/stevedonovan/Penlight +-- [u]: https://github.com/mirven/underscore.lua +-- [cw]: https://github.com/catwell/cwtest +-- [l]: https://github.com/Olivine-Labs/luassert +-- +-- Predeclare both function names +local keyvaluesame +local deepsame +-- +--- keyvaluesame(table, table) => true or false +-- Helper method to compare all the keys and values of a table +keyvaluesame = function (t1, t2) + for k1, v1 in pairs(t1) do + local v2 = t2[k1] + if v2 == nil or not deepsame(v1, v2) then return false end + end + + -- Check for any keys present in t2 but not t1 + for k2, _ in pairs(t2) do + if t1[k2] == nil then return false end + end + + return true +end +-- +--- deepsame(item, item) => true or false +-- Compare two items of any type for identity +deepsame = function (t1, t2) + local ty1, ty2 = type(t1), type(t2) + if ty1 ~= ty2 then return false end + if ty1 ~= 'table' then return t1 == t2 end + + -- If ._eq is found, use == and end quickly. + -- As of Lua 5.3 == only cares if **one** of the two items has a __eq + -- metamethod. Penlight, underscore and cwtest take the same approach, + -- so I will as well. + local eq = rawget(getmetatable(t1) or {}, '__eq') + if (type(eq) == 'function') then + return not not eq(t1, t2) + else + return keyvaluesame(t1, t2) + end +end + +---- tapered test suite + +local exit_status = 0 +local test_count = 0 +local debug_level = 3 + +-- All other tests are defined in terms of this primitive, which is +-- kept private. +local _test = function (exp, msg) + test_count = test_count + 1 + + if msg then + msg = sprintf(" - %s", msg) + else + msg = '' + end + + if exp then + printf("ok %s%s\n", test_count, msg) + else + exit_status = 1 + exit_status + printf("not ok %s%s\n", test_count, msg) + local info = get_info(debug_level) + printf("# Trouble in %s around line %s\n", + slice(info.source, 2), info.currentline) + end +end + +local ok = function (expression, msg) + _test(expression, msg) +end + +local nok = function (expression, msg) + _test(not expression, msg) +end + +local is = function (actual, expected, msg) + local result = actual == expected; + if not result then + printdiff("is", actual, expected) + end + _test(result, msg) +end + +local isnt = function (actual, expected, msg) + _test(actual ~= expected, msg) +end + +local same = function (actual, expected, msg) + local result = deepsame(actual, expected) + if not result then + printdiff("same", actual, expected) + end + _test(result, msg) +end + +local like = function (str, pattern, msg) + _test(str_find(str, pattern), msg) +end + +local unlike = function (str, pattern, msg) + _test(not str_find(str, pattern), msg) +end + +local pass = function (msg) + _test(true, msg) +end + +local fail = function (msg) + _test(false, msg) +end + +local boom = function (func, args, msg) + local err, errmsg + err, errmsg = pcall(func, unpack(args)) + _test(not err, msg) + if not err and type(errmsg) == 'string' then + printf('# Got this error: "%s"\n', errmsg) + end +end + +local done = function (n) + local expected = tonumber(n) + if not expected or test_count == expected then + printf('1..%d\n', test_count) + elseif expected ~= test_count then + exit_status = 1 + exit_status + local s + if expected == 1 then s = '' else s = 's' end + printf("# Bad plan. You planned %d test%s but ran %d\n", + expected, s, test_count) + end + exit(exit_status) +end + +local version = function () + return "2.3.0" +end + +local author = function () + return "Peter Aronoff" +end + +local url = function () + return "https://github.com/telemachus/tapered" +end + +local license = function () + return "BSD 3-Clause" +end + +return { + ok = ok, + nok = nok, + is = is, + isnt = isnt, + same = same, + like = like, + unlike = unlike, + pass = pass, + fail = fail, + boom = boom, + done = done, + version = version, + author = author, + url = url, + license = license +} diff --git a/tests/config/tapered/tapered-1.1-0.rockspec b/tests/config/tapered/tapered-1.1-0.rockspec new file mode 100644 index 0000000..d6dea7d --- /dev/null +++ b/tests/config/tapered/tapered-1.1-0.rockspec @@ -0,0 +1,21 @@ +package = 'tapered' +version = '1.1-0' +source = { + url = 'https://bitbucket.org/telemachus/tapered/downloads/tapered-v1.1-0.tar.gz', + dir = 'tapered', +} +description = { + summary = 'Very minimal tap testing for Lua.', + detailed = [[ + Very minimal tap testing for Lua. Arguably too minimal. + ]], + license = 'BSD 3-Clause' +} +dependencies = { + 'lua >= 5.1' +} +build = { + type = 'builtin', + modules = { tapered = 'src/tapered.lua' }, + copy_directories = { 'doc' } +} diff --git a/tests/config/tapered/tapered-1.2.0-1.rockspec b/tests/config/tapered/tapered-1.2.0-1.rockspec new file mode 100644 index 0000000..5e6c364 --- /dev/null +++ b/tests/config/tapered/tapered-1.2.0-1.rockspec @@ -0,0 +1,21 @@ +package = 'tapered' +version = '1.2.0-1' +source = { + url = 'https://bitbucket.org/telemachus/tapered/downloads/tapered-v1.2.0-1.tar.gz', + dir = 'tapered', +} +description = { + summary = 'Very minimal tap testing for Lua.', + detailed = [[ + Very minimal tap testing for Lua. Arguably too minimal. + ]], + license = 'BSD 3-Clause' +} +dependencies = { + 'lua >= 5.1' +} +build = { + type = 'builtin', + modules = { tapered = 'src/tapered.lua' }, + copy_directories = { 'doc' } +} diff --git a/tests/config/tapered/tapered-1.2.1-1.rockspec b/tests/config/tapered/tapered-1.2.1-1.rockspec new file mode 100644 index 0000000..673e1af --- /dev/null +++ b/tests/config/tapered/tapered-1.2.1-1.rockspec @@ -0,0 +1,21 @@ +package = 'tapered' +version = '1.2.1-1' +source = { + url = 'https://bitbucket.org/telemachus/tapered/downloads/tapered-v1.2.1-1.tar.gz', + dir = 'tapered', +} +description = { + summary = 'Very minimal tap testing for Lua.', + detailed = [[ + Very minimal tap testing for Lua. Arguably too minimal. + ]], + license = 'BSD 3-Clause' +} +dependencies = { + 'lua >= 5.1' +} +build = { + type = 'builtin', + modules = { tapered = 'src/tapered.lua' }, + copy_directories = { 'doc' } +} diff --git a/tests/config/tapered/tapered-2.0.0-1.rockspec b/tests/config/tapered/tapered-2.0.0-1.rockspec new file mode 100644 index 0000000..5e5484a --- /dev/null +++ b/tests/config/tapered/tapered-2.0.0-1.rockspec @@ -0,0 +1,21 @@ +package = 'tapered' +version = '2.0.0-1' +source = { + url = 'https://bitbucket.org/telemachus/tapered/downloads/tapered-v2.0.0-1.tar.gz', + dir = 'tapered', +} +description = { + summary = 'Very minimal tap testing for Lua.', + detailed = [[ + Very minimal tap testing for Lua. Arguably too minimal. + ]], + license = 'BSD 3-Clause' +} +dependencies = { + 'lua >= 5.1' +} +build = { + type = 'builtin', + modules = { tapered = 'src/tapered.lua' }, + copy_directories = { 'doc' } +} diff --git a/tests/config/tapered/tapered-2.0.1-1.rockspec b/tests/config/tapered/tapered-2.0.1-1.rockspec new file mode 100644 index 0000000..1127375 --- /dev/null +++ b/tests/config/tapered/tapered-2.0.1-1.rockspec @@ -0,0 +1,21 @@ +package = 'tapered' +version = '2.0.1-1' +source = { + url = 'https://bitbucket.org/telemachus/tapered/downloads/tapered-v2.0.1-1.tar.gz', + dir = 'tapered', +} +description = { + summary = 'Very minimal tap testing for Lua.', + detailed = [[ + Very minimal tap testing for Lua. Arguably too minimal. + ]], + license = 'BSD 3-Clause' +} +dependencies = { + 'lua >= 5.1' +} +build = { + type = 'builtin', + modules = { tapered = 'src/tapered.lua' }, + copy_directories = { 'doc' } +} diff --git a/tests/config/tapered/tapered-2.1.0-1.rockspec b/tests/config/tapered/tapered-2.1.0-1.rockspec new file mode 100644 index 0000000..f14c756 --- /dev/null +++ b/tests/config/tapered/tapered-2.1.0-1.rockspec @@ -0,0 +1,21 @@ +package = 'tapered' +version = '2.1.0-1' +source = { + url = 'https://bitbucket.org/telemachus/tapered/downloads/tapered-v2.1.0-1.tar.gz', + dir = 'tapered', +} +description = { + summary = 'Very minimal tap testing for Lua.', + detailed = [[ + Very minimal tap testing for Lua. Arguably too minimal. + ]], + license = 'BSD 3-Clause' +} +dependencies = { + 'lua >= 5.1' +} +build = { + type = 'builtin', + modules = { tapered = 'src/tapered.lua' }, + copy_directories = { 'doc' } +} diff --git a/tests/config/tapered/tapered-2.2.0-1.rockspec b/tests/config/tapered/tapered-2.2.0-1.rockspec new file mode 100644 index 0000000..cfb8b51 --- /dev/null +++ b/tests/config/tapered/tapered-2.2.0-1.rockspec @@ -0,0 +1,22 @@ +package = 'tapered' +version = '2.2.0-1' +source = { + url = 'git://github.com/telemachus/tapered.git', + branch = 'master', + tag = 'v2.2.0-1' +} +description = { + summary = 'Very minimal tap testing for Lua.', + detailed = [[ + Very minimal tap testing for Lua. Arguably too minimal. + ]], + license = 'BSD 3-Clause' +} +dependencies = { + 'lua >= 5.1' +} +build = { + type = 'builtin', + modules = { tapered = 'src/tapered.lua' }, + copy_directories = { 'doc' } +} diff --git a/tests/config/tapered/tapered-2.3.0-1.rockspec b/tests/config/tapered/tapered-2.3.0-1.rockspec new file mode 100644 index 0000000..b3060fb --- /dev/null +++ b/tests/config/tapered/tapered-2.3.0-1.rockspec @@ -0,0 +1,22 @@ +package = 'tapered' +version = '2.3.0-1' +source = { + url = 'git://github.com/telemachus/tapered.git', + branch = 'master', + tag = 'v2.3.0-1' +} +description = { + summary = 'Very minimal tap testing for Lua.', + detailed = [[ + Very minimal tap testing for Lua. Arguably too minimal. + ]], + license = 'BSD 3-Clause' +} +dependencies = { + 'lua >= 5.1' +} +build = { + type = 'builtin', + modules = { tapered = 'src/tapered.lua' }, + copy_directories = { 'doc' } +} diff --git a/tests/config/tapered/test/.luacov b/tests/config/tapered/test/.luacov new file mode 100644 index 0000000..7d3205e --- /dev/null +++ b/tests/config/tapered/test/.luacov @@ -0,0 +1,55 @@ +--- Global configuration file. Copy, customize and store in your +-- project folder as '.luacov' for project specific configuration. +-- If some options are missing, their default values from this file +-- will be used. +-- @class module +-- @name luacov.defaults +return { + + -- default filename to load for config options if not provided + -- only has effect in 'luacov.defaults.lua' + ["configfile"] = ".luacov", + + -- filename to store stats collected + ["statsfile"] = "luacov.stats.out", + + -- filename to store report + ["reportfile"] = "luacov.report.out", + + -- luacov.stats file updating frequency. + -- The lower this value - the more frequenty results will be written out to luacov.stats + -- You may want to reduce this value for short lived scripts (to for example 2) to avoid losing coverage data. + ["savestepsize"] = 100, + + -- Run reporter on completion? (won't work for ticks) + runreport = false, + + -- Delete stats file after reporting? + deletestats = false, + + -- Process Lua code loaded from raw strings + -- (that is, when the 'source' field in the debug info + -- does not start with '@') + codefromstrings = false, + + -- Patterns for files to include when reporting + -- all will be included if nothing is listed + -- (exclude overrules include, do not include + -- the .lua extension, path separator is always '/') + ["include"] = { 'src/tapered' }, + + -- Patterns for files to exclude when reporting + -- all will be included if nothing is listed + -- (exclude overrules include, do not include + -- the .lua extension, path separator is always '/') + ["exclude"] = { + "luacov$", + "luacov/reporter$", + "luacov/defaults$", + "luacov/runner$", + "luacov/stats$", + "luacov/tick$", + }, + + +} diff --git a/tests/config/tapered/test/boom-result.txt b/tests/config/tapered/test/boom-result.txt new file mode 100644 index 0000000..871b509 --- /dev/null +++ b/tests/config/tapered/test/boom-result.txt @@ -0,0 +1,7 @@ +ok 1 - ok - return nada.__eq +# Got this error: "boom-test.lua:13: attempt to index local 'x' (a nil value)" +not ok 2 - not ok - Test boom with a method that throws no exception +# Trouble in boom-test.lua around line 16 +ok 3 - ok - Test boom with a method that throws an exception "Kaboom!" +# Got this error: "Kaboom!" +1..3 diff --git a/tests/config/tapered/test/boom-test.lua b/tests/config/tapered/test/boom-test.lua new file mode 100755 index 0000000..f2b585a --- /dev/null +++ b/tests/config/tapered/test/boom-test.lua @@ -0,0 +1,19 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' +local error = error + +--- boom +-- boom(method, arguments, [msg]) tests if the function throws an exception when +-- given the arguments supplied. The arguments parameter must be a table. It can +-- be empty if the given method takes no arguments, but it cannot be nil. +-- +-- Note that boom expects an exception. It passes if an exception is throw, and +-- fails if one is not. The exception, however, will be swallowed by boom so +-- that test execution continues. +local broken = function (x) return x.__eq end +local add = function (x, y) return x+y end +tap.boom(broken, {}, 'ok - return nada.__eq') +tap.boom(add, {2, 3}, 'not ok - Test boom with a method that throws no exception') +tap.boom(error, {'Kaboom!'}, + 'ok - Test boom with a method that throws an exception "Kaboom!"') +tap.done() diff --git a/tests/config/tapered/test/done-failure-result.txt b/tests/config/tapered/test/done-failure-result.txt new file mode 100644 index 0000000..ce29e24 --- /dev/null +++ b/tests/config/tapered/test/done-failure-result.txt @@ -0,0 +1,2 @@ +ok 1 +# Bad plan. You planned 10 tests but ran 1 diff --git a/tests/config/tapered/test/done-failure-test.lua b/tests/config/tapered/test/done-failure-test.lua new file mode 100644 index 0000000..40603d4 --- /dev/null +++ b/tests/config/tapered/test/done-failure-test.lua @@ -0,0 +1,5 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +tap.ok(true) +tap.done(10) diff --git a/tests/config/tapered/test/done-success-result.txt b/tests/config/tapered/test/done-success-result.txt new file mode 100644 index 0000000..45541bc --- /dev/null +++ b/tests/config/tapered/test/done-success-result.txt @@ -0,0 +1,2 @@ +ok 1 +1..1 diff --git a/tests/config/tapered/test/done-success-test.lua b/tests/config/tapered/test/done-success-test.lua new file mode 100644 index 0000000..9e58ddd --- /dev/null +++ b/tests/config/tapered/test/done-success-test.lua @@ -0,0 +1,5 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +tap.ok(true) +tap.done(1) diff --git a/tests/config/tapered/test/dynamic-setup-teardown-result.txt b/tests/config/tapered/test/dynamic-setup-teardown-result.txt new file mode 100644 index 0000000..7130f99 --- /dev/null +++ b/tests/config/tapered/test/dynamic-setup-teardown-result.txt @@ -0,0 +1,10 @@ +# I'm a little teapot. +ok 1 - setup() only with '# I'm a little teapot.' +# This is my handle and this is my spout. +ok 2 - setup() handle and spout, teardown() cleanup on aisle 9 +# Cleanup on aisle 9! +# This is my handle and this is my spout. +ok 3 - setup() again handle and spout, teardown() changed +# I changed this. +ok 4 - Both setup and teardown should be gone now: redefined as nil. +1..4 diff --git a/tests/config/tapered/test/dynamic-setup-teardown-test.lua b/tests/config/tapered/test/dynamic-setup-teardown-test.lua new file mode 100755 index 0000000..c7abb9d --- /dev/null +++ b/tests/config/tapered/test/dynamic-setup-teardown-test.lua @@ -0,0 +1,27 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +-- luacheck: globals setup teardown +function setup() + print("# I'm a little teapot.") +end +tap.ok(true, "setup() only with '# I'm a little teapot.'") + +function setup() + print('# This is my handle and this is my spout.') +end +function teardown() + print('# Cleanup on aisle 9!') +end +tap.ok(true, 'setup() handle and spout, teardown() cleanup on aisle 9') + +function teardown() + print('# I changed this.') +end +tap.ok(true, 'setup() again handle and spout, teardown() changed') + +setup = nil +teardown = nil +tap.ok(true, 'Both setup and teardown should be gone now: redefined as nil.') + +tap.done() diff --git a/tests/config/tapered/test/exit-failure-test.lua b/tests/config/tapered/test/exit-failure-test.lua new file mode 100755 index 0000000..18339c2 --- /dev/null +++ b/tests/config/tapered/test/exit-failure-test.lua @@ -0,0 +1,6 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +-- Test exit status with a failed test. Call `fail` and we're done! +tap.fail('not ok - No test: just fail') +tap.done() diff --git a/tests/config/tapered/test/exit-success-test.lua b/tests/config/tapered/test/exit-success-test.lua new file mode 100755 index 0000000..b588b64 --- /dev/null +++ b/tests/config/tapered/test/exit-success-test.lua @@ -0,0 +1,6 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +-- Testing exit status. Call `pass` and then we're out. +tap.pass('ok - No test: just pass') +tap.done() diff --git a/tests/config/tapered/test/informational-fields-result.txt b/tests/config/tapered/test/informational-fields-result.txt new file mode 100644 index 0000000..4d0ebc1 --- /dev/null +++ b/tests/config/tapered/test/informational-fields-result.txt @@ -0,0 +1,4 @@ +ok 1 - author() should be 'Peter Aronoff' +ok 2 - version() should be '2.3.0' +ok 3 - license() should be 'BSD 3-Clause' +ok 4 - url() should be 'https://github.com/telemachus/tapered' diff --git a/tests/config/tapered/test/informational-fields-test.lua b/tests/config/tapered/test/informational-fields-test.lua new file mode 100755 index 0000000..35ef7d8 --- /dev/null +++ b/tests/config/tapered/test/informational-fields-test.lua @@ -0,0 +1,8 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +tap.is(tap.author(), 'Peter Aronoff', "author() should be 'Peter Aronoff'") +tap.is(tap.version(), '2.3.0', "version() should be '2.3.0'") +tap.is(tap.license(), 'BSD 3-Clause', "license() should be 'BSD 3-Clause'") +tap.is(tap.url(), "https://github.com/telemachus/tapered", + "url() should be 'https://github.com/telemachus/tapered'") diff --git a/tests/config/tapered/test/is-isnt-result.txt b/tests/config/tapered/test/is-isnt-result.txt new file mode 100644 index 0000000..583a9a7 --- /dev/null +++ b/tests/config/tapered/test/is-isnt-result.txt @@ -0,0 +1,43 @@ +ok 1 - ok - is(2+1, 3) +not ok 2 - not ok - is(2+2, 3) +# Trouble in is-isnt-test.lua around line 8 +ok 3 - ok - is(print, print) +not ok 4 - not ok - is(print, 3) +# Trouble in is-isnt-test.lua around line 10 +ok 5 - ok - is("hello", "hello") +not ok 6 - not ok - is("goodbye", "hello") +# Trouble in is-isnt-test.lua around line 12 +ok 7 - ok - is(nil, nil) +not ok 8 - not ok - is(nil, false) +# Trouble in is-isnt-test.lua around line 14 +ok 9 - ok - is(false, false) +ok 10 - ok - is(true, true) +not ok 11 - not ok - is(true, false) +# Trouble in is-isnt-test.lua around line 17 +not ok 12 - not ok - is(false, true) +# Trouble in is-isnt-test.lua around line 18 +ok 13 - ok - isnt(2+2, 3) +not ok 14 - not ok - isnt(2+2, 4) +# Trouble in is-isnt-test.lua around line 20 +ok 15 - ok - isnt(3, print) +ok 16 - ok - isnt(print, os.exit) +ok 17 - ok - isnt("hello", "goodbye") +not ok 18 - not ok - isnt("hello", "hello") +# Trouble in is-isnt-test.lua around line 24 +not ok 19 - not ok - isnt(nil, nil) +# Trouble in is-isnt-test.lua around line 25 +ok 20 - ok - isnt(nil, false) +not ok 21 - not ok - isnt(false, false) +# Trouble in is-isnt-test.lua around line 27 +not ok 22 - not ok - isnt(true, true) +# Trouble in is-isnt-test.lua around line 28 +ok 23 - ok - isnt(true, false) +ok 24 - ok - isnt(false, true) +ok 25 - ok - isnt(nil, false) +not ok 26 - not ok - isnt(false, false) +# Trouble in is-isnt-test.lua around line 32 +not ok 27 - not ok - isnt(true, true) +# Trouble in is-isnt-test.lua around line 33 +ok 28 - ok - isnt(true, false) +ok 29 - ok - isnt(false, true) +1..29 diff --git a/tests/config/tapered/test/is-isnt-test.lua b/tests/config/tapered/test/is-isnt-test.lua new file mode 100755 index 0000000..d1c7636 --- /dev/null +++ b/tests/config/tapered/test/is-isnt-test.lua @@ -0,0 +1,36 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +--- is and isnt +-- is(actual, expected, [msg]) tests if actual == expected +-- isnt(actual, expected, [msg]) tests if actual ~= expected +tap.is(2+1, 3, 'ok - is(2+1, 3)') +tap.is(2+2, 3, 'not ok - is(2+2, 3)') +tap.is(print, print, 'ok - is(print, print)') +tap.is(print, 3, 'not ok - is(print, 3)') +tap.is('hello', 'hello', 'ok - is("hello", "hello")') +tap.is('goodbye', 'hello', 'not ok - is("goodbye", "hello")') +tap.is(nil, nil, 'ok - is(nil, nil)') +tap.is(nil, false, 'not ok - is(nil, false)') +tap.is(false, false, 'ok - is(false, false)') +tap.is(true, true, 'ok - is(true, true)') +tap.is(true, false, 'not ok - is(true, false)') +tap.is(false, true, 'not ok - is(false, true)') +tap.isnt(2+2, 3, 'ok - isnt(2+2, 3)') +tap.isnt(2+2, 4, 'not ok - isnt(2+2, 4)') +tap.isnt(3, print, 'ok - isnt(3, print)') +tap.isnt(print, os.exit, 'ok - isnt(print, os.exit)') +tap.isnt('hello', 'goodbye', 'ok - isnt("hello", "goodbye")') +tap.isnt('hello', 'hello', 'not ok - isnt("hello", "hello")') +tap.isnt(nil, nil, 'not ok - isnt(nil, nil)') +tap.isnt(nil, false, 'ok - isnt(nil, false)') +tap.isnt(false, false, 'not ok - isnt(false, false)') +tap.isnt(true, true, 'not ok - isnt(true, true)') +tap.isnt(true, false, 'ok - isnt(true, false)') +tap.isnt(false, true, 'ok - isnt(false, true)') +tap.isnt(nil, false, 'ok - isnt(nil, false)') +tap.isnt(false, false, 'not ok - isnt(false, false)') +tap.isnt(true, true, 'not ok - isnt(true, true)') +tap.isnt(true, false, 'ok - isnt(true, false)') +tap.isnt(false, true, 'ok - isnt(false, true)') +tap.done() diff --git a/tests/config/tapered/test/like-unlike-result.txt b/tests/config/tapered/test/like-unlike-result.txt new file mode 100644 index 0000000..f61aac2 --- /dev/null +++ b/tests/config/tapered/test/like-unlike-result.txt @@ -0,0 +1,13 @@ +ok 1 - ok - like('sat', 'sat') +not ok 2 - not ok - like('sat', 'bbb') +# Trouble in like-unlike-test.lua around line 8 +ok 3 - ok - unlike('sat', 'q') +not ok 4 - not ok - unlike('q', 'q') +# Trouble in like-unlike-test.lua around line 10 +ok 5 - ok - like(' sat', '%s+sat') +ok 6 - ok - like('934', '%d%d%d') +ok 7 - ok - like('934', '%d%d') +not ok 8 - not ok - like('934', '%d%s') +# Trouble in like-unlike-test.lua around line 14 +ok 9 - ok - unlike('934', '%d%s') +1..9 diff --git a/tests/config/tapered/test/like-unlike-test.lua b/tests/config/tapered/test/like-unlike-test.lua new file mode 100755 index 0000000..ab48ca7 --- /dev/null +++ b/tests/config/tapered/test/like-unlike-test.lua @@ -0,0 +1,16 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +--- like and unlike +-- like(string, pattern, [msg]) tests if the pattern matches the string +-- unlike(string, pattern, [msg]) tests if the pattern does not match the string +tap.like('sat', 'sat', "ok - like('sat', 'sat')") +tap.like('sat', 'bbb', "not ok - like('sat', 'bbb')") +tap.unlike('sat', 'q', "ok - unlike('sat', 'q')") +tap.unlike('q', 'q', "not ok - unlike('q', 'q')") +tap.like(' sat', '%s+sat', "ok - like(' sat', '%s+sat')") +tap.like('934', '%d%d%d', "ok - like('934', '%d%d%d')") +tap.like('934', '%d%d', "ok - like('934', '%d%d')") +tap.like('934', '%d%s', "not ok - like('934', '%d%s')") +tap.unlike('934', '%d%s', "ok - unlike('934', '%d%s')") +tap.done() diff --git a/tests/config/tapered/test/ok-nok-result.txt b/tests/config/tapered/test/ok-nok-result.txt new file mode 100644 index 0000000..ef45c6a --- /dev/null +++ b/tests/config/tapered/test/ok-nok-result.txt @@ -0,0 +1,7 @@ +ok 1 - ok - ok(true) +not ok 2 - not ok - ok(false) +# Trouble in ok-nok-test.lua around line 8 +ok 3 - ok - nok(false) +not ok 4 - not ok - nok(true) +# Trouble in ok-nok-test.lua around line 10 +1..4 diff --git a/tests/config/tapered/test/ok-nok-test.lua b/tests/config/tapered/test/ok-nok-test.lua new file mode 100755 index 0000000..c2a0b4d --- /dev/null +++ b/tests/config/tapered/test/ok-nok-test.lua @@ -0,0 +1,11 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +--- ok and nok +-- ok(exp, [msg]) tests if exp returns (or is) a truthy value. +-- nok(exp, [msg]) tests if exp returns (or is) a falsey value. +tap.ok(true, 'ok - ok(true)') +tap.ok(false, 'not ok - ok(false)') +tap.nok(false, 'ok - nok(false)') +tap.nok(true, 'not ok - nok(true)') +tap.done() diff --git a/tests/config/tapered/test/pass-fail-result.txt b/tests/config/tapered/test/pass-fail-result.txt new file mode 100644 index 0000000..8c29e66 --- /dev/null +++ b/tests/config/tapered/test/pass-fail-result.txt @@ -0,0 +1,4 @@ +ok 1 - ok - No test: just pass +not ok 2 - not ok - No test: just fail +# Trouble in pass-fail-test.lua around line 10 +1..2 diff --git a/tests/config/tapered/test/pass-fail-test.lua b/tests/config/tapered/test/pass-fail-test.lua new file mode 100755 index 0000000..0f2418a --- /dev/null +++ b/tests/config/tapered/test/pass-fail-test.lua @@ -0,0 +1,11 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +--- pass and fail +-- pass([msg]) is not a test. It always passes. +-- fail([msg]) is not a test. It always fails. +-- These two methods are useful for checking the basic setup of a test suite. +-- Also, pass can be used as a quasi-skip and fail as a quasi-TODO. +tap.pass('ok - No test: just pass') +tap.fail('not ok - No test: just fail') +tap.done() diff --git a/tests/config/tapered/test/result_test03.txt b/tests/config/tapered/test/result_test03.txt new file mode 100644 index 0000000..58c066c --- /dev/null +++ b/tests/config/tapered/test/result_test03.txt @@ -0,0 +1,55 @@ +ok 1 - Test ok on true +not ok 2 - Test ok on false +# Trouble in test03-allfuncs.lua around line 9 +ok 3 - Test nok on false +not ok 4 - Test nok on true +# Trouble in test03-allfuncs.lua around line 11 +ok 5 - Test is on 2 + 1 = 3 +not ok 6 - Test is on 2 + 2 = 3 +# Trouble in test03-allfuncs.lua around line 17 +ok 7 - Test isnt on 2 + 2 = 3 +not ok 8 - Test isnt on 2 + 2 = 4 +# Trouble in test03-allfuncs.lua around line 19 +ok 9 - Test same on two empty tables +ok 10 - Test same on two identical, simple tables +not ok 11 - Test same on two simple non-identical tables +# Trouble in test03-allfuncs.lua around line 36 +ok 12 - Test same on two identical, nested tables +ok 13 - Test same on identical, array-like tables +not ok 14 - Test same on two non-identical, array-like tables +# Trouble in test03-allfuncs.lua around line 43 +ok 15 - Test same on two identical, hash-like tables +not ok 16 - Test same on two non-identical, hash-like tables +# Trouble in test03-allfuncs.lua around line 65 +ok 17 - Test same on two identical tables with functions as values +not ok 18 - Test same on two non-identical tables with functions as values +# Trouble in test03-allfuncs.lua around line 74 +ok 19 - Test same on two dissimilar tables that share .__eq => true +not ok 20 - Test same: first table .__eq => true, second => false +# Trouble in test03-allfuncs.lua around line 89 +not ok 21 - Test same: first table .__eq => false, second => true +# Trouble in test03-allfuncs.lua around line 90 +not ok 22 - Test same on two similar tables where first .__eq => true +# Trouble in test03-allfuncs.lua around line 91 +not ok 23 - Test same on two similar tables where second .__eq => true +# Trouble in test03-allfuncs.lua around line 92 +ok 24 - Test like with string 'sat' and pattern 'sat' +not ok 25 - Test like with string 'sat' and pattern 'bbb' +# Trouble in test03-allfuncs.lua around line 98 +ok 26 - Test unlike with string 'sat' and pattern 'q' +not ok 27 - Test unlike with string 'q' and pattern 'q' +# Trouble in test03-allfuncs.lua around line 100 +ok 28 - Test like with string ' sat' and pattern '%s+sat' +ok 29 - Test like with string '934' and pattern '%d%d%d' +ok 30 - Test like with string '934' and pattern '%d%d' +ok 31 - Test unlike with string '934' and pattern '%d%s' +ok 32 - No test: just pass +not ok 33 - No test: just fail +# Trouble in test03-allfuncs.lua around line 112 +not ok 34 - Test boom with a method that throws no exception +# Trouble in test03-allfuncs.lua around line 123 +ok 35 - Test boom with a method that throws an exception "Kaboom!" +# Got this error: +# Kaboom! +1..35 +# Bad plan. You planned 43 tests but ran 35 diff --git a/tests/config/tapered/test/runner.bash b/tests/config/tapered/test/runner.bash new file mode 100755 index 0000000..f7f38fc --- /dev/null +++ b/tests/config/tapered/test/runner.bash @@ -0,0 +1,71 @@ +#!/usr/bin/env bats + +@test "ignition!" { + run true + [ "$status" -eq 0 ] + [ "$output" = "" ] +} + +@test "ok and nok" { + run lua -lluacov ok-nok-test.lua + [ "$status" -eq 2 ] + [ "$output" = "$(cat ok-nok-result.txt)" ] +} + +@test "is and isnt" { + run lua -lluacov is-isnt-test.lua + [ "$status" -eq 13 ] + [ "$output" = "$(cat is-isnt-result.txt)" ] +} + +@test "same" { + run lua -lluacov same-test.lua + [ "$status" -eq 10 ] + [ "$output" = "$(cat same-result.txt)" ] +} + +@test "like and unlike" { + run lua -lluacov like-unlike-test.lua + [ "$status" -eq 3 ] + [ "$output" = "$(cat like-unlike-result.txt)" ] +} + +@test "pass and fail" { + run lua -lluacov pass-fail-test.lua + [ "$status" -eq 1 ] + [ "$output" = "$(cat pass-fail-result.txt)" ] +} + +@test "boom" { + run lua -lluacov boom-test.lua + [ "$status" -eq 1 ] + # This is foul, but choices are limited. Error messages for Lua 5.3 + # are different, so I need to look at the output but NOT the errors. + [ "$(grep -v '^#' <<< "$output")" = "$(cat boom-result.txt | grep -v '^#')" ] +} + +@test "done success" { + run lua -lluacov done-success-test.lua + [ "$status" -eq 0 ] + [ "$output" = "$(cat done-success-result.txt)" ] +} + +@test "done failure" { + run lua -lluacov done-failure-test.lua + [ "$status" -eq 1 ] + [ "$output" = "$(cat done-failure-result.txt)" ] +} + +@test "exit status" { + run lua -lluacov exit-success-test.lua + [ "$status" -eq 0 ] + + run lua -lluacov exit-failure-test.lua + [ "$status" -eq 1 ] +} + +@test "informational fields" { + run lua -lluacov informational-fields-test.lua + [ "$status" -eq 0 ] + [ "$output" = "$(cat informational-fields-result.txt)" ] +} diff --git a/tests/config/tapered/test/same-result.txt b/tests/config/tapered/test/same-result.txt new file mode 100644 index 0000000..c77a5a4 --- /dev/null +++ b/tests/config/tapered/test/same-result.txt @@ -0,0 +1,31 @@ +ok 1 - ok - same({}, {} +ok 2 - ok - same({1,2,3}, {1,2,3}) +not ok 3 - not ok - same({1}, {}) +# Trouble in same-test.lua around line 7 +ok 4 - ok - same({{1}, 2, {3,4}}, {{1}, 2, {3,4}) +ok 5 - ok - same({"Monday", "Tuesday"}, {"Monday", "Tuesday"}) +not ok 6 - not ok - same({"Monday", "Tuesday"}, {"Monday", "Tuesday", "Wednesday"}) +# Trouble in same-test.lua around line 15 +ok 7 - ok - same({Monday = 1}, {Monday = 1}) +not ok 8 - not ok - same({Monday = 1}, {Monday = 1, Tuesday = 2}) +# Trouble in same-test.lua around line 29 +ok 9 - ok - same({m = {1,2}, n = {1,2}}, {m = {1,2}, n = {1,2}}) +not ok 10 - not ok - same({m = {1,2}, n = {1,2}}, {m = {1,2}, n = {1,2,3}}) +# Trouble in same-test.lua around line 36 +not ok 11 - not ok - same({m = {1,2}, n = {1,2,3}}, {m = {1,2}, n = {1,2}}) +# Trouble in same-test.lua around line 37 +ok 12 - ok - same({p = print, a = assert}, {p = print, a = assert}) +not ok 13 - not ok - same({p = print, a = assert}, {p = print, a = assert, e = error}) +# Trouble in same-test.lua around line 44 +ok 14 - ok - same({4, s=4}, {6, s=4},__eq => x[s] and y[s] are even) +ok 15 - ok - same({6, s=8}, {4, s=4},__eq => x[s] and y[s] are even) +not ok 16 - not ok - same({4, s=4}, {4, s=3},__eq => x[s] and y[s] are even) +# Trouble in same-test.lua around line 61 +not ok 17 - not ok - same({4, s=4}, {4, s=3},__eq => x[s] and y[s] are even) +# Trouble in same-test.lua around line 63 +not ok 18 - not ok - same({4, s=4}, {6, s=4},__eq => x[s] is even, y[s] odd) +# Trouble in same-test.lua around line 68 +ok 19 - ok - same({4, s=4}, {4, s=3},__eq => x[s] is even, y[s] odd) +not ok 20 - not ok - same({4, s=3}, {4, s=4},__eq => x[s] is even, y[s] odd) +# Trouble in same-test.lua around line 72 +1..20 diff --git a/tests/config/tapered/test/same-test.lua b/tests/config/tapered/test/same-test.lua new file mode 100644 index 0000000..68c3574 --- /dev/null +++ b/tests/config/tapered/test/same-test.lua @@ -0,0 +1,74 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' +-- luacheck: compat + +tap.same({},{}, 'ok - same({}, {}') +tap.same({1,2,3}, {1,2,3}, 'ok - same({1,2,3}, {1,2,3})') +tap.same({1},{}, 'not ok - same({1}, {})') +tap.same({{1}, 2, {3,4}},{{1}, 2, {3,4}}, + 'ok - same({{1}, 2, {3,4}}, {{1}, 2, {3,4})') + +local days1 = { 'Monday', 'Tuesday' } +local days2 = { 'Monday', 'Tuesday' } +tap.same(days1, days2, 'ok - same({"Monday", "Tuesday"}, {"Monday", "Tuesday"})') +local days3 = { 'Monday', 'Tuesday', 'Wednesday', } +tap.same(days2, days3, + 'not ok - same({"Monday", "Tuesday"}, {"Monday", "Tuesday", "Wednesday"})') + +local hash1 = { + Monday = 1, +} +local hash2 = { + Monday = 1, +} +local hash3 = { + Monday = 1, + Tuesday = 2, +} +tap.same(hash1, hash2, 'ok - same({Monday = 1}, {Monday = 1})') +tap.same(hash1, hash3, + 'not ok - same({Monday = 1}, {Monday = 1, Tuesday = 2})') + +local n1 = { m = { 1, 2 }, n = { 1, 2 } } +local n2 = { m = { 1, 2 }, n = { 1, 2 } } +local n3 = { m = { 1, 2 }, n = { 1, 2, 3 } } +tap.same(n1, n2, 'ok - same({m = {1,2}, n = {1,2}}, {m = {1,2}, n = {1,2}})') +tap.same(n1, n3, 'not ok - same({m = {1,2}, n = {1,2}}, {m = {1,2}, n = {1,2,3}})') +tap.same(n3, n1, 'not ok - same({m = {1,2}, n = {1,2,3}}, {m = {1,2}, n = {1,2}})') + +local method_table1 = { p = print, a = assert } +local method_table2 = { p = print, a = assert } +local method_table3 = { p = print, a = assert, e = error } +tap.same(method_table1, method_table2, + 'ok - same({p = print, a = assert}, {p = print, a = assert})') +tap.same(method_table1, method_table3, + 'not ok - same({p = print, a = assert}, {p = print, a = assert, e = error})') + +local foo = {4, s = 4} +local bar = {6, s = 8} +local oof = {4, s = 3} +local mt1 = {} +local mt2 = {} +local evens = function (x, y) return x['s'] % 2 == 0 and y['s'] % 2 == 0 end +local even_odd = function (x, y) return x['s'] % 2 == 0 and y['s'] % 2 ~= 0 end +mt1.__eq = evens +mt2.__eq = even_odd +setmetatable(foo, mt1) +setmetatable(bar, mt1) +setmetatable(oof, mt1) +tap.same(foo, bar, 'ok - same({4, s=4}, {6, s=4},__eq => x[s] and y[s] are even)') +tap.same(bar, foo, 'ok - same({6, s=8}, {4, s=4},__eq => x[s] and y[s] are even)') +tap.same(foo, oof, + 'not ok - same({4, s=4}, {4, s=3},__eq => x[s] and y[s] are even)') +tap.same(oof, foo, + 'not ok - same({4, s=4}, {4, s=3},__eq => x[s] and y[s] are even)') +setmetatable(foo, mt2) +setmetatable(bar, mt2) +setmetatable(oof, mt2) +tap.same(foo, bar, + 'not ok - same({4, s=4}, {6, s=4},__eq => x[s] is even, y[s] odd)') +tap.same(foo, oof, + 'ok - same({4, s=4}, {4, s=3},__eq => x[s] is even, y[s] odd)') +tap.same(oof, foo, + 'not ok - same({4, s=3}, {4, s=4},__eq => x[s] is even, y[s] odd)') +tap.done() diff --git a/tests/config/tapered/test/setup-teardown-result.txt b/tests/config/tapered/test/setup-teardown-result.txt new file mode 100644 index 0000000..c5d5668 --- /dev/null +++ b/tests/config/tapered/test/setup-teardown-result.txt @@ -0,0 +1,4 @@ +# I'm a little teapot. +ok 1 - Short and stout. +# Here is my handle, and here is my spout. +1..1 diff --git a/tests/config/tapered/test/setup-teardown-test.lua b/tests/config/tapered/test/setup-teardown-test.lua new file mode 100755 index 0000000..974a4aa --- /dev/null +++ b/tests/config/tapered/test/setup-teardown-test.lua @@ -0,0 +1,14 @@ +package.path = '../src/?.lua;' .. package.path +local tap = require 'tapered' + +-- luacheck: globals setup teardown +function setup() + print("# I'm a little teapot.") +end + +function teardown() + print("# Here is my handle, and here is my spout.") +end + +tap.ok(true, "Short and stout.") +tap.done() diff --git a/tests/config/test.cfg b/tests/config/test.cfg new file mode 100644 index 0000000..b45226c --- /dev/null +++ b/tests/config/test.cfg @@ -0,0 +1,46 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- disable output buffering to crashes do not leave us without log +io.stdout:setvbuf('no') +io.stderr:setvbuf('no') + +-- modify path to be able to load testing modules +package.path = package.path .. ';' .. env.SOURCE_PATH .. '/?.lua' + +-- export testing module in globals +local tapered = require('tapered.src.tapered') +for k, v in pairs(tapered) do + _G[k] = v +end + +-- don't send priming queries etc. +modules.unload 'detect_time_skew' +modules.unload 'priming' +modules.unload 'ta_signal_query' +modules.unload 'ta_update' + +-- load test +local ffi = require('ffi') +assert(type(env.TEST_FILE) == 'string') +log_info(ffi.C.LOG_GRP_TESTS, 'processing test file %s', env.TEST_FILE) +local tests = dofile(env.TEST_FILE) + +-- run test after processed config file +-- default config will be used and we can test it. +assert(type(tests) == 'table', + string.format('file %s did not return a table of test' + .. ' functions, did you forget return?', + env.TEST_FILE)) + +local runtest = require('test_utils').test +worker.coroutine(function () + local at_least_one_test = false + for idx, t in ipairs(tests) do + assert(type(t) == 'function', + string.format('test table idx %d in file %s' + .. ' is not a function', idx, env.TEST_FILE)) + at_least_one_test = true + runtest(t) + end + assert(at_least_one_test, 'test set is empty?! a typo in function name?') + done() +end) diff --git a/tests/config/test_dns_generators.lua b/tests/config/test_dns_generators.lua new file mode 100644 index 0000000..4a7cc0a --- /dev/null +++ b/tests/config/test_dns_generators.lua @@ -0,0 +1,134 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local ffi = require('ffi') +local kr_cach = kres.context().cache + + +local charset = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'} + +local function gen_bytes(len) + local bytes = {} + for _ = 1,len do + table.insert(bytes, charset[math.random(1, #charset)]) + end + local result = table.concat(bytes) + assert(#result == len) + return result +end + +local function gen_ttl() + return math.random(0, 2^31-1) +end + +local function gen_rrtype() + return math.random(1024, 61000) +end + +local function gen_rdata(len) + assert(len >= 1 and len <= 65535) + return gen_bytes(len) +end + +local function gen_label(len) -- in bytes including the length byte + assert(len >= 2 and len <= 64) + local bytes = {string.char(len - 1), gen_bytes(len - 1)} + return table.concat(bytes) +end + +local function gen_dname() + local target_len -- length 2 bytes does not make sense + while target_len == nil or target_len == 2 do + target_len = math.random(1, 255) + end + + local labels = {string.char(0)} + local cur_len = 1 + while target_len > cur_len do + local new_len = math.random( + 2, + math.min(target_len - cur_len, + 64)) + if (target_len - cur_len - new_len) == 1 then + -- it is a trap, single-byte label is allowed only at the end + -- we cannot leave room for single-byte label in the next round + if new_len == 64 then + goto continue -- we are at max label length, try again + end + new_len = new_len + 1 + end + table.insert(labels, 1, gen_label(new_len)) + cur_len = cur_len + new_len + ::continue:: + end + assert(target_len == cur_len) + local dname = table.concat(labels) + assert(#dname >= 1 and #dname <= 255) + assert(string.byte(dname, #dname) == 0) + return dname +end + + +local function gen_rrset() + local rrs = {} + local maxsize = 300 -- RR data size in bytes per RR set, does not include owner etc. + local target_len = math.random(1, maxsize) + local cur_len = 0 + while target_len > cur_len do + local new_len = math.random(1, target_len - cur_len) + local new_rr = gen_rdata(new_len) + cur_len = cur_len + #new_rr + table.insert(rrs, new_rr) + end + assert(target_len == cur_len) + return rrs, cur_len +end + + +local function add_random_rrset() + local owner = gen_dname() + local ttl = gen_ttl() + local rr_type = gen_rrtype() + local rdata_set = gen_rrset() + + local kr_rrset = kres.rrset(owner, rr_type, kres.class.IN, ttl) + for _, rr in ipairs(rdata_set) do + assert(kr_rrset:add_rdata(rr, #rr)) + end + assert(kr_cach:insert(kr_rrset, nil, ffi.C.KR_RANK_SECURE)) +end + +ffi.cdef('int usleep(uint32_t usec);') -- at least in current glibc it's always 32-bit + +local rr_count = 0 +local function gen_batch() + for _ = 1,math.random(1,10) do + add_random_rrset() + rr_count = rr_count + 1 + if rr_count % 100 == 0 then + print('cache usage ', cache.stats()['usage_percent'], '%') + end + end + kr_cach:commit() + ffi.C.usleep(15) -- stop *whole process* to give better chance to GC executing + local delay + if math.random(1,4) == 1 then + delay = 1 -- give a chance to DNS resolving + else + delay = 0 + end + event.after(delay, gen_batch) +end + +return { + add_random_rrset=add_random_rrset, + gen_batch=gen_batch, + gen_bytes=gen_bytes, + gen_dname=gen_dname, + gen_label=gen_label, + gen_rdata=gen_rdata, + gen_rrset=gen_rrset, + gen_rrtype=gen_rrtype, + gen_ttl=gen_ttl +} diff --git a/tests/config/test_utils.lua b/tests/config/test_utils.lua new file mode 100644 index 0000000..4389293 --- /dev/null +++ b/tests/config/test_utils.lua @@ -0,0 +1,121 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local M = {} + +function M.test(f, ...) + local res, exception = xpcall(f, debug.traceback, ...) + if not res then + io.stderr:write(string.format('%s\n', exception)) + os.exit(2) + end + return res +end + +function M.table_keys_to_lower(table) + local res = {} + for k, v in pairs(table) do + res[k:lower()] = v + end + return res +end + +local function contains(pass, fail, table, value, message) + message = message or string.format('table contains "%s"', value) + for _, v in pairs(table) do + if v == value then + pass(message) + return + end + end + fail(message) + return +end + +function M.contains(table, value, message) + return contains(pass, fail, table, value, message) +end + +function M.not_contains(table, value, message) + return contains(fail, pass, table, value, message) +end + +local function answer2table(pkt) + local got_answers = {} + local ans_rrs = pkt:rrsets(kres.section.ANSWER) + for i = 1, #ans_rrs do + rrs = ans_rrs[i] + for rri = 0, rrs:rdcount() - 1 do + local rr = ans_rrs[i]:txt_fields(rri) + got_answers[rr.owner] = got_answers[rr.owner] or {} + got_answers[rr.owner][rr.type] = got_answers[rr.owner][rr.type] or {} + table.insert(got_answers[rr.owner][rr.type], rr.rdata) + table.sort(got_answers[rr.owner][rr.type]) + end + end + return got_answers +end + +M.NODATA = -1 +-- Resolve a name and check the answer. Do *not* return until finished. +-- expected_rdata is one string or a table of strings in presentation format +function M.check_answer(desc, qname, qtype, expected_rcode, expected_rdata) + assert(type(qtype) == 'number') + local qtype_str = kres.tostring.type[qtype] + qname = string.lower(qname) + + local expected_answer = {} + if expected_rdata ~= nil then + if type(expected_rdata) ~= 'table' then + expected_rdata = { expected_rdata } + end + if #expected_rdata > 0 then + table.sort(expected_rdata) + expected_answer = { + [qname] = { + [qtype_str] = + expected_rdata + } + } + end + end + + local wire_rcode = expected_rcode + if expected_rcode == kres.rcode.NOERROR and type(expected_rdata) == 'table' + and #expected_rdata == 0 then + expected_rcode = M.NODATA + end + if expected_rcode == M.NODATA then wire_rcode = kres.rcode.NOERROR end + + local done = false + local callback = function(pkt) + ok(pkt, 'answer not dropped') + same(pkt:rcode(), wire_rcode, + desc .. ': expecting answer for query ' .. qname .. ' ' .. qtype_str + .. ' with rcode ' .. kres.tostring.rcode[wire_rcode]) + + ok((pkt:ancount() > 0) == (expected_rcode == kres.rcode.NOERROR), + desc ..': checking number of answers for ' .. qname .. ' ' .. qtype_str) + + if expected_rdata then + same(expected_answer, answer2table(pkt), 'ANSWER section matches') + end + done = true + end + resolve(qname, qtype, kres.class.IN, {}, + function(...) + local ok, err = xpcall(callback, debug.traceback, ...) + if not ok then + fail('error in check_answer callback function') + io.stderr:write(string.format('%s\n', err)) + os.exit(2) + end + end + ) + + for delay = 0.1, 5, 0.5 do -- total max 23.5s in 9 steps + if done then return end + worker.sleep(delay) + end + if not done then fail('check_answer() timed out') end +end + +return M diff --git a/tests/config/tls.test.lua b/tests/config/tls.test.lua new file mode 100644 index 0000000..ef811bb --- /dev/null +++ b/tests/config/tls.test.lua @@ -0,0 +1,29 @@ +local ffi = require('ffi') +ffi.cdef([[ const char * gnutls_check_version (const char * req_version); ]]) + +-- SPDX-License-Identifier: GPL-3.0-or-later +local function test_session_config() + ok(net.tls_sticket_secret(), + 'net.tls_sticket_secret() to trigger key regeneration') + if ffi.C.gnutls_check_version("3.6.3") ~= nil then + ok(net.tls_sticket_secret('0123456789ABCDEF0123456789ABCDEF'), + 'net.tls_sticket_secret with valid key') + end + boom(net.tls_sticket_secret, {{}}, + 'net.tls_sticket_secret({}) is invalid') + boom(net.tls_sticket_secret, {'0123456789ABCDEF0123456789ABCDE'}, + 'net.tls_sticket_secret with too short key') + + boom(net.tls_sticket_secret_file, {}, + 'net.tls_sticket_secret_file without filename') + boom(net.tls_sticket_secret_file, {{}}, + 'net.tls_sticket_secret_file with non-string filename') + boom(net.tls_sticket_secret_file, {'/tmp/a_non_existent_file_REALLY_1528898130'}, + 'net.tls_sticket_secret_file with non-existent filename') + boom(net.tls_sticket_secret_file, {'/dev/null'}, + 'net.tls_sticket_secret_file with empty file') +end + +return { + test_session_config +} diff --git a/tests/config/worker.test.lua b/tests/config/worker.test.lua new file mode 100644 index 0000000..756bb5f --- /dev/null +++ b/tests/config/worker.test.lua @@ -0,0 +1,65 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- check prerequisites +if not worker.bg_worker then + -- skipping worker test because it doesnt support background worker + os.exit(77) +else + -- import primitives for synchronisation + local monotime = require('cqueues').monotime + + -- test whether sleeping works + local function test_worker_sleep() + local now = monotime() + ok(pcall(worker.sleep, 0.1), 'sleep works') + local elapsed = monotime() - now + ok(elapsed > 0, 'sleep takes non-zero time') + end + + -- helper to track number of executions + local cv = require('cqueues.condition').new() + local tasks = 0 + local function work () + worker.sleep(0.1) + tasks = tasks - 1 + if tasks == 0 then + cv:signal() + elseif tasks < 0 then + error('too many executions') + end + end + + -- test whether coroutines work + local function test_worker_coroutine() + tasks = 2 + worker.coroutine(work) + worker.coroutine(work) + -- Check if coroutines finish + local status = cv:wait(1) + same(tasks, 0, 'all coroutines finish') + ok(status, 'coroutines finish successfully') + -- Check if nesting coroutines works + local now = monotime() + tasks = 100 + worker.coroutine(function () + for _ = 1, 100 do + worker.coroutine(work) + end + end) + status = cv:wait(1) + local elapsed = monotime() - now + same(tasks, 0, 'all nested coroutines finish') + ok(status, 'nested coroutines finish successfully') + -- Test if 100 coroutines didnt execute synchronously + -- (the wait time would be 100 * 0.1 = 10s sleep time) + -- Concurrent sleep time should still be ~0.1s (added some safe margin) + ok(elapsed < 0.5, 'coroutines didnt block while sleeping') + end + + -- plan tests + local tests = { + test_worker_sleep, + test_worker_coroutine + } + + return tests +end |