Start of API system overhaul, in support of configuration mapping

This commit is contained in:
Jason Perkins 2012-04-04 16:21:22 -04:00
parent 38ae459803
commit 22266a995d
7 changed files with 361 additions and 3 deletions

View File

@ -4,6 +4,82 @@
-- Copyright (c) 2002-2012 Jason Perkins and the Premake project
--
premake.api = {}
local api = premake.api
--
-- A place to store the current active objects in each project scope.
--
api.scope = {}
--
-- Register a new API function. See the built-in API definitions below
-- for usage examples.
--
function api.register(description)
-- verify the name
local name = description.name
if not name then
error("missing name", 2)
end
if _G[name] then
error("name in use", 2)
end
-- make sure there is a handler available for this kind of value
if not api["set" .. description.kind] then
error("invalid kind", 2)
end
_G[name] = function(value)
return api.callback(description, value)
end
end
--
-- Callback for all API functions; everything comes here first, and then
-- parceled out to the individual set...() functions.
--
function api.callback(field, value)
-- find the right target object for this field
local target
if field.scope == "project" then
target = api.scope.project or api.scope.solution
else
target = api.scope.configuration
end
if not target then
error("no " .. field.scope .. " in scope", 3)
end
-- find and call the setter for this field's value kind
local setter = api["set" .. field.kind]
setter(target, field, value)
end
--
-- Set a new string value on an API field.
--
function api.setstring(target, field, value)
end
-----------------------------------------------------------------------------
-- Everything below this point is a candidate for deprecation
-----------------------------------------------------------------------------
--
-- Here I define all of the getter/setter functions as metadata. The actual
-- functions are built programmatically below.
@ -807,6 +883,10 @@
cfg[name] = { }
end
end
-- this is the new place for storing scoped objects
api.scope.configuration = cfg
return cfg
end
@ -914,6 +994,9 @@
-- add an empty, global configuration to the project
configuration { }
-- this is the new place for storing scoped objects
api.scope.project = premake.CurrentContainer
return premake.CurrentContainer
end
@ -936,6 +1019,9 @@
-- add an empty, global configuration
configuration { }
-- this is the new place for storing scoped objects
api.scope.solution = premake.CurrentContainer
return premake.CurrentContainer
end

View File

@ -154,7 +154,22 @@
end
return result
end
--
-- Looks for an object within an array. Returns its index if found,
-- or nil if the object could not be found.
--
function table.indexof(tbl, obj)
local count = #tbl
for i = 1, count do
if tbl[i] == obj then
return i
end
end
end
--
@ -178,4 +193,4 @@
return result
end

139
tests/api/test_callback.lua Normal file
View File

@ -0,0 +1,139 @@
--
-- tests/api/test_callback.lua
-- Tests the main API value-setting callback.
-- Copyright (c) 2012 Jason Perkins and the Premake project
--
T.api_callback = {}
local suite = T.api_callback
local api = premake.api
--
-- Setup and teardown
--
function suite.setup()
api.settest = function(target, field, value)
test_args = {
["target"] = target,
["field"] = field,
["value"] = value
}
end
end
function suite.teardown()
_G["testapi"] = nil
test_args = nil
api.settest = nil
end
--
-- Verify that the callback hands off control to setter for
-- the field's value kind.
--
function suite.callsSetter_onFieldKind()
api.register { name = "testapi", kind = "test", scope = "project" }
solution "MySolution"
testapi "test"
test.isnotnil(test_args)
end
--
-- Verify that the field description is passed along to the setter.
--
function suite.setterGetsFieldDescription()
api.register { name = "testapi", kind = "test", scope = "project" }
solution "MySolution"
testapi "test"
test.isequal("testapi", test_args.field.name)
end
--
-- Verify that the value is passed along to the setter.
--
function suite.setterGetsFieldDescription()
api.register { name = "testapi", kind = "test", scope = "project" }
solution "MySolution"
testapi "test"
test.isequal("test", test_args.value)
end
--
-- If the field scope is "project", and there is no active solution
-- or project, an error should be raised.
--
function suite.errorRaised_onProjectScopeWithNoProject()
api.register { name = "testapi", kind = "test", scope = "project" }
ok, err = pcall(function ()
testapi "test"
end)
test.isfalse(ok)
end
--
-- If the field scope is "configuration" and there is no active configuration,
-- an error should be raised.
--
function suite.errorRaised_onConfigScopeWithNoConfig()
api.register { name = "testapi", kind = "test", scope = "configuration" }
ok, err = pcall(function ()
testapi "test"
end)
test.isfalse(ok)
end
--
-- If the field scope is "project" and there is an active solution, but not an
-- active project, the solution should be the target.
--
function suite.solutionTarget_onProjectScopeWithActiveSolution()
api.register { name = "testapi", kind = "test", scope = "project" }
local sln = solution "MySolution"
testapi "test"
test.istrue(sln == test_args.target)
end
--
-- If the field scope is "project" and there is an active project, it should
-- be the target.
--
function suite.projectTarget_onProjectScopeWithActiveProject()
api.register { name = "testapi", kind = "test", scope = "project" }
local sln = solution "MySolution"
local prj = project "MyProject"
testapi "test"
test.istrue(prj == test_args.target)
end
--
-- If the field scope is "configuration" and there is an active configuration,
-- it should be the target.
--
function suite.configTarget_onConfigScopeWithActiveConfig()
api.register { name = "testapi", kind = "test", scope = "configuration" }
local sln = solution "MySolution"
local cfg = configuration {}
testapi "test"
test.istrue(cfg == test_args.target)
end

View File

@ -0,0 +1,97 @@
--
-- tests/api/test_register.lua
-- Tests the new API registration function.
-- Copyright (c) 2012 Jason Perkins and the Premake project
--
T.api_register = {}
local suite = T.api_register
local api = premake.api
--
-- Setup and teardown
--
local callback_args
function suite.setup()
suite.callback = api.callback
api.callback = function(...) callback_args = arg end
end
function suite.teardown()
_G["testapi"] = nil
api.callback = suite.callback
callback_args = nil
end
--
-- Verify that the function exists.
--
function suite.registerFunctionExists()
test.isequal("function", type(premake.api.register))
end
--
-- When called, a new function with with provided name should
-- added to the global namespace.
--
function suite.createsNewGlobalFunction()
api.register { name = "testapi", kind = "string", scope = "project" }
test.isequal("function", type(testapi));
end
--
-- Verify that an error is raised if no name is provided.
--
function suite.raisesError_onMissingName()
ok, err = pcall(function ()
api.register { kind = "string", scope = "project" }
end)
test.isfalse(ok)
end
--
-- Verify that an error is raised if the name is already in use.
--
function suite.raisesError_onExistingGlobalName()
testapi = "testapi"
ok, err = pcall(function ()
api.register { name = "testapi", kind = "string", scope = "project" }
end)
test.isfalse(ok)
end
--
-- Verify that an error is raised if an invalid kind is used.
--
function suite.raisesError_onInvalidKind()
ok, err = pcall(function ()
api.register { name = "testapi", kind = "bogus", scope = "project" }
end)
test.isfalse(ok)
end
--
-- Verify that the central API callback is invoked by the registered function.
--
function suite.callbackInvoked_onApiCall()
api.register { name = "testapi", kind = "testkind", scope = "project" }
testapi "testvalue"
test.isnotnil(callback_args)
end

View File

@ -8,6 +8,8 @@
T.table = { }
local suite = T.table
local t
--
-- table.contains() tests
@ -44,6 +46,16 @@
end
--
-- table.indexof() tests
--
function suite.indexof_returnsIndexOfValueFound()
local idx = table.indexof({ "a", "b", "c" }, "b")
test.isequal(2, idx)
end
--
-- table.isempty() tests
--

View File

@ -75,6 +75,10 @@
dofile("config/test_objdir.lua")
dofile("config/test_targetinfo.lua")
-- API tests
dofile("api/test_callback.lua")
dofile("api/test_register.lua")
-- Baking tests
dofile("base/test_baking.lua")

View File

@ -197,13 +197,18 @@
-- Test execution function
--
local _OS_host = _OS
-- TODO: move this out to the test suite's premake4.lua so
-- this file can be reused for other projects
local function test_setup(suite, fn)
-- clear out some important globals
_ACTION = "test"
_ARGS = { }
_OPTIONS = { }
_OS = _OS_host
premake.solution.list = { }
premake.api.scope = { }
io.indent = nil
io.eol = "\n"