I made a plugin called Code Cop for running unit tests on my code. To do this, you create modules that contain a series of test cases. In this module you’ll at the very least be requiring the module that contains the code you’ll be running tests on.
Here’s an example of how this could look:
My problem with this is that TestHello and Hello in this example are both required, and then kept in memory indefinitely. If I make a change, they are still cached so I have to cut/paste the modules so Studio will reload them.
Currently I do the following on each of the test case modules to get around this:
function TestRunner:RefreshModulesInMemory()
local modules = self:_getTestModules()
for _, module in ipairs(modules) do
local clone = module:Clone()
clone.Parent = module.Parent
module:Destroy()
end
end
This bit of code runs before the tests, so all of those modules will be refreshed and have their new source loaded. The problem with this is the modules the tests require are not reloaded, or the modules those ones requires.
The only option I can think of that would be feasible is extending RefreshModulesInMemory() so it applies to every module in the game. Is there a better way to go about this? And is this even safe? I’m concerned this automatic cut/paste approach could result in code loss.
Yeah, I figured there wasn’t anything built in I could use to clear the cache. Was hoping there would be something a little cleaner than cloning in new copies of the modules but I guess this is the best available right now. It’ll work but that’s not going to play well with a team create environment at all if there’s more than one programmer working at a time, bummer
Not sure what you’re trying to do exactly, but when I’m dealing with modulescripts I’m running with my plugin, I do something like require(moduleScript:clone()) that way it runs a fresh version of the modulescript I just edited.
That does sound very promising. With my current approach of just recreating the test modules that would work really well. My biggest issue right now is that even if the test modules are cloned, the modules they require are not. I have all these modules that the plugin matches for, but then each one requires at least one other module.
Kind of stuck I guess. Looks like the only option I really have is to recreate every module in the game to make sure they’re not cached anymore.
If you dont mind being hacky, you could do something like this
if offlineMode then
require(moduleScript:clone())
else
require(moduleScript)
end
of course that could cause some issues depending on how you are setting up your stuff.
If you stuffed all the modulescripts in one place and then made the plugin copy/paste/delete all of the modulescripts at once, that might work for you.
If it’s a plugin, you can read the .Source property and thus do something like:
local function assertf(c,m,...)
if c then return end
error(m:format(...),0)
end
local loading,ERR = {},{}
local function customRequire(mod)
local cached = loading[mod]
while cached == false do wait() cached = loading[mod] end
assertf(cached ~= ERR,"Error while loading module")
if cached then return cached end
local s,e = loadstring(mod.Source)
assertf(s,"Parsing error for %s: %s", mod:GetFullName(), tostring(e))
loading[mod] = false
local env = setmetatable({
script = mod;
require = customRequire;
},{__index=getfenv()})
s,e = pcall(setfenv(s,env))
if not s then loading[mod] = ERR end
assertf(s,"Running error for %s: %s", mod:GetFullName(), tostring(e))
loading[mod] = e return e
end
customRequire(firstModule)
Clearing the cache is just emptying the loading table.