Today’s adventure involved writing a system that makes it easy to test features and capabilities of the engine. Here’s a selection of tests that show it off:
-- `feature.lua == true` if no subtests fail.
define("lua", {
-- Failed subtests are logged for later scrutiny. Subtest names are used
-- only for logging.
{"base", {
{"_G", standard, type(_G) == "table"},
{"_VERSION", standard, type(_VERSION) == "string"},
{"assert", standard, type(assert) == "function"},
}},
-- Tests can be wrapped in a function to avoid runtime errors
-- (e.g. indexing `table` when it doesn't exist).
{"table", function()return{
type(table) == "table",
{"insert", standard, type(table.insert) == "function"},
{"remove", standard, type(table.remove) == "function"},
{"foreach", optional, type(table.foreach) == "function"},
{"maxn", optional, type(table.maxn) == "function"},
-- Optional tests are logged, but do not cause the parent test to fail.
}end},
})
define("roblox", {
-- Failure of a required test causes subsequent tests to be ignored; their
-- assumed failure wont spam the log.
{"lua", required, feature.lua},
{"base", {
{"game", standard, type(game) == "userdata"},
{"wait", standard, type(wait) == "function"},
{"Game", optional, type(Game) == "userdata"},
{"Wait", optional, type(Wait) == "function"},
}},
{"math", function()return{
{"clamp", standard, type(math.clamp) == "function"},
{"noise", standard, type(math.noise) == "function"},
{"sign", standard, type(math.sign) == "function"},
}end},
})
-- Define features that may actually be useful.
define("HttpEnabled", {feature.roblox, function()
local ok, result = pcall(function()
game:GetService("HttpService"):GetAsync("https://www.roblox.com/robots.txt")
end)
return not ok and result ~= "Http requests are not enabled" or ok
end})
define("LoadstringEnabled", {feature.roblox, function()
return not not loadstring("")
end})
-- Elsewhere...
if feature.HttpEnabled then
-- We can use HttpService!
end
if feature.LoadstringEnabled then
-- We can use loadstring!
end
The fun part was writing it with as few dependencies as possible. The implementation requires only pcall
for calling functions and type
for checking values, though it’s possible to use only pcall
(e.g. pcall(function(v) return v+1 end, value) == true
means value
is a number).