diff --git a/src/base/api.lua b/src/base/api.lua index c309c10a..cc8ae393 100644 --- a/src/base/api.lua +++ b/src/base/api.lua @@ -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 diff --git a/src/base/table.lua b/src/base/table.lua index ff874aad..67a9b24e 100644 --- a/src/base/table.lua +++ b/src/base/table.lua @@ -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 - \ No newline at end of file + diff --git a/tests/api/test_callback.lua b/tests/api/test_callback.lua new file mode 100644 index 00000000..eb16bdb5 --- /dev/null +++ b/tests/api/test_callback.lua @@ -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 diff --git a/tests/api/test_register.lua b/tests/api/test_register.lua new file mode 100644 index 00000000..f63dff85 --- /dev/null +++ b/tests/api/test_register.lua @@ -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 diff --git a/tests/base/test_table.lua b/tests/base/test_table.lua index 2bf26ed1..90963c27 100644 --- a/tests/base/test_table.lua +++ b/tests/base/test_table.lua @@ -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 -- diff --git a/tests/premake4.lua b/tests/premake4.lua index 381bb3b6..f4b8cbaa 100644 --- a/tests/premake4.lua +++ b/tests/premake4.lua @@ -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") diff --git a/tests/testfx.lua b/tests/testfx.lua index 152f0e5d..722c44ff 100644 --- a/tests/testfx.lua +++ b/tests/testfx.lua @@ -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"