Module:Format ISBN/data/testcases

-- Unit tests for Module:Format ISBN/data. Click talk page to run tests. -- Example usage: --  -- Console example: --  mw.log(p.run_tests(mw.getCurrentFrame))

local p = require('Module:UnitTests') local _data = nil

local function _load_data if not _data then _data = mw.loadData('Module:Format ISBN/data') end end

function p:_catch(function_) local status, error_ = pcall(function _load_data; function_ end) if not status then self:equals(error_, "aborted", "(no error)") end return status end

local function _table_length(table_) local count = 0 for _ in ipairs(table_) do		count = count + 1 end return count end

local _checks, _check_failures = 0, 0

function p:_check_equals(name, actual, expected) _checks = _checks + 1 if actual ~= expected then self:equals(name, actual, expected) _check_failures = _check_failures + 1 end end

function p:test_main_registration_groups_exist self:_catch(function		self:equals("type(_data[\"978-0\"])", type(_data["978-0"]), "table")		self:equals("type(_data[\"978-1\"])", type(_data["978-1"]), "table")		self:equals("type(_data[\"979-8\"])", type(_data["979-8"]), "table")	end) end

function p:_test_range_shape(locus, digits, range) self:_check_equals(locus .. " type(range)", type(range), "table") self:_check_equals(locus .. " #range == 2", _table_length(range), 2)

self:_check_equals(locus .. " type(range[1])", type(range[1]), "number") self:_check_equals(locus .. " type(range[2])", type(range[2]), "number")

local max_ = tonumber(string.sub("999999999", 1, 8 - digits)) self:_check_equals(locus .. " range[1] <= " .. max_, range[1] <= max_, true) self:_check_equals(locus .. " range[2] <= " .. max_, range[2] <= max_, true) self:_check_equals(locus .. " range[2] >= range[1]", range[2] >= range[1], true) end

function p:_test_entry_shape(locus, entry) self:_check_equals(locus .. " type(entry)", type(entry), "table") self:_check_equals(locus .. " #entry > 0", _table_length(entry) > 0, true)

self:_check_equals(locus .. " type(entry[1])", type(entry[1]), "number") self:_check_equals(locus .. " entry[1] >= 1", entry[1] >= 1, true) self:_check_equals(locus .. " entry[1] <= 7", entry[1] <= 7, true)

for j, range in ipairs(entry) do		if j > 1 then self:_test_range_shape(locus .. "[" .. j .. "]", entry[1], range) end end end

function p:_test_table_shape(prefix) local locus = "_data[\"" .. prefix .. "\"]" local pattern = "^97[89][-]%d+$" self:_check_equals(locus .. " type(prefix)", type(prefix), "string") self:_check_equals(locus .. " pattern(prefix)", prefix:match(pattern), prefix) self:_check_equals(locus .. " type(_data[prefix])", type(_data[prefix]), "table")

for i, entry in ipairs(_data[prefix]) do self:_test_entry_shape(locus .. "[" .. i .. "]", entry) end end

function p:__test_table_shape(prefix) _checks, _check_failures = 0, 0 if not self:_catch(function self:_test_table_shape(prefix) end) then return "aborted" end return _check_failures == 0 and "valid" or "invalid" end

function p:test_all_tables_for_shape_validity self:_catch(function		self:_check_equals("type(_data)", type(_data), "table")

for prefix in pairs(_data) do			local validity = self:__test_table_shape(prefix) local name = "_data[\"" .. prefix .. "\"]" .. ", " .. _checks .. " attributes validated" .. ", " .. _check_failures .. " failed" self:equals(name, validity, "valid") end end) end

function _build_allocations_table(data) local allocations = {} for prefix, ranges in pairs(data) do		local prefix_ = prefix:gsub("[-]", "") local exponent = 12 - prefix_:len local basis = tonumber(prefix_) * 10 ^ exponent

for i, entry in ipairs(ranges) do			local digits = entry[1] local power = 10 ^ (exponent - (8 - digits)) for j, range in ipairs(entry) do				if j > 1 then local start = basis + range[1] * power local finish = basis + range[2] * power + power - 1 local locus = "_data[\"" .. prefix .. "\"][" .. i .. "][" .. j .."]" table.insert(allocations, {start, finish, locus}) end end end end table.sort(allocations, function(a, b) return a[1] < b[1] end) return allocations end

function p:_test_for_range_overlaps(allocations) _checks, _check_failures = 0, 0 for i, range in ipairs(allocations) do		if i > 1 then local preceding_range = allocations[i - 1] local is_disjoint = preceding_range[2] < range[1] local name = preceding_range[3] .. " < " .. range[3] .. " (" .. preceding_range[2] .. " < " .. range[1] .. ")" self:_check_equals(name, is_disjoint, true) end end return _check_failures == 0 and "valid" or "invalid" end

function p:test_for_range_overlaps self:_catch(function		local allocations = _build_allocations_table(_data)		local validity = self:_test_for_range_overlaps(allocations)

local name = "" .. _checks .. " range overlaps checked" .. ", " .. _check_failures .. " failed" self:equals(name, validity, "valid") end) end

return p