From bbccf2b18a9de1e547bf768908f9c9664c159ae3 Mon Sep 17 00:00:00 2001 From: prestidigitator Date: Fri, 1 Mar 2013 19:34:17 -0800 Subject: [PATCH] Initial code --- README.md | 200 +++++++++++++++++++- lib/ModLib.lua | 348 +++++++++++++++++++++++++++++++++++ test/ModLib_Version_test.lua | 41 +++++ test/ModLib_test.lua | 94 ++++++++++ test/lib/ModLib_list.txt | 4 + test/lib/MyLib_1-0.lua | 3 + test/lib/MyLib_1-1.lua | 3 + test/lib/MyLib_2-0-0.lua | 3 + test/lib/MyLib_2-0-5.lua | 3 + 9 files changed, 698 insertions(+), 1 deletion(-) create mode 100644 lib/ModLib.lua create mode 100644 test/ModLib_Version_test.lua create mode 100644 test/ModLib_test.lua create mode 100644 test/lib/ModLib_list.txt create mode 100644 test/lib/MyLib_1-0.lua create mode 100644 test/lib/MyLib_1-1.lua create mode 100644 test/lib/MyLib_2-0-0.lua create mode 100644 test/lib/MyLib_2-0-5.lua diff --git a/README.md b/README.md index 5899d84..9b5848d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,202 @@ minetest-lib-ModLib =================== -Library for Minetest mods and libraries to load other libraries with version requirements and one-time loading \ No newline at end of file +Library for Minetest mods and libraries to load other libraries with version +requirements and one-time loading + +General +======= + +This is NOT a "mod"; it is a LIBRARY for use by mods and other libraries. That +means it does not modify how Minetest works in any way, but provides a useful +API to help mods do so. This particular library is used purely for managing +the loading of other libraries, and has no dependencies. + +Installation +============ + +The file "lib/ModLib.lua" in this package should be installed in a "lib" +subdirectory of the main directory of the mod that depends on it. If other +mods under the same game also include versions of ModLib, only one instance of +any given version will be loaded. This is the same for any libraries that +ModLib is used to load as well. + +Other libraries the mod depends on, and that will be loaded using ModLib, +should also be included in this "lib" subdirectory. Their filenames must have +the form "LibName_version.lua". LibName is the name of the library, which must +start with a letter and contain only letters and digits. And version is a +version number like "x.y.z" (with any number of components) where the periods +are replaced with dashes ("x-y-z"). Due to a definiciency in the current +version of Lua, another file called "ModLib_list.txt" must exist in the "lib" +subdirectory and must contain the name of each library file on a separate line, +with no extra characters or empty lines. In Linux this list can be generated +using the command "ls -1 >ModLib_list.txt" from within the "lib" subdirectory. + +The ONLY exception to including the version number in the name of the library +file is for "ModLib.lua", and that is because it includes special boostrap code +and a version of it must be loadable from any mod that loads a library, without +depending on a specific version during bootstrap. + +For example, if a mod "my_mod" depends on LibMod 1.0 and version 2.3 of another +mod library "MyLib" and is installed to: + + .../mods/minetest/my_mod + +then the following files must exist: + + .../mods/minetest/my_mod/lib/ModLib_list.txt + .../mods/minetest/my_mod/lib/ModLib.lua + .../mods/minetest/my_mod/lib/MyLib_2-3.lua + +and ModLib_list.txt must contain the following lines: + + ModLib_list.txt + ModLib.lua + MyLib_2-3.lua + +(The inclusion of "ModLib_list.txt" and "ModLib.lua" in this file is optional.) + +Use +=== + +All libraries a mod depends on should be included with the mod. This ensures +these libraries are available to the mod whether or not they are also included +in other mods, and whether or not the version in any other mods is incompatible +with the version this mod needs. The exception would be if the mod depends on +another mod and uses a library the dependency includes in a limited fashion +rather than being directly dependent on details of the library API. + +In order to load one of these dependent libraries from your mod through ModLib, +first you must bootstrap ModLib, then add your "lib" subdirectory to its +library load path, then ask it to load your library, specifying what versions +of the library are compatible with your mod. For the above example using +ModLib 1.0 and MyLib 2.3 this would look like: + + -- Bootstrap ModLib + local MOD_NAME = minetest.get_current_modname() + local MOD_PATH = minetest.get_modpath(MOD_NAME) + local ModLib = dofile(MOD_PATH.."/lib/ModLib.lua") + + -- Add to the library path: + ModLib.addDir(MOD_PATH.."/lib") + + -- Load the dependent library. Note that this MyLib 2.3 OR LATER. If the + -- exact version were required, or if a range of versions were acceptable, + -- then a second (maximum) version number would be added to the call. + local MyLib = ModLib.load("MyLib", "2.3") + +Note carefully the use of local variable rather than globals. This MUST always +be how a mod or library loads another library, in order to allow multiple +versions to coexist. Note, however, that you may add the module API as a field +of the module's own API in order to use it from other files or even other mods +(see the note above about libraries included by dependent mods). So my_mod +could then do this: + + my_mod.MyLib = MyLib + +which would allow my_mod.MyLib to be accessed from any Lua file in the mod or +any other mod that depends on it. + +The use of ModLib from another library is only slightly different. The library +should assume that the mod that is loading it has added any library directories +to the load path already, so it can skip this step. So from a library this +looks like: + + -- Bootstrap ModLib + local LOADING_MOD = minetest.get_current_modname() + local LOADING_MOD_PATH = minetest.get_modpath(MOD_NAME) + local ModLib = dofile(LOADING_MOD_PATH.."/lib/ModLib.lua") + + -- Load the dependent library, with a minimum version of 1.0. + local MyOtherLib = ModLib.load("MyOtherLib", "1.0") + + local MyLib = {} + ... + return MyLib + +Now let's assume that MyLib is updated a few times and everything works up to +version 3.0, but then 3.1 comes along and gets included in a bunch of mods, and +we discover that this update breaks my_mod. Until we can update my_mod to +account for the changes, we make a one-line change to make sure it will not +load anything above version 3.0 for my_mod: + + local MyLib = ModLib.load("MyLib", "2.3", "3.0") + +The other modules that can use version 3.1 will continue to use it and will not +be affected by this change. + +Advanced Use +============ + +What if newer versions of ModLib come out, and your module depends on newer +features? Well, there must always be an unversioned "lib/ModLib.lua" that any +mod may load during bootstrap (from any mod that might load the library). +However, ModLib itself acts as another library that it can load by version, and +it is perfectly okay to have both the unversioned file name (with any actual +version in it) and versioned filenames in the load path. So just go ahead and +include your newer version of ModLib twice in the mod, with the two paths +"lib/ModLib.lua" and "lib/ModLib_x-y-z.lua". + +What about more complex version dependencies? The API currently allows a +minimum version of a library to be specified, a maximum version, or both. What +if there are multiple ranges, or one particular plagued version that we want to +exclude? In future versions of ModLib the API may allow for more complex +version specification, but for now you can take advantage of the builtin error +handling mechanism of Lua to successively try to load acceptable versions or +ranges. + +For example, take the above scenario and assume that MyLib comes out with +version 3.2 that fixes the issues my_mod had with version 3.1. How can we load +verison 3.2 if it is available, and otherwise load versions 2.3 through 3.0? +First, we could choose to obsolete use of the older versions and switch my_mod +to ship with version 3.2 instead of the old 2.3 to ensure it is available. But +we could also do this: + + local status, MyLib = pcall(ModLib.load, "MyLib", "3.2") + if not (status and MyLib) then + status, MyLib = pcall(ModLib.load, "2.3", "3.0") + end + if not (status and MyLib) then + error("No compatible version of MyLib found") + end + +Suggested Version Dependencies +============================== + +How specific you make the version requirements is really up to you. It depends +on how you want to balance stability versus flexibility. For maximum +stability, you should require only versions that you have thoroughly tested +your mod or library against. For more flexibility, allowing future library +versions that might fix defects or enhanced behavior can be enabled by only +requiring a minimum version, at least until such time as up update to the +library breaks things. + +A middle ground could be to require exact versions of unstable or uncertain +libraries, but allow libraries from more consistent developers to update +without limit, or until some large minor version number (e.g. 2.99999) but not +the next major version (e.g. 3.0). + +Testing +======= + +A couple of unit tests are included with ModLib. From the base directory of +the package, you can run the following, and should get the listed output: + + $ lua test/ModLib_test.lua + + ModLib tests PASSED + + $ lua test/ModLib_Version.lua + + ModLib.Version tests PASSED + +These tests can also be run within Minetest by including the ModLib library in +a mod in the normal way, adding the "test" directory and all contents from this +package as a subdirectory in the mod's main directory, and running the +following from the mod's code: + + local MOD_NAME = minetest.get_current_modname() + local MOD_PATH = minetest.get_modpath(MOD_NAME) + dofile(MOD_PATH.."test/ModLib_test.lua") + dofile(MOD_PATH.."test/ModLib_Version_test.lua") + diff --git a/lib/ModLib.lua b/lib/ModLib.lua new file mode 100644 index 0000000..fa362c9 --- /dev/null +++ b/lib/ModLib.lua @@ -0,0 +1,348 @@ +-- Constants that must NEVER change + +local REGISTRY_KEY = "ModLib:registry" +local LIB_DIRS_KEY = "ModLib:libdirs" +local THIS_LIB_NAME = "ModLib" + + +-- Constants for THIS version + +local THIS_VERSION_STR = "1.0" + +local LIB_NAME_PATT = "^[a-zA-Z][a-zA-Z0-9]*$" + +local LIB_FILENAME_PATT = "^([a-zA-Z][a-zA-Z0-9]*)_([0-9]+)()" +local LIB_FILENAME_EXTRA_PATT = "^%-([0-9]+)()" +local LIB_FILENAME_END_PATT = "^%.lua$" + +local HACKY_DIR_LIST_FILENAME = "ModLib_list.txt" + + +-- Class Version, used to store both a hash and sorted table of library +-- instances by canonicalized version number. + + +--- Type initialized from "x.y.z" version strings, able to compare + -- lexicographically, component by component (x, then y, then z, etc.). + -- For example, if these versions are defined: + -- + -- verA = ModLib.Version("1") + -- verB = ModLib.Version("1.0") + -- verC = ModLib.Version("1.0.0.3") + -- verD = ModLib.Version("1.1") + -- verE = ModLib.Version("2.0") + -- + -- then the following expression will evaluate to true: + -- + -- verA == verB and verB < verC and verC < verD and verD < verE + -- + -- Converting a version back to a string will return the "canonicalized" + -- version number by stripping all trailing zeroes (until there is only one + -- component remaining). For example, given the above definitions, all of the + -- following are true: + -- + -- tostring(verB) == "1" + -- tostring(verD) == "1.1" + -- tostring(ModLib.Version("0.0.0")) = "0" + -- tostring(ModLib.Version("0.1")) = "0.1" +local Version = {} +local Version_meta = {} +local Version_inst_meta = {} +setmetatable(Version, Version_meta) + +local function Version_constr(class, verstr) + self = {} + + local len = string.len(verstr) + local si = 1 + local ei, comp + while si <= len do + ei = string.find(verstr, ".", si, true) + if not ei then ei = len+1 end + if ei <= si or ei == len then + self = nil + break + end + comp = string.sub(verstr, si, ei-1) + if not string.find(comp, "^[0-9]+$") then + self = nil + break; + end + table.insert(self, tonumber(comp)) + si = ei+1 + end + if self then + while #self > 1 and self[#self] == 0 do + table.remove(self) + end + setmetatable(self, Version_inst_meta) + end + return self +end +Version_meta.__call = Version_constr + +local function Version_cmp(ver1, ver2) + local diff + for i = 1, math.min(#ver1, #ver2) do + diff = ver1[i] - ver2[i] + if diff > 0 then return 1 elseif diff < 0 then return -1 end + end + diff = #ver1 - #ver2 + if diff > 0 then return 1 elseif diff < 0 then return -1 end + return 0 +end +Version_inst_meta.__eq = function(ver1, ver2) + if #ver1 ~= #ver2 then return false end + return (Version_cmp(ver1, ver2) == 0) +end +Version_inst_meta.__lt = function(ver1, ver2) + return (Version_cmp(ver1, ver2) < 0) +end + +local function Version_concat(val1, val2) + if type(val1) == "table" then + val1 = tostring(val1) + end + if type(val2) == "table" then + val2 = tostring(val2) + end + return val1..val2 +end +Version_inst_meta.__concat = Version_concat + +local function Version_tostring(ver) + return table.concat(ver, ".") +end +Version_inst_meta.__tostring = Version_tostring + + +-- Bootstrap stuff, so we only "load" this library once + +local THIS_VERSION = Version(THIS_VERSION_STR) + +local registry = _G[REGISTRY_KEY] or {} +_G[REGISTRY_KEY] = registry + +local libDirs = _G[LIB_DIRS_KEY] or {} +_G[LIB_DIRS_KEY] = libDirs + +local modLibVersions = registry[THIS_LIB_NAME] or {} +registry[THIS_LIB_NAME] = modLibVersions +local modLibVersionsByValue = modLibVersions.byValue or {} +modLibVersions.byValue = modLibVersionsByValue +local modLibVersionsSorted = modLibVersions.sorted or {} +modLibVersions.sorted = modLibVersionsSorted + +local ModLib = modLibVersionsByValue[tostring(THIS_VERSION)] +if ModLib then return ModLib end + + +-- Finally the ModLib class itself, with Version enclosed for external use: + + +--- Used to load versions of libraries with version requirements, and load each + -- version only once. + -- + -- Example of use: + -- + -- local MOD_NAME = minetest.get_current_modname() + -- local MOD_PATH = minetest.get_modpath(MOD_NAME) + -- local ModLib = dofile(MOD_PATH.."/lib/ModLib_1-0.lua") + -- ModLib.addDir(MOD_PATH.."/lib") + -- local MyLib = ModLib.load("MyLib", "i.j.k", "m.n.p") + -- + -- where "i.j.k" and "m.n.p" are version strings indicating the minimum and + -- maximum versions of "MyLib" that can be used by the module (with any number + -- of components, not limited to the three shown here). This will attempt to + -- load a file called "MyLib_a-b-c.lua" from any of the directories added + -- through ModLib.addDir(dir) meeting the requirement that + -- "i.j.k" <= "a.b.c" <= "m.n.p" lexicographically (after trailing zero + -- components are stripped from each version string). If multiple files meet + -- the requirement, the one with greatest "a.b.c" will be used. To require an + -- exact version, use the same value for the minimum and maximum versions. To + -- require ANY version (the highest version will be used), omit both + -- versions (or use nil for them). To specify a half-open range, use nil for + -- either (or omit the maximum). + -- + -- These are the requirements for mod libraries: + -- + -- 1.) The library name must begin with a letter, and must contain only + -- letters and digits. + -- 2.) They MUST NOT set any global variables except in the rare case that it + -- is always going to be okay to share the value between ALL versions + -- of the library (note the local variables in the example above). In + -- particular, the "MyLib" variable here must NOT be set as a global + -- variable by the library, and must instead be a local variable returned + -- from the file. + -- 3.) They MUST return the main class of the library itself from the file. + -- 4.) They MAY optionally set MyLib.VERSION in the returned main class, but + -- this will be overwritten by ModLib with the canonicalized version + -- string "a.b.c", constructed from the name of the file the library is + -- loaded from. + -- + -- Note that ModLib itself is a mod library, so it could conceivably be used + -- to load a different version of itself after the initial bootstrap loading + -- (though it's unlikely you'd want to)! + -- + -- Here is an example of a dirt simple mod library that defines a field and a + -- function: + -- + -- -- MyDumbLib_1-0.lua + -- local MyDumbLib = {} + -- MyDumbLib.value = 1 + -- function MyDumbLib.foo() print("foo!") end + -- return MyDumbLib + -- + -- NOTE: Due to a deficiency in the Lua 5.1 standard library, directories + -- cannot be listed without the aid of a C extension to the API. Therefore, + -- you must create a file called "ModLib_list.txt" in each directory added + -- through ModLib.addDir(dir) that contains one simple filename per line of + -- the valid library files in that directory. This can be done in Linux using + -- the command "ls -1 >ModLib_list.txt" from within the directory. Hopefully + -- this library becomes a part of the Minetest core at some point, and all of + -- this can be done flawlessly from the C++ instead. +ModLib = {} +ModLib.Version = Version +ModLib.VERSION = tostring(THIS_VERSION) + +-- Returns index of the last array element from a sorted array less than or +-- equal to a value, zero if all elements of the table are greater than the +-- value or if the table is empty. +local function binarySearchUpperBound(table, value) + if #table < 1 then return 0 end + if table[#table] <= value then return #table end + if table[1] > value then return 0 end + + local si = 1 + local ei = #table + local mi = math.floor((si+ei)/2) + local mv = table[mi] + + while mi > si do + local mv = table[mi] + if mv <= value then + si = mi + else + ei = mi + end + mi = math.floor((si+ei)/2) + mv = table[mi] + end + + return si +end + +local function addLib(libName, ver, filePath) + local verStr = tostring(ver) + if not registry[libName].byValue[verStr] then + local loader = loadfile(filePath) + local lib = loader(libName) + lib.VERSION = tostring(ver) + + registry[libName].byValue[verStr] = lib + + local sorted = registry[libName].sorted + local pos = binarySearchUpperBound(sorted, ver) + if not pos then pos = 0 end + table.insert(sorted, pos+1, ver) + end +end + +local function ModLib_load(libName, minVerStr, maxVerStr) + local minVer = minVerStr and Version(minVerStr) + local maxVer = maxVerStr and Version(maxVerStr) + if minVerStr and not minVer then + error("Bad version string '"..minVerStr.."'", 2) + end + if maxVerStr and not maxVer then + error("Bad version string '"..maxVerStr.."'", 2) + end + if not string.match(libName, LIB_NAME_PATT) then + error("Bad library name '"..libName.."'", 2) + end + if minVer and maxVer and minVer > maxVer then + error("Min version greater than max version", 2) + end + + local libVersions = registry[libName] or {} + registry[libName] = libVersions + local libVersionsByValue = libVersions.byValue or {} + libVersions.byValue = libVersionsByValue + local libVersionsSorted = libVersions.sorted or {} + libVersions.sorted = libVersionsSorted + + if maxVer then + local lib = libVersionsByValue[tostring(maxVer)] + if lib then return lib end + end + + for i, dir in ipairs(libDirs) do + local listFile = io.open(dir.."/"..HACKY_DIR_LIST_FILENAME) + if listFile then + listFile:close() + for fileName in io.lines(dir.."/"..HACKY_DIR_LIST_FILENAME) do + local name, verStr, i = string.match(fileName, LIB_FILENAME_PATT) + if name and verStr and i then + while i <= #fileName do + local verComp, ni = string.match(fileName, + LIB_FILENAME_EXTRA_PATT, + i) + if verComp and ni then + verStr = verStr.."."..verComp + i = ni + else + break + end + end + if not string.match(fileName, LIB_FILENAME_END_PATT, i) then + name = nil + verStr = nil + end + end + if name and verStr and name == libName then + local ver = Version(verStr) + if (not minVer or ver >= minVer) and + (not maxVer or ver <= maxVer) + then + local filePath = dir.."/"..fileName + local file = io.open(filePath) + if file then + file:close() + addLib(libName, ver, filePath) + if maxVer and ver == maxVer then + return libVersionsByValue[tostring(maxVer)] + end + end + end + end + end + end + end + + local pos = (maxVer) and binarySearchUpperBound(libVersionsSorted, maxVer) + or #libVersionsSorted + local ver = (pos and pos >= 1 and pos <= #libVersionsSorted) + and libVersionsSorted[pos] + or nil + if not ver or (minVer and ver < minVer) then + local msg = "Library " + if minVer then msg = msg..tostring(minVer).." < " end + msg = msg.."'"..libName.."'" + if maxVer then msg = msg.." < "..tostring(maxVer) end + msg = msg.." not found" + error(msg, 2) + end + + return libVersionsByValue[tostring(ver)] +end +ModLib.load = ModLib_load + +local function ModLib_addDir(dir) + if not libDirs[dir] then + table.insert(libDirs, dir) + libDirs[dir] = #libDirs + end +end +ModLib.addDir = ModLib_addDir + +return ModLib diff --git a/test/ModLib_Version_test.lua b/test/ModLib_Version_test.lua new file mode 100644 index 0000000..e575eb9 --- /dev/null +++ b/test/ModLib_Version_test.lua @@ -0,0 +1,41 @@ +local BASE_DIR +if minetest then + local LOADING_MOD = minetest.get_current_modname() + BASE_DIR = minetest.get_modpath(LOADING_MOD) +else + BASE_DIR = os.getenv("PWD") +end + +local ModLib = dofile(BASE_DIR.."/lib/ModLib.lua") + +local verA = ModLib.Version("1") +local verB = ModLib.Version("1.0") +local verC = ModLib.Version("1.0.0.3") +local verD = ModLib.Version("1.1") +local verE = ModLib.Version("2.0") + +assert("1" == tostring(verA)) +assert("1" == tostring(verB)) +assert("1.0.0.3" == tostring(verC)) +assert("1.1" == tostring(verD)) +assert("2" == tostring(verE)) + +assert(verA == verB) +assert(verB < verC) +assert(verC < verD) +assert(verD < verE) + +assert(verA <= verB) +assert(verB <= verC) +assert(verC <= verD) +assert(verD <= verE) + +assert("0" == tostring(ModLib.Version("0.0.0"))) +assert("0.1" == tostring(ModLib.Version("0.1"))) + +local verF = ModLib.Version("3.14") +local verG = ModLib.Version("3.4") + +assert(verG < verF) + +print("ModLib.Version tests PASSED") diff --git a/test/ModLib_test.lua b/test/ModLib_test.lua new file mode 100644 index 0000000..0cf8f92 --- /dev/null +++ b/test/ModLib_test.lua @@ -0,0 +1,94 @@ +local BASE_DIR +if minetest then + local LOADING_MOD = minetest.get_current_modname() + BASE_DIR = minetest.get_modpath(LOADING_MOD) +else + BASE_DIR = os.getenv("PWD") +end + +local ModLib = dofile(BASE_DIR.."/lib/ModLib.lua") +ModLib.addDir(BASE_DIR.."/lib") +ModLib.addDir(BASE_DIR.."/test/lib") + +assert(not pcall(ModLib.load, "Zoog")) + +local MyLib1 = ModLib.load("MyLib", "1", "1") +local MyLib1b = ModLib.load("MyLib", "1.0", "1.0") + +assert(MyLib1) +assert(MyLib1b) +assert(MyLib1 == MyLib1b) +assert("1" == MyLib1.VERSION) +assert("1.0" == MyLib1.v) + +local MyLib1_1 = ModLib.load("MyLib", "1.1", "1.1") + +assert(MyLib1_1) +assert(MyLib1_1 ~= MyLib1) +assert("1.1" == MyLib1_1.VERSION) +assert("1.1" == MyLib1_1.v) + +local MyLib2 = ModLib.load("MyLib", "2.0.0.0.0.0", "2") + +assert(MyLib2) +assert(MyLib2 ~= MyLib1) +assert(MyLib2 ~= MyLib1_1) +assert("2" == MyLib2.VERSION) +assert("2.0.0" == MyLib2.v) + +local MyLib2_0_5 = ModLib.load("MyLib", "2.0.5", "2.0.5") + +assert(MyLib2_0_5) +assert(MyLib2_0_5 ~= MyLib1) +assert(MyLib2_0_5 ~= MyLib1_1) +assert(MyLib2_0_5 ~= MyLib2) +assert("2.0.5" == MyLib2_0_5.VERSION) +assert("2.0.5" == MyLib2_0_5.v) + +local MyLib_any = ModLib.load("MyLib") + +assert(MyLib_any) +assert(MyLib_any == MyLib2_0_5) + +local MyLib_min0_5 = ModLib.load("MyLib", "0.5") +local MyLib_min1_0_5 = ModLib.load("MyLib", "1.0.5") +local MyLib_min1_5 = ModLib.load("MyLib", "1.0.5") +local MyLib_min2 = ModLib.load("MyLib", "2") +assert(not pcall(ModLib.load, "MyLib", "3")) + +assert(MyLib_min0_5) +assert(MyLib_min1_0_5) +assert(MyLib_min1_5) +assert(MyLib_min2) +assert(MyLib_min0_5 == MyLib2_0_5) +assert(MyLib_min1_0_5 == MyLib2_0_5) +assert(MyLib_min1_5 == MyLib2_0_5) +assert(MyLib_min2 == MyLib2_0_5) + +assert(not pcall(ModLib.load, "MyLib", nil, "0.5")) +local MyLib_max1 = ModLib.load("MyLib", nil, "1") +local MyLib_max1_0_1 = ModLib.load("MyLib", nil, "1.0.1") +local MyLib_max1_1 = ModLib.load("MyLib", nil, "1.1") +local MyLib_max1_5 = ModLib.load("MyLib", nil, "1.5") +local MyLib_max2 = ModLib.load("MyLib", nil, "2") +local MyLib_max3 = ModLib.load("MyLib", nil, "3") + +assert(MyLib_max1 == MyLib1) +assert(MyLib_max1_0_1 == MyLib1) +assert(MyLib_max1_1 == MyLib1_1) +assert(MyLib_max1_5 == MyLib1_1) +assert(MyLib_max2 == MyLib2) +assert(MyLib_max3 == MyLib2_0_5) + +assert(not pcall(ModLib.load, "MyLib", "2", "1")) +local MyLib_1_to_2 = ModLib.load("MyLib", "1", "2") +local MyLib_1_1_to_2 = ModLib.load("MyLib", "1.1", "2") +local MyLib_2_to_3 = ModLib.load("MyLib", "2", "3") +assert(not pcall(ModLib.load, "MyLib", "2.5", "3")) + +assert(MyLib_1_to_2) +assert(MyLib_1_to_2 == MyLib2) +assert(MyLib_1_1_to_2 == MyLib2) +assert(MyLib_2_to_3 == MyLib2_0_5) + +print("ModLib tests PASSED") diff --git a/test/lib/ModLib_list.txt b/test/lib/ModLib_list.txt new file mode 100644 index 0000000..80c2a8f --- /dev/null +++ b/test/lib/ModLib_list.txt @@ -0,0 +1,4 @@ +MyLib_1-0.lua +MyLib_1-1.lua +MyLib_2-0-0.lua +MyLib_2-0-5.lua diff --git a/test/lib/MyLib_1-0.lua b/test/lib/MyLib_1-0.lua new file mode 100644 index 0000000..a174aa6 --- /dev/null +++ b/test/lib/MyLib_1-0.lua @@ -0,0 +1,3 @@ +local MyLib = {} +MyLib.v = "1.0" +return MyLib diff --git a/test/lib/MyLib_1-1.lua b/test/lib/MyLib_1-1.lua new file mode 100644 index 0000000..5603993 --- /dev/null +++ b/test/lib/MyLib_1-1.lua @@ -0,0 +1,3 @@ +local MyLib = {} +MyLib.v = "1.1" +return MyLib diff --git a/test/lib/MyLib_2-0-0.lua b/test/lib/MyLib_2-0-0.lua new file mode 100644 index 0000000..cfb2688 --- /dev/null +++ b/test/lib/MyLib_2-0-0.lua @@ -0,0 +1,3 @@ +local MyLib = {} +MyLib.v = "2.0.0" +return MyLib diff --git a/test/lib/MyLib_2-0-5.lua b/test/lib/MyLib_2-0-5.lua new file mode 100644 index 0000000..0396974 --- /dev/null +++ b/test/lib/MyLib_2-0-5.lua @@ -0,0 +1,3 @@ +local MyLib = {} +MyLib.v = "2.0.5" +return MyLib