Code
--[[
Safe Fun
"Safe" wrappers for functions which may crash your recipe.
Many of the foldit LUA functions terminate the recipe
if called with an invalid parameter.
For example, selection.Select terminates the recipe if the segment
specified is outside of the valid range of segments.
The wrapper functions are prefixed with "Safe" and added to the same
namespace (table) as the original problem function. For example,
selection.SafeSelect can be used in place of selection.Select.
The wrapper functions catch the error using the LUA pcall
function, and return an error code and an error message
if the underlying function failed.
A return code of zero indicate the underlying function was
successful. The return values from the underlying function are
returned after the return code. For example, structure.IsHydrophobic
returns a boolean, so structure.SafeIsHydrophobic returns an number
and a boolean:
local rc
local isPhobic
rc, isPhobic = structure.SafeIsHydrophobic ( segIdx )
if rc ~= 0 then
print ( "IsHydrophobic ( " .. segidx .. " ), rc = " .. rc ..
", errmsg = \"" .. errmsg .. "\"" )
else
if isPhobic then
...
end
end
A negative return code indicates the underlying function
encountered an error. A return code of -1 is commonly used
to indicate a bad segment index.
Large negative return codes indicate a general error:
* -997 = argument type error
* -998 = argument count error
* -999 = other unspecifed error
For example, the call
rc, errmsg = behavior.SafeSetClashImportance ( "very" )
would return rc = -997, and errmsg = "(number expected, got string)".
On the other hand, with a otherwise valid numeric argument, the call
rc, errmsg = behavior.SafeSetClashImportance ( 19 )
would return rc = -1, and errmsg = "(clashing importance value outside
valid range - between 0 and 1))".
Calling scripts should at least check for a zero return code,
and possibly log information for debugging purposes. In most cases,
scripts should be able to continue, perhaps after skipping the
current work item.
The most common errors seem to be a bad segment number,
bad band parameters, and a bad rotamer number.
This recipe consists of a "safe functions" section and a test routine.
The safe functions can be copied into other recipes, in whole or in part.
The safefun table and the CommonError function are required for the other
functions to work.
The test routine is table-driven, and tests each safe function. The test table
includes the expected return code for each test. An error message is printed if
the actual return code does not match the expected return code.
The test cases generally cover specific error cases and one or more successful
rc = 0 cases. In particular, one band.SafeAddBetweenSegments case should be
successful. The band from this test case, which should have band index 1,
is then used in later tests.
version history
1.1 LociOiling 2020/02/23
+ added dialog, break down tests by group
1.2 LociOiling 2020/02/24
+ added "test all" option
+ more tests and functions:
* band.SafeAddBetweenSegments - test added for new symmetric chain option
* structure.InsertResidue and structure.DeleteResidue added
1.3 LociOiling 2020/02/25
+ minor cosmetic fixes
+ add parameter type error as a test type
+ rework error checking, use CommonError instead of ParseError
* new return code -997 for argument type error
* new return code -998 for argument count error
]]--
Recipe = "Safe Fun"
Version = "1.3"
ReVersion = Recipe .. " " .. Version
--
-- options
--
local opts = {}
opts.runall = true -- test all groups if true
opts.ttpasses = true -- run all tests which are expected to pass
opts.ttsegrng = false -- run all tests for segment range errors
opts.ttpvalue = false -- run all tests for parameter value errors
opts.ttargcnt = true -- run all tests for argument count errors
opts.ttargtyp = true -- run all tests for argument type errors
--
-- safe functions v 1.0
--
-- safe functions use pcall to invoke functions which
-- may unexpectedly terminate the script
--
-- safe functions are added to the existing Foldit psuedo-classes
--
--
-- common section used by all safe functions
--
safefun = {}
--
-- CommonError -- common routine used by safe functions,
-- checks for common errors
--
-- checks for errors like bad segment and bad band index
-- even for functions where they don't apply -- efficiency
-- not a key concern here
--
-- any error that appears more than once gets tested here
--
-- first return codes may not be unique
--
safefun.CommonError = function ( errmsg )
local BADSEG = "segment index out of bounds"
local ARGCNT = "Expected %d+ arguments."
local BADARG = "bad argument #%d+ to '%?' (%b())"
local EXPECT = "expected, got"
local BADATOM = "atom number out of bounds"
local BADBAND = "band index out of bounds"
local BADSYMM = "symmetry index out of bounds"
local BADACID = "invalid argument, unknown aa code"
local errp, errq = errmsg:find ( BADSEG )
if errp ~= nil then
return -1, errmsg
end
--
-- "bad argument" messages include argument type errors
-- and some types of argument value errors
-- trap only the argument type errors here
--
local errp, errq, errd = errmsg:find ( BADARG )
if errp ~= nil then
local errp2 = errd:find ( EXPECT )
if errp2 ~= nil then
return -997, errmsg -- argument type error
end
end
local errp, errq = errmsg:find ( ARGCNT )
if errp ~= nil then
return -998, errmsg
end
local errp, errq = errmsg:find ( BADATOM )
if errp ~= nil then
return -2, errmsg
end
local errp, errq = errmsg:find ( BADBAND )
if errp ~= nil then
return -3, errmsg
end
local errp, errq = errmsg:find ( BADACID )
if errp ~= nil then
return -2, errmsg
end
return 0, errmsg
end
--
-- end of common section used by all safe functions
--
--
-- band.SafeAdd uses pcall
-- to call band.Add, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the band index of the added band (number).
-- -1 - bad segment number
-- -2 - reserved
-- -3 - reserved
-- -4 - bad rho
-- -5 - bad theta
-- -6 - bad phi
-- -99x - other error
--
band.SafeAdd = function ( ... )
--
-- error messages
--
local BADRHO = "rho must be"
local BADTHETA = "theta must be"
local BADPHI = "phi must be"
--
local good, errmsg = pcall ( band.Add, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
local errp = err2:find ( BADRHO )
if errp ~= nil then
return -4, err2
end
local errp = err2:find ( BADTHETA )
if errp ~= nil then
return -5, err2
end
local errp = err2:find ( BADPHI )
if errp ~= nil then
return -6, err2
end
return -999, err2
end
end
--
-- band.SafeAddBetweenSegments uses pcall
-- to call band.AddBetweenSegments, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the band index of the added band (number).
-- -1 - bad segment number
-- -2 - bad atom number
-- -3 - bad symmetric chain number
-- -99x - other error
--
band.SafeAddBetweenSegments = function ( ... )
local good, errmsg = pcall ( band.AddBetweenSegments, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- band.SafeAddToBandEndpoint uses pcall
-- to call band.ToBandEndpoint, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the band index of the added band (number).
-- -1 - bad segment number
-- -2 - bad atom number
-- -3 - bad band number
-- -99x - other error
--
band.SafeAddToBandEndpoint = function ( ... )
local good, errmsg = pcall ( band.AddToBandEndpoint, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- band.SafeDelete uses pcall
-- to call band.Delete, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -3 - bad band number
-- -99x - other error
--
band.SafeDelete = function ( ... )
local good, errmsg = pcall ( band.Delete, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- band.SafeDisable uses pcall
-- to call band.Disable, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -3 - bad band number
-- -99x - other error
--
band.SafeDisable = function ( ... )
local good, errmsg = pcall ( band.Disable, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- band.SafeGetGoalLength uses pcall
-- to call band.GetGoalLength, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the goal length
-- of the specifed band (number).
-- -3 - bad band number
-- -99x - other error
--
band.SafeGetGoalLength = function ( ... )
local good, errmsg = pcall ( band.GetGoalLength, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- band.SafeGetLength uses pcall
-- to call band.GetLength, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the current length
-- of the specifed band (number).
-- -3 - bad band number
-- -99x - other error
--
band.SafeGetLength = function ( ... )
local good, errmsg = pcall ( band.GetLength, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- band.SafeGetStrength uses pcall
-- to call band.GetStrength, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the strength
-- of the specified band (number).
-- -3 - bad band number
-- -99x - other error
--
band.SafeGetStrength = function ( ... )
local good, errmsg = pcall ( band.GetStrength, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
--
-- band.SafeSetGoalLength uses pcall
-- to call band.SetGoalLength, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful
-- -1 - reserved
-- -2 - reserved
-- -3 - bad band number
-- -4 - bad band length
-- -99x - other error
--
band.SafeSetGoalLength = function ( ... )
--
-- error messages
--
local BADLEN = "band length out of bounds"
--
local good, errmsg = pcall ( band.SetGoalLength, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
local errp = err2:find ( BADLEN )
if errp ~= nil then
return -4, err2
end
return -999, err2
end
end
--
-- band.SafeSetStrength uses pcall
-- to call band.SetStrength, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful
-- -1 - reserved
-- -2 - reserved
-- -3 - bad band number
-- -4 - bad band strength
-- -99x - other error
--
band.SafeSetStrength = function ( ... )
--
-- error messages
--
local BADSTREN = "band strength out of bounds"
--
local good, errmsg = pcall ( band.SetStrength, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
local errp = err2:find ( BADSTREN )
if errp ~= nil then
return -4, err2
end
return -999, err2
end
end
--
-- behavior.SafeSetClashImportance uses pcall
-- to call behavior.SetClashImportance, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad CI
-- -99x - other error
--
behavior.SafeSetClashImportance = function ( ... )
--
-- error message
--
local BADCLASH = "clashing importance value outside valid range"
--
local good, errmsg = pcall ( behavior.SetClashImportance, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
local errp = err2:find ( BADCLASH )
if errp ~= nil then
return -1, err2
end
return -999, err2
end
end
--
-- contactmap.SafeIsContact uses pcall
-- to call contactmap.IsContact, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the contact status
-- of the specified segments (boolean).
-- -1 - bad segment index
-- -2 - bad amino acid code
-- -99x - other error
--
contactmap.SafeIsContact = function ( ... )
local good, errmsg = pcall ( contactmap.IsContact, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- freeze.SafeFreeze uses pcall
-- to call freeze.Freeze, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -2 - bad amino acid code
-- -99x - other error
--
freeze.SafeFreeze = function ( ... )
local good, errmsg = pcall ( freeze.Freeze, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- freeze.SafeIsFrozen uses pcall
-- to call freeze.IsFrozen, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the freeze status
-- of the specified segment (boolean).
-- -1 - bad segment index
-- -2 - bad amino acid code
-- -99x - other error
--
freeze.SafeIsFrozen = function ( ... )
local good, errmsg = pcall ( freeze.IsFrozen, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- freeze.SafeUnfreeze uses pcall
-- to call freeze.Unfreeze, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -2 - bad amino acid code
-- -99x - other error
--
freeze.SafeUnfreeze = function ( ... )
local good, errmsg = pcall ( freeze.Unfreeze, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- rotamer.SafeGetCount uses pcall
-- to call rotamer.GetCount, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the rotamer count
-- of the specified segment (boolean).
-- -1 - bad segment index
-- -2 - bad amino acid code
-- -99x - other error
--
rotamer.SafeGetCount = function ( ... )
local good, errmsg = pcall ( rotamer.GetCount, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- rotamer.SafeSetRotamer uses pcall
-- to call rotamer.SetRotamer, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -2 - bad rotamer index
-- -99x - other error
--
rotamer.SafeSetRotamer = function ( ... )
--
-- error messages
--
local BADSNAP = "snap index out of bounds"
--
local good, errmsg = pcall ( rotamer.SetRotamer, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
local errp = err2:find ( BADSNAP )
if errp ~= nil then
return -2, err2
end
return -999, err2
end
end
--
-- selection.SafeIsSelected uses pcall
-- to call selection.IsSelected, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the selection status
-- of the specified segment (boolean).
-- -1 - bad segment index
-- -99x - other error
--
selection.SafeIsSelected = function ( ... )
local good, errmsg = pcall ( selection.IsSelected, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
--
-- selection.SafeSelect uses pcall
-- to call selection.Select, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -99x - other error
--
selection.SafeSelect = function ( ... )
local good, errmsg = pcall ( selection.Select, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- selection.SafeSelectRange uses pcall
-- to call selection.SelectRange, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -99x - other error
--
selection.SafeSelectRange = function ( ... )
local good, errmsg = pcall ( selection.SelectRange, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeCanMutate uses pcall
-- to call structure.CanMutate, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the mutability status
-- of the specified segment (boolean).
-- -1 - bad segment index
-- -2 - bad amino acid code
-- -99x - other error
--
structure.SafeCanMutate = function ( ... )
local good, errmsg = pcall ( structure.CanMutate, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeDeleteCut uses pcall
-- to call structure.DeleteCut, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeDeleteCut = function ( ... )
local good, errmsg = pcall ( structure.DeleteCut, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeDeleteResidue uses pcall
-- to call structure.DeleteResidue, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeDeleteResidue = function ( ... )
local good, errmsg = pcall ( structure.DeleteResidue, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeGetAminoAcid uses pcall
-- to call structure.GetAminoAcid, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the one-letter amino acid code
-- of the specified segment (string).
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeGetAminoAcid = function ( ... )
local good, errmsg = pcall ( structure.GetAminoAcid, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeGetAtomCount uses pcall
-- to call structure.GetAtomCount, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the atom count
-- of the specified segment (number).
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeGetAtomCount = function ( ... )
local good, errmsg = pcall ( structure.GetAtomCount, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeGetDistance uses pcall
-- to call structure.GetDistance, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned
-- value is the distance
-- 0 - successful, second returned value is
-- the distance between
-- the specified segments (number).
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeGetDistance = function ( ... )
local good, errmsg = pcall ( structure.GetDistance, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeGetNote uses pcall
-- to call structure.GetNote, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the note text
-- for the specified segment (string).
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeGetNote = function ( ... )
local good, errmsg = pcall ( structure.GetNote, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeGetSecondaryStructure uses pcall
-- to call structure.GetSecondaryStructure, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the one-letter secondary structure code
-- of the specified segment (string).
-- -1 - bad segment index
-- -2 - bad amino acid code
-- -99x - other error
--
structure.SafeGetSecondaryStructure = function ( ... )
local good, errmsg = pcall ( structure.GetSecondaryStructure, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeInsertCut uses pcall
-- to call structure.InsertCut, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeInsertCut = function ( ... )
local good, errmsg = pcall ( structure.InsertCut, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeInsertResidue uses pcall
-- to call structure.InsertResidue, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeInsertResidue = function ( ... )
local good, errmsg = pcall ( structure.InsertResidue, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeIsHydrophobic uses pcall
-- to call structure.IsHydrophobic, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the hydrophobicity status
-- of the specified segment (boolean).
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeIsHydrophobic = function ( ... )
local good, errmsg = pcall ( structure.IsHydrophobic, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeIsLocked uses pcall
-- to call structure.IsLocked, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the locked status
-- of the specified segment (boolean).
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeIsLocked = function ( ... )
local good, errmsg = pcall ( structure.IsLocked, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeIsMutable uses pcall
-- to call structure.IsMutable, returning
-- a numeric return code.
--
-- If the return code is non-zero,
-- an error message is also returned.
--
-- The return codes are:
--
-- 0 - successful, second returned value is
-- the mutability status
-- of the specified segment (boolean).
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeIsMutable = function ( ... )
local good, errmsg = pcall ( structure.IsMutable, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeSetAminoAcid uses pcall
-- to call structure.SetAminoAcid, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -2 - bad amino acid code
-- -99x - other error
--
structure.SafeSetAminoAcid = function ( ... )
local good, errmsg = pcall ( structure.SetAminoAcid, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeSetAminoAcidSelected uses pcall
-- to call structure.SetAminoAcidSelected, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -2 - bad amino acid code
-- -99x - other error
--
structure.SafeSetAminoAcidSelected = function ( ... )
local good, errmsg = pcall ( structure.SetAminoAcidSelected, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeSetNote uses pcall
-- to call structure.SetNote, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -99x - other error
--
structure.SafeSetNote = function ( ... )
local good, errmsg = pcall ( structure.SetNote, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
return -999, err2
end
end
--
-- structure.SafeSetSecondaryStructure uses pcall
-- to call structure.SetSecondaryStructure, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -1 - bad segment index
-- -2 - bad secondary structure code
-- -99x - other error
--
structure.SafeSetSecondaryStructure = function ( ... )
--
-- error messages
--
local BADCODE = "invalid argument"
local good, errmsg = pcall ( structure.SetSecondaryStructure, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
local errp = err2:find ( BADCODE )
if errp ~= nil then
return -2, err2
end
return -999, err2
end
end
--
-- structure.SafeSetSecondaryStructureSelected uses pcall
-- to call structure.SetSecondaryStructureSelected, returning
-- a numeric return code and an error message.
--
-- The return codes are:
--
-- 0 - successful, error message is nil
-- -2 - bad secondary structure code
-- -99x - other error
--
structure.SafeSetSecondaryStructureSelected = function ( ... )
--
-- error message for an invalid secondary structure code
--
local BADCODE = "invalid argument"
local good, errmsg = pcall ( structure.SetSecondaryStructureSelected, unpack ( arg ) )
if good then
return 0, errmsg
else
local crc, err2 = safefun.CommonError ( errmsg )
if crc ~= 0 then
return crc, err2
end
local errp = err2:find ( BADCODE )
if errp ~= nil then
return -2, err2
end
return -999, err2
end
end
--
-- end of safe functions v 1.0
--
--
-- try to terminate the recipe
--
function main ()
print ( ReVersion )
print ( "Puzzle: " .. puzzle.GetName () )
print ( "Track: " .. ui.GetTrackName () )
save.Quicksave ( 3 )
save.SaveSecondaryStructure ()
band.DeleteAll ()
local segCnt = structure.GetCount ()
print ( "Segment count = " .. segCnt )
--
-- many functions use segment indexes,
-- define some useful ones
--
local SEGOUT = segCnt + 1
local SEGFOO1 = -1
local SEGFOO2 = -999
local SEGFOO3 = 999
local SEGMID = math.floor ( segCnt / 2 )
print ( "Middle segment = " .. SEGMID )
--
-- assorted other bad values
--
local HIATOM = 99 -- atom number
local HIROTA = 999 -- rotamer number
local HIBAND = 999
local LOBAND = -1
local LNGBND = 11000
local SHRTBND= -1
local STRNGBND = 12
local WEAKBND = -1
local HIRHO = 11000 -- you take the high rho
local LORHO = -39 -- and I'll take the low rho
local OKRHO = 20 -- and this one is just right
local HITHETA = 4.0
local LOTHETA = -1.0
local OKTHETA = 3.14 / 2
local HIPHI = 3.14 * 3
local LOPHI = -1.0
local OKPHI = 3.14
local HISYM = 36 -- when does an oligomer become a polymer?
--
-- function table - one entry per function to be tested
--
-- somewhat repetitive and repetitious, but foldit does
-- not allow access to the Lua "debug" functions
--
local FUNCNAME = 1
local FUNCCALL = 2
local func = {
Add = { "band.SafeAdd",
band.SafeAdd, },
AddBetweenSegments = { "band.SafeAddBetweenSegments",
band.SafeAddBetweenSegments, },
AddToBandEndpoint = { "band.SafeAddToBandEndpoint",
band.SafeAddToBandEndpoint, },
Delete = { "band.SafeDelete",
band.SafeDelete, },
Disable = { "band.SafeDisable",
band.SafeDisable, },
GetGoalLength = { "band.SafeGetGoalLength",
band.SafeGetGoalLength, },
GetLength = { "band.SafeGetLength",
band.SafeGetLength, },
GetStrength = { "band.SafeGetStrength",
band.SafeGetStrength, },
SetGoalLength = { "band.SafeSetGoalLength",
band.SafeSetGoalLength, },
SetStrength = { "band.SafeSetStrength",
band.SafeSetStrength, },
SetClashImportance = { "behavior.SafeSetClashImportance",
behavior.SafeSetClashImportance, },
IsContact = { "contactmap.SafeIsContact",
contactmap.SafeIsContact, },
Freeze = { "freeze.SafeFreeze",
freeze.SafeFreeze, },
IsFrozen = { "freeze.SafeIsFrozen",
freeze.SafeIsFrozen, },
Unfreeze = { "freeze.SafeUnfreeze",
freeze.SafeUnfreeze, },
GetCount = { "rotamer.SafeGetCount",
rotamer.SafeGetCount, },
SetRotamer = { "rotamer.SafeSetRotamer",
rotamer.SafeSetRotamer, },
Select = { "selection.SafeSelect",
selection.SafeSelect, },
IsSelected = { "selection.SafeIsSelected",
selection.SafeIsSelected, },
SelectRange = { "selection.SafeSelectRange",
selection.SafeSelectRange, },
CanMutate = { "structure.SafeCanMutate",
structure.SafeCanMutate, },
DeleteCut = { "structure.SafeDeleteCut",
structure.SafeDeleteCut, },
DeleteResidue = { "structure.SafeDeleteResidue",
structure.SafeDeleteResidue, },
GetAminoAcid = { "structure.SafeGetAminoAcid",
structure.SafeGetAminoAcid, },
GetAtomCount = { "structure.SafeGetAtomCount",
structure.SafeGetAtomCount, },
GetDistance = { "structure.SafeGetDistance",
structure.SafeGetDistance, },
GetNote = { "structure.SafeGetNote",
structure.SafeGetNote, },
GetSecondaryStructure = { "structure.SafeGetSecondaryStructure",
structure.SafeGetSecondaryStructure, },
InsertCut = { "structure.SafeInsertCut",
structure.SafeInsertCut, },
InsertResidue = { "structure.SafeInsertResidue",
structure.SafeInsertResidue, },
IsHydrophobic = { "structure.SafeIsHydrophobic",
structure.SafeIsHydrophobic, },
IsLocked = { "structure.SafeIsLocked",
structure.SafeIsLocked, },
IsMutable = { "structure.SafeIsMutable",
structure.SafeIsMutable, },
SetAminoAcid = { "structure.SafeSetAminoAcid",
structure.SafeSetAminoAcid, },
SetAminoAcidSelected = { "structure.SafeSetAminoAcidSelected",
structure.SafeSetAminoAcidSelected, },
SetNote = { "structure.SafeSetNote",
structure.SafeSetNote, },
SetSecondaryStructure = { "structure.SafeSetSecondaryStructure",
structure.SafeSetSecondaryStructure, },
SetSecondaryStructureSelected = { "structure.SafeSetSecondaryStructureSelected",
structure.SafeSetSecondaryStructureSelected, },
}
--
-- test table - one entry per test, grouped by function
--
-- first column is a reference to the function table
-- second column is the expected return code from the "safe" function
-- third "column" is a table containing the arguments passed to the function
--
local TESTFUNC = 1 -- function table entry
local TESTEXRC = 2 -- expected return code
local TESTARGZ = 3 -- argument array
local TESTTYPE = 4 -- test type
--
-- test types - intended to be bit flags, but Lua 5.1 doesn't support them
--
TTPASSES = 0x01 -- expected to succeed
TTSEGRNG = 0x02 -- segment range error
TTPVALUE = 0x04 -- parameter value error
TTARGCNT = 0x08 -- argument count error
TTARGTYP = 0x10 -- argument type error
--
-- band functions
--
local bandtest = {
{ func.Add, -1, { 1, SEGMID, SEGOUT, OKRHO, OKTHETA, OKPHI }, TTSEGRNG, },
{ func.Add, -4, { 1, SEGMID, segCnt, HIRHO, OKTHETA, OKPHI }, TTPVALUE, },
{ func.Add, -4, { 1, SEGMID, segCnt, LORHO, OKTHETA, OKPHI }, TTPVALUE, },
{ func.Add, -5, { 1, SEGMID, segCnt, OKRHO, HITHETA, OKPHI }, TTPVALUE, },
{ func.Add, -5, { 1, SEGMID, segCnt, OKRHO, LOTHETA, OKPHI }, TTPVALUE, },
{ func.Add, -6, { 1, SEGMID, segCnt, OKRHO, OKTHETA, HIPHI }, TTPVALUE, },
{ func.Add, -6, { 1, SEGMID, segCnt, OKRHO, OKTHETA, LOPHI }, TTPVALUE, },
{ func.AddBetweenSegments, -1, { 1, SEGOUT, }, TTSEGRNG, },
{ func.AddBetweenSegments, -1, { SEGFOO1, segCnt, }, TTSEGRNG, },
{ func.AddBetweenSegments, -1, { SEGFOO2, SEGFOO2 }, TTSEGRNG, },
{ func.AddBetweenSegments, -997, { "foo", "bar", }, TTARGTYP, },
{ func.AddBetweenSegments, -2, { 1, SEGMID, HIATOM, HIATOM }, TTPVALUE, },
{ func.AddBetweenSegments, -3, { 1, 10, nil, nil, HISYM, }, TTPVALUE, },
{ func.AddBetweenSegments, 0, { 1, 10, }, TTPASSES, },--used below
{ func.AddBetweenSegments, 0, { 2, 10, }, TTPASSES, },--used below
{ func.AddToBandEndpoint, 0, { 1, 1, }, TTPASSES, },--uses band
{ func.AddToBandEndpoint, -2, { 1, 1, HIATOM, }, TTPVALUE, },--uses band
{ func.AddToBandEndpoint, -1, { SEGFOO1, 1, }, TTSEGRNG, },
{ func.AddToBandEndpoint, -3, { 5, 13, }, TTPVALUE, },
{ func.AddToBandEndpoint, -1, { SEGFOO3, 1, }, TTSEGRNG, },
{ func.AddToBandEndpoint, -997, { "foo", "bar", }, TTARGTYP, },
{ func.Delete, 0, { 2, }, TTPASSES, },--uses band
{ func.Delete, -3, { HIBAND, }, TTPVALUE, },
{ func.Delete, -3, { LOBAND, }, TTPVALUE, },
{ func.Disable, 0, { 1, }, TTPASSES, },--uses band
{ func.Disable, -3, { HIBAND, }, TTPVALUE, },
{ func.Disable, -3, { LOBAND, }, TTPVALUE, },
{ func.GetGoalLength, 0, { 1, }, TTPASSES, },--uses band
{ func.GetGoalLength, -3, { HIBAND, }, TTPVALUE, },
{ func.GetGoalLength, -3, { LOBAND, }, TTPVALUE, },
{ func.GetLength, 0, { 1, }, TTPASSES, },--uses band
{ func.GetLength, -3, { HIBAND, }, TTPVALUE, },
{ func.GetLength, -3, { LOBAND, }, TTPVALUE, },
{ func.GetStrength, 0, { 1, }, TTPASSES, },--uses band
{ func.GetStrength, -3, { HIBAND, }, TTPVALUE, },
{ func.GetStrength, -3, { LOBAND, }, TTPVALUE, },
{ func.SetGoalLength, 0, { 1, 1, }, TTPASSES, },--uses band
{ func.SetGoalLength, -3, { HIBAND, 1, }, TTPVALUE, },
{ func.SetGoalLength, -3, { LOBAND, 1, }, TTPVALUE, },
{ func.SetGoalLength, -4, { 1, LNGBND, }, TTPVALUE, },--uses band
{ func.SetGoalLength, -4, { 1, SHRTBND, }, TTPVALUE, },--uses band
{ func.SetStrength, 0, { 1, 1, }, TTPASSES, },--uses band
{ func.SetStrength, -3, { HIBAND, 1, }, TTPVALUE, },
{ func.SetStrength, -3, { LOBAND, 1, }, TTPVALUE, },
{ func.SetStrength, -4, { 1, STRNGBND, }, TTPVALUE, },--uses band
{ func.SetStrength, -4, { 1, WEAKBND, }, TTPVALUE, },--uses band
}
--
-- behavior functions
--
local bhavtest = {
{ func.SetClashImportance, -997, { "foo", }, TTARGTYP, },
{ func.SetClashImportance, -1, { -1, }, TTPVALUE, },
{ func.SetClashImportance, -1, { SEGFOO3, }, TTPVALUE, },
{ func.SetClashImportance, 0, { 1, }, TTPASSES, },
}
--
-- contactmap functions
--
local conttest = {
{ func.IsContact, 0, { 1, segCnt, }, TTPASSES, },
{ func.IsContact, -1, { 1, SEGOUT, }, TTSEGRNG, },
{ func.IsContact, -1, { SEGFOO1, segCnt, }, TTSEGRNG, },
}
--
-- freeze functions
--
local frzetest = {
{ func.Freeze, 0, { 1, true, true, }, TTPASSES, },
{ func.Freeze, -1, { SEGOUT, true, true, }, TTSEGRNG, },
{ func.Freeze, -1, { SEGFOO1, true, true, }, TTSEGRNG, },
{ func.Freeze, -998, { 1, true, }, TTARGCNT, },
{ func.Freeze, -998, { 1, true, }, TTARGCNT, },
{ func.IsFrozen, 0, { 1, }, TTPASSES, },
{ func.IsFrozen, -1, { SEGOUT, }, TTSEGRNG, },
{ func.IsFrozen, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.Unfreeze, 0, { 1, true, true, }, TTPASSES, },
{ func.Unfreeze, -1, { SEGOUT, true, true, }, TTSEGRNG, },
{ func.Unfreeze, -1, { SEGFOO1, true, true, }, TTSEGRNG, },
{ func.Unfreeze, -998, { 1, true, }, TTARGCNT, },
{ func.Unfreeze, -998, { 1, true, }, TTARGCNT, },
}
--
-- rotamer functions
--
local rotatest = {
{ func.GetCount, 0, { 1, }, TTPASSES, },
{ func.GetCount, -1, { SEGOUT, }, TTSEGRNG, },
{ func.GetCount, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.SetRotamer, -2, { 1, HIROTA, }, TTPVALUE, },
{ func.SetRotamer, -1, { SEGFOO1, 1 }, TTSEGRNG, },
{ func.SetRotamer, -1, { SEGFOO1, HIROTA, }, TTSEGRNG, },
}
--
-- selection functions
--
local seletest = {
{ func.IsSelected, -997, { "foo", }, TTARGTYP, },
{ func.IsSelected, -1, { -1, }, TTSEGRNG, },
-- { func.IsSelected, -1, { SEGFOO3, }, TTSEGRNG, },
{ func.IsSelected, -1, { SEGOUT, }, TTSEGRNG, },
{ func.Select, -997, { "foo", }, TTARGTYP, },
{ func.Select, -1, { -1, }, TTSEGRNG, },
-- { func.Select, -1, { SEGFOO3, }, TTSEGRNG, },
{ func.Select, -1, { SEGOUT, }, TTSEGRNG, },
{ func.Select, -997, { "next error OK" }, TTARGTYP, },--test error
{ func.Select, 0, { 1, 777, }, TTSEGRNG, },--test error
{ func.Select, -997, { "prev error OK" }, TTARGTYP, },--test error
{ func.SelectRange, -1, { 1, SEGOUT, }, TTSEGRNG, },
{ func.SelectRange, -1, { SEGFOO1, segCnt }, TTSEGRNG, },
{ func.SelectRange, -1, { SEGFOO2, SEGFOO2 }, TTSEGRNG, },
{ func.SelectRange, -998, { 1, SEGMID, 999 }, TTARGCNT, },
}
--
-- structure functions
--
local strutest = {
{ func.CanMutate, -1, { SEGOUT, "a", }, TTSEGRNG, },
{ func.CanMutate, -1, { SEGFOO1, "a", }, TTSEGRNG, },
{ func.CanMutate, 0, { 1, "a", }, TTPASSES, },
{ func.CanMutate, -2, { 1, "x", }, TTARGVAL, },
{ func.DeleteCut, -1, { SEGOUT, }, TTSEGRNG, },
{ func.DeleteCut, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.DeleteResidue, -1, { SEGOUT, }, TTSEGRNG, },
{ func.DeleteResidue, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.GetAminoAcid, -1, { SEGOUT, }, TTSEGRNG, },
{ func.GetAminoAcid, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.GetAtomCount, -1, { SEGOUT, }, TTSEGRNG, },
{ func.GetAtomCount, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.GetDistance, -1, { 1, SEGOUT, }, TTSEGRNG, },
{ func.GetDistance, -1, { 1, SEGFOO1, }, TTSEGRNG, },
{ func.GetDistance, -1, { SEGFOO1, 10, }, TTSEGRNG, },
{ func.GetDistance, -1, { SEGOUT, 10, }, TTSEGRNG, },
{ func.GetNote, -1, { SEGOUT, }, TTSEGRNG, },
{ func.GetNote, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.GetSecondaryStructure, -1, { SEGOUT, }, TTSEGRNG, },
{ func.GetSecondaryStructure, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.InsertCut, -1, { SEGOUT, }, TTSEGRNG, },
{ func.InsertCut, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.InsertResidue, -1, { SEGOUT, }, TTSEGRNG, },
{ func.InsertResidue, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.IsHydrophobic, -1, { SEGOUT, }, TTSEGRNG, },
{ func.IsHydrophobic, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.IsLocked, -1, { SEGOUT, }, TTSEGRNG, },
{ func.IsLocked, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.IsMutable, -1, { SEGOUT, }, TTSEGRNG, },
{ func.IsMutable, -1, { SEGFOO1, }, TTSEGRNG, },
{ func.SetAminoAcid, -2, { 1, "b", }, TTPVALUE, },
{ func.SetAminoAcid, -1, { -1, "a", }, TTSEGRNG, },
{ func.SetAminoAcid, -2, { 1, "b", }, TTARGVAL, },
{ func.SetAminoAcidSelected, 0, { "a", }, TTPASSES, },
{ func.SetAminoAcidSelected, -2, { "b", }, TTARGVAL, },
{ func.SetNote, -1, { SEGOUT, "test note", }, TTSEGRNG, },
{ func.SetNote, -1, { SEGFOO1, "test note", }, TTSEGRNG, },
{ func.SetNote, -998, { 1, }, TTARGCNT, },
{ func.SetNote, 0, { 1, "test note", }, TTPASSES, },
{ func.SetSecondaryStructure, -2, { 1, "R", }, TTPVALUE, },
{ func.SetSecondaryStructure, -1, { SEGFOO1, "X", }, TTSEGRNG, },
{ func.SetSecondaryStructure, -1, { SEGFOO1, "L", }, TTSEGRNG, },
{ func.SetSecondaryStructureSelected, -2, { "R", }, TTSEGRNG, },
{ func.SetSecondaryStructureSelected, -2, { "X", }, TTPVALUE, },
{ func.SetSecondaryStructureSelected, 0, { "L", }, TTPASSES, },
}
local GPARM = 1
local GNAME = 2
local GDOIT = 3
local testgrpz = {
{ bandtest, "band", false, },
{ bhavtest, "behavior", false, },
{ conttest, "contactmap", false, },
{ frzetest, "freeze", false, },
{ rotatest, "rotamer", false, },
{ seletest, "selection", false, },
{ strutest, "structure", false, },
}
--
-- inline dialog!
--
local ask = dialog.CreateDialog ( ReVersion )
ask.l001 = dialog.AddLabel ( "select the type of tests to perform" )
ask.ttpasses = dialog.AddCheckbox ( "run tests expected to pass", opts.ttpasses )
ask.ttsegrng = dialog.AddCheckbox ( "run tests for segment range errors", opts.ttsegrng )
ask.ttpvalue = dialog.AddCheckbox ( "run tests for parameter value errors", opts.ttpvalue )
ask.ttargcnt = dialog.AddCheckbox ( "run tests for parameter count errors", opts.ttargcnt )
ask.ttargtyp = dialog.AddCheckbox ( "run tests for parameter type errors", opts.ttargtyp )
ask.l005 = dialog.AddLabel ( "" )
ask.l006 = dialog.AddLabel ( "Test functions in all Foldit function groups:" )
ask.runall = dialog.AddCheckbox ( "all groups", opts.runall )
ask.l010 = dialog.AddLabel ( "or, uncheck \"all groups\" and select the groups to test:" )
for ii = 1, #testgrpz do
ask [ testgrpz [ ii ] [ GNAME ] ] = dialog.AddCheckbox ( testgrpz [ ii ] [ GNAME ], testgrpz [ ii ] [ GDOIT ] )
end
ask.OK = dialog.AddButton ( "OK", 1 )
ask.cancel = dialog.AddButton ( "Cancel", -1 )
local askrc = dialog.Show ( ask )
local grpcnt = 0
if askrc > 0 then
opts.ttpasses = ask.ttpasses.value
opts.ttsegrng = ask.ttsegrng.value
opts.ttpvalue = ask.ttpvalue.value
opts.ttargcnt = ask.ttargcnt.value
opts.ttargtyp = ask.ttargtyp.value
opts.runall = ask.runall.value
if opts.runall then
for ii = 1, #testgrpz do
testgrpz [ ii ] [ GDOIT ] = true
grpcnt = grpcnt + 1
end
else
for ii = 1, #testgrpz do
testgrpz [ ii ] [ GDOIT ] = ask [ testgrpz [ ii ] [ GNAME ] ].value
if testgrpz [ ii ] [ GDOIT ] then
grpcnt = grpcnt + 1
end
end
end
else
cleanup ()
end
if opts.ttpasses then
print ( "running test where the function should succeed" )
end
if opts.ttsegrng then
print ( "running tests for segment range errors" )
end
if opts.ttpvalue then
print ( "running tests for invalid parameter value errors" )
end
if opts.ttargcnt then
print ( "running tests for invalid parameter count errors" )
end
if opts.ttargtyp then
print ( "running tests for invalid parameter type errors" )
end
print ( "" )
print ( grpcnt .. " groups selected" )
if grpcnt == 0 then
print ( "no tests selected, cleaning up" )
cleanup ()
end
print ( "--" )
--
-- all this inline too! maybe we need a function or two!
--
local totlruns = 0
local totlfailz = 0
local totlfuns = 0
local totlskipz = 0
for gg = 1, #testgrpz do
if testgrpz [ gg ] [ GDOIT ] then
print ( "-----------------------------------------" )
print ( "testing functions in the " .. testgrpz [ gg ] [ GNAME ] .. " group" )
local lastfun = nil
local testruns = 0
local testfailz = 0
local testfuns = 0
local testskipz = 0
local testtabl = testgrpz [ gg ] [ GPARM ]
for tt = 1, #testtabl do
local ttp = testtabl [ tt ] [ TESTTYPE ]
if ( opts.ttpasses and ( ttp == TTPASSES ) )
or ( opts.ttsegrng and ( ttp == TTSEGRNG ) )
or ( opts.ttpvalue and ( ttp == TTPVALUE ) )
or ( opts.ttargcnt and ( ttp == TTARGCNT ) )
or ( opts.ttargtyp and ( ttp == TTARGTYP ) ) then
testruns = testruns + 1
if testtabl [ tt ] [ TESTFUNC ] ~= lastfun then
testfuns = testfuns + 1
lastfun = testtabl [ tt ] [ TESTFUNC ]
print ( "-----------------------------------------" )
end
local argz = testtabl [ tt ] [ TESTARGZ ]
local calltext
--
-- format a description of the function call
--
calltext = lastfun [ FUNCNAME ] .. " ( "
for ii = 1, #argz do
local argtext
if type ( argz [ ii ] ) == "string" then
argtext = "\"" .. argz [ ii ] .. "\""
else
argtext = tostring ( argz [ ii ] )
end
if ii > 1 then
calltext = calltext .. ", "
end
calltext = calltext .. argtext
end
calltext = calltext .. " )"
--
-- call the function
--
--[[
local ask = dialog.CreateDialog ( ReVersion )
ask.testnum = dialog.AddLabel ( "test # " .. tt )
ask.calltext = dialog.AddTextbox ( "next: ", calltext )
structure.SetNote ( 1, "next test: " .. tt .. " - " .. calltext )
ask.OK = dialog.AddButton ( "OK", 1 )
dialog.Show ( ask )
]]--
print ( "test # " .. tt .. ": calling " .. calltext )
local rc, errmsg
rc, errmsg = lastfun [ FUNCCALL ] ( unpack ( argz ) )
--
-- report the results
--
calltext = calltext .. ", rc = " .. rc
if rc < 0 and errmsg ~= nil then
calltext = calltext .. ", errmsg = \"" .. errmsg .. "\""
end
print ( calltext )
if rc ~= testtabl [ tt ] [ TESTEXRC ] then
testfailz = testfailz + 1
if rc == 0 then
print ( "ERROR: unexpected rc = 0" )
else
print ( "ERROR: unexpected rc = " .. rc .. ", errmsg = \"" .. errmsg .. "\"" )
end
end
else
testskipz = testskipz + 1
end
end
--
print ( "-----------------------------------------" )
print ( "" )
print ( testgrpz [ gg ] [ GNAME ] .. " tests complete" )
print ( )
print ( testfuns .. " functions tested in " .. testgrpz [ gg ] [ GNAME ] .. " group" )
print ( testruns .. " tests run" )
print ( testfailz .. " test failures" )
print ( testskipz .. " tests skipped based on type" )
totlfuns = totlfuns + testfuns
totlruns = totlruns + testruns
totlfailz = totlfailz + testfailz
totlskipz = totlskipz + testskipz
--
end
end
print ( "-----------------------------------------" )
print ( "" )
print ( "all tests complete" )
print ( totlfuns .. " functions tested in " .. grpcnt .. " groups" )
print ( totlruns .. " total tests run" )
print ( totlfailz .. " test failures" )
print ( totlskipz .. " tests skipped based on type" )
cleanup ()
end
function cleanup ( errmsg )
print ( "---" )
--
-- model 100 - print recipe name, puzzle, track, time, score, and gain
--
local reason
local start, stop, line, msg
if errmsg == nil then
reason = "complete"
else
--
-- model 120 - civilized error reporting,
-- thanks to Bruno K. and Jean-Bob
--
start, stop, line, msg = errmsg:find ( ":(%d+):%s()" )
if msg ~= nil then
errmsg = errmsg:sub ( msg, #errmsg )
end
if errmsg:find ( "Cancelled" ) ~= nil then
reason = "cancelled"
else
reason = "error"
end
end
print ( ReVersion .. " " .. reason )
print ( "Puzzle: " .. puzzle.GetName () )
print ( "Track: " .. ui.GetTrackName () )
if reason == "error" then
print ( "Unexpected error detected" )
if line ~= nil then
print ( "Error line: " .. line )
end
print ( "Error: \"" .. errmsg .. "\"" )
end
--
-- model 130 - reset clash importance, clear selections, restore structures, etc.
--
save.Quickload ( 3 )
save.LoadSecondaryStructure ()
selection.DeselectAll ()
behavior.SetClashImportance ( 1.0 )
band.DeleteAll ()
end
-- main call
xpcall ( main, cleanup )
--end of script