1
0

API have been very heavily modified.

This commit is contained in:
Jon Michael Aanes 2017-06-27 13:50:27 +02:00
parent 12adee0398
commit fe65aeed90
5 changed files with 176 additions and 112 deletions

View File

@ -1,7 +1,9 @@
-- Check for possible loading errors
local string_dist
do
local thispath = ... and select('1', ...):match('.+%.') or ''
local thispath = ... and (...):match('.+%.') or ''
local function import (name, ignore_failure)
local was_loaded, lib_or_error = pcall(require, thispath..name)
if not was_loaded then
@ -14,16 +16,26 @@ do
string_dist = import 'string_distance'
end
if not debug then
error('[errors/loading]: Expected the debug library to be available in _G.')
elseif not debug.getinfo then
error('[errors/loading]: Expected debug.getinfo to be available in the debug library.')
end
assert(debug and debug.getinfo)
--------------------------------------------------------------------------------
-- Constants
local DEFAULT_ERROR_MSG_NORMAL = 'Unknown error occured'
local DEFAULT_ERROR_MSG_INTERNAL = 'Unknown internal error occured'
local DEFAULT_ERROR_MSG_CORRECT = 'Unexpected input "%s", maybe you meant %s?'
--------------------------------------------------------------------------------
-- Util
local function format_probable_strings (probable_strings, amount)
assert(type(probable_strings) == 'table')
assert(type(amount) == 'number')
assert(#probable_strings > 0)
assert(type(amount) == 'number')
local l = {}
for i = 1, math.min(amount, #probable_strings) do
@ -36,70 +48,96 @@ local function format_probable_strings (probable_strings, amount)
end
--------------------------------------------------------------------------------
-- Error handler
local ErrorHandler = setmetatable({}, {__call = function (c, ...) return c.new(...) end})
ErrorHandler.__index = ErrorHandler
local function internal_error (self, module_suffix, format_msg, ...)
-- internal_error(error_handler, module_suffix [, format_msg, ...])
function ErrorHandler.new (module_name)
return setmetatable({module_name = module_name, registered = {}}, ErrorHandler)
-- Error check
assert(type(self) == 'table' and self.is_error_handler)
assert(type(module_suffix) == 'string')
format_msg = format_msg or DEFAULT_ERROR_MSG_NORMAL
assert(type(format_msg) == 'string')
-- Format
return error(('[%s%s]: '..format_msg):format(self.module_name, module_suffix, ...), 3)
end
function ErrorHandler:register (f)
assert(type(self) == 'table')
assert(type(f) == 'function')
self.registered[f] = true
local function external_error (self, module_suffix, format_msg, ...)
-- external_error(error_handler, module_suffix [, format_msg, ...])
-- Error check
assert(type(self) == 'table' and self.is_error_handler)
assert(type(module_suffix) == 'string')
format_msg = format_msg or DEFAULT_ERROR_MSG_INTERNAL
assert(type(format_msg) == 'string')
-- Find error level
local level = 3
while self.registered[debug.getinfo(level, 'f').func] do
level = level + 1
end
-- Format
return error(('[%s%s]: '..format_msg):format(self.module_name, module_suffix, ...), level -1)
end
local ERROR_MODES = { ['internal'] = true, ['external'] = true }
local function correct_error (self, module_suffix, format_msg, gotten_string, possible_strings)
-- correct_error(error_handler, module_suffix [, format_msg], gotten_string, possible_strings)
function ErrorHandler:getErrorFunc (mode, name)
-- Error check
assert(type(self) == 'table' and self.is_error_handler)
assert(type(module_suffix) == 'string')
format_msg = format_msg or DEFAULT_ERROR_MSG_CORRECT
assert(type(format_msg) == 'string')
assert(type(gotten_string) == 'string')
assert(type(possible_strings) == 'table')
-- Parameter fixing
local mode = mode or 'internal'
local name = name or (mode == 'external' and '') or mode
if name ~= '' then name = '/'..name end
-- Do stuff
local possible_strings = string_dist.strings_with_highest_similarity(gotten_string, possible_strings)
local list_string = format_probable_strings(possible_strings, 3)
-- Format
return error(('[%s%s]: '..format_msg):format(self.module_name, module_suffix, gotten_string, list_string), 3)
end
-- Error checking
assert(ERROR_MODES[mode])
assert(type(name) == 'string')
-- Create error funcs.
if mode == 'internal' then
return function (format_msg, ...)
assert(type(format_msg) == 'string')
return error(('[%s%s]: '..format_msg):format(self.module_name, name, ...), 2)
end
elseif mode == 'external' then
return function (format_msg, ...)
assert(type(format_msg) == 'string')
local level = 2
while self.registered[debug.getinfo(level, 'f').func] do
level = level + 1
end
return error(('[%s%s]: '..format_msg):format(self.module_name, name, ...), level)
local ErrorHandler_mt = {__call = function (self, ...) internal_error(self, '', ...) end}
return function (module_name)
assert(type(module_name) == 'string')
local err_hdl = setmetatable({}, ErrorHandler_mt)
err_hdl.is_error_handler = true
err_hdl.module_name = module_name
err_hdl.registered = {}
err_hdl.any_registered = false
function err_hdl.internal (...)
local args = {...}
if args[1] == err_hdl then table.remove(args, 1) end
internal_error(err_hdl, '/internal', unpack(args))
end
function err_hdl.external (...)
local args = {...}
if args[1] == err_hdl then table.remove(args, 1) end
external_error(err_hdl, '', unpack(args))
end
function err_hdl.correct (...)
local args = {...}
if args[1] == err_hdl then table.remove(args, 1) end
if #args == 2 then table.insert(args, 1, DEFAULT_ERROR_MSG_CORRECT) end
correct_error(err_hdl, '', unpack(args))
end
function err_hdl.register (...)
local args = {...}
if args[1] == err_hdl then table.remove(args, 1) end
assert(#args > 0)
for i = 1, #args do
assert(type(args[i]) == 'function')
err_hdl.registered[args[i]] = true
err_hdl.any_registered = true
end
end
assert(false)
return err_hdl
end
function ErrorHandler:attemptCorrection (format_msg, gotten_string, probable_strings)
assert(type(self) == 'table', 'Wow, mister, remember to call with OO notation.')
assert(type(format_msg) == 'string')
assert(type(gotten_string) == 'string')
assert(type(probable_strings) == 'table')
local probable_strings = string_dist.strings_with_highest_similarity(gotten_string, probable_strings)
local list_string = format_probable_strings(probable_strings, 3)
return self:getErrorFunc('internal', '')(format_msg, gotten_string, list_string)
end
function ErrorHandler:__call (format_msg, ...)
return self:getErrorFunc('internal')(format_msg, ...)
end
--------------------------------------------------------------------------------
return {
ErrorHandler = ErrorHandler
}

View File

@ -1,2 +1,2 @@
return require 'errors.errors'
return require (((...) ~= 'init' and (...) .. '.' or '') .. 'errors')

View File

@ -1,69 +1,95 @@
local error_handler = require('errors').ErrorHandler 'errors_test'
local SUITE = require('TestSuite').new('errors')
local SUITE = require 'TestSuite' 'errors'
SUITE:setEnviroment {
error_handler = error_handler,
external_error = error_handler:getErrorFunc 'external',
internal_error = error_handler:getErrorFunc 'internal',
error = require 'errors' 'test_errors'
}
--------------------------------------------------------------------------------
-- Basic errors functionallity
-- Basic errors functions
SUITE:addTest('errors basic functionallity', function()
local func_a = function () external_error 'Hello World' end
local func_b, func_b_line = function () func_a() end, curline()
error_handler:register(func_a)
local status, error_msg = pcall(func_b)
assert_equal(false, status)
local expected_error = './test/test_errors.lua:'..func_b_line..': [errors_test]: Hello World'
assert_equal(expected_error, error_msg)
SUITE:addTest('Basic error', function()
local expected_error = './test/test_errors.lua:'..curline(1)..': [test_errors]: Hello World'
assert_equal(expected_error, select(2, pcall(error, 'Hello World')))
end)
SUITE:addTest('errors deep nesting works', function()
local func_a = function () external_error 'Hello World' end
local func_b, func_b_line = function () func_a() end, curline()
local func_c, func_c_line = function () func_b() end, curline()
error_handler:register(func_a)
error_handler:register(func_b)
local status, error_msg = pcall(func_c)
assert_equal(false, status)
local expected_error = './test/test_errors.lua:'..func_c_line..': [errors_test]: Hello World'
assert_equal(expected_error, error_msg)
SUITE:addTest('Basic formatting', function()
local expected_error = './test/test_errors.lua:'..curline(1)..': [test_errors]: Welcome to the Errors'
assert_equal(expected_error, select(2, pcall(error, '%s to the %s', 'Welcome', 'Errors')))
end)
SUITE:addTest('internal errors also works', function()
local func_a, func_a_line = function () internal_error 'Hello World' end, curline()
local func_b, func_b_line = function () func_a() end, curline()
local func_c, func_c_line = function () func_b() end, curline()
error_handler:register(func_a)
error_handler:register(func_b)
local status, error_msg = pcall(func_c)
assert_equal(false, status)
local expected_error = './test/test_errors.lua:'..func_a_line..': [errors_test/internal]: Hello World'
assert_equal(expected_error, error_msg)
SUITE:addTest('Default generic error', function()
local expected_error = './test/test_errors.lua:'..curline(1)..': [test_errors]: Unknown error occured'
assert_equal(expected_error, select(2, pcall(error)))
end)
SUITE:addTest('errors modes can be overwritten', function()
local extra_error = error_handler:getErrorFunc('external', 'extra')
local func, func_line = function () extra_error 'Hi' end, curline()
local status, error_msg = pcall(func)
local expected_error = './test/test_errors.lua:'..func_line..': [errors_test/extra]: Hi'
assert_equal(expected_error, error_msg)
SUITE:addTest('Internal is almost the same as error in itself', function()
local expected_error = './test/test_errors.lua:'..curline(1)..': [test_errors/internal]: World Hello'
assert_equal(expected_error, select(2, pcall(error.internal, 'World Hello')))
end)
SUITE:addTest('errors modes can be overwritten, including internal', function()
local extra_error = error_handler:getErrorFunc('internal', 'extra')
local func, func_line = function () extra_error 'Hi' end, curline()
SUITE:addTest('Internal can also be called using object index', function()
local expected_error = './test/test_errors.lua:'..curline(1)..': [test_errors/internal]: André the Giant'
assert_equal(expected_error, select(2, pcall(error.internal, error, 'André the Giant')))
end)
local status, error_msg = pcall(func)
local expected_error = './test/test_errors.lua:'..func_line..': [errors_test/extra]: Hi'
assert_equal(expected_error, error_msg)
SUITE:addTest('Internal includes the stack', function()
local func_a = function() error.internal 'Thomas the Steam Train' end
local expected_error = './test/test_errors.lua:'..curline(-1)..': [test_errors/internal]: Thomas the Steam Train'
assert_equal(expected_error, select(2, pcall(func_a)))
end)
SUITE:addTest('Internal includes the stack 2', function()
local func_a = function() error.internal 'Thomas the Steam Train' end
local func_b = function() return pcall(func_a) end
local expected_error = './test/test_errors.lua:'..curline(-2)..': [test_errors/internal]: Thomas the Steam Train'
assert_equal(expected_error, select(2, pcall(func_a)))
end)
--------------------------------------------------------------------------------
-- External
SUITE:addTest('External errors hides the stack', function()
local func_a = function () error.external 'Hello World' end
local func_b = function () func_a() end
error.register(func_a)
local expected_error = './test/test_errors.lua:'..curline(-3)..': [test_errors]: Hello World'
assert_equal(expected_error, select(2, pcall(func_b)))
end)
SUITE:addTest('External errors can be triggered deep', function()
local func_a = function () error.external 'Hello World' end
local func_b = function () func_a() end
local func_c = function () func_b() end
error.register(func_a, func_b)
local expected_error = './test/test_errors.lua:'..curline(-3)..': [test_errors]: Hello World'
assert_equal(expected_error, select(2, pcall(func_c)))
end)
SUITE:addTest('Internal errors don\'t hide the stack', function()
local func_a = function () error.internal 'Hello World' end
local func_b = function () func_a() end
local func_c = function () func_b() end
error.register(func_a, func_b)
local expected_error = './test/test_errors.lua:'..curline(-5)..': [test_errors/internal]: Hello World'
assert_equal(expected_error, select(2, pcall(func_c)))
end)
--------------------------------------------------------------------------------
-- Correcting errors
SUITE:addTest('Correct errors helps with autocorrection', function()
local _, err_msg = pcall(error.correct, 'Not %s, maybe: %s', 'help', {'Help', 'whelp', 'halp'})
local expected_error = './test/test_errors.lua:'..curline(-1)..': [test_errors]: Not help, maybe: Help, whelp or halp'
assert_equal(expected_error, err_msg)
end)
SUITE:addTest('Correct errors has nice default string', function()
local _, err_msg = pcall(error.correct, 'Vlard', {'Vlad', 'Question Mark', 'Frankenstein'})
local expected_error = './test/test_errors.lua:'..curline(-1)..': [test_errors]: Unexpected input "Vlard", maybe you meant Vlad, Question Mark or Frankenstein?'
assert_equal(expected_error, err_msg)
end)
--------------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
local SUITE = require('TestSuite').new('string_distance')
local SUITE = require 'TestSuite' 'string_distance'
SUITE:setEnviroment {
levenshtein = require('string_distance').levenshtein,
longest_common_subsequence = require('string_distance').longest_common_subsequence,

View File

@ -1,7 +1,7 @@
package.path = package.path .. ';./test/?.lua;./src/?.lua'
local TEST_SUITE = require("TestSuite").new('errors')
TEST_SUITE:addModules('test/test_*')
local TEST_SUITE = require 'TestSuite' 'errors'
TEST_SUITE:addModules 'test/test_*'
TEST_SUITE:setOptions(...)
TEST_SUITE:runTests()