diff --git a/tools/nevermore-cli/build-scripts/combine-test-places.luau b/tools/nevermore-cli/build-scripts/combine-test-places.luau index 2f894e1570..e3ac74ad75 100644 --- a/tools/nevermore-cli/build-scripts/combine-test-places.luau +++ b/tools/nevermore-cli/build-scripts/combine-test-places.luau @@ -7,6 +7,11 @@ -- 1. Deserializes the .rbxl -- 2. Reparents ServerScriptService children (the package root) into the combined ServerScriptService -- 3. Reads the test script file and creates a Script under ReplicatedStorage._BatchScripts +-- +-- The injected script is the ONLY thing tagged `_BatchTest_`, because that tag is how +-- batch-test-runner picks what to execute and it can only execute one thing. This used to tag every +-- Script under ServerScriptService instead, which is the same script right up until a project has +-- two -- and then the runner ran whichever the tag happened to list first, silently testing nothing. local fs = require("@lune/fs") local process = require("@lune/process") @@ -40,17 +45,33 @@ local baseContents = fs.readFile(packages[1].rbxlPath) local game = roblox.deserializePlace(baseContents) local HttpService = game:GetService("HttpService") +local ReplicatedStorage = game:GetService("ReplicatedStorage") local ServerScriptService = game:GetService("ServerScriptService") ServerScriptService.LoadStringEnabled = true --- Process first package -for _, descendant in ServerScriptService:GetDescendants() do - if descendant:IsA("Script") then - descendant:AddTag("_BatchTest_" .. packages[1].slug) +local batchScripts = roblox.Instance.new("Folder") +batchScripts.Name = "_BatchScripts" +batchScripts.Parent = ReplicatedStorage + +-- Carries the package's entry point as source for the runner to loadstring. Never runs on its own: +-- a Script under ReplicatedStorage is inert, and the runner wants to choose when it runs anyway. +local function injectTestScript(pkg) + if not fs.isFile(pkg.scriptPath) then + print(`[CombinePlaces] No script template for {pkg.slug} at {pkg.scriptPath}`) + process.exit(1) end + + local testScript = roblox.Instance.new("Script") + testScript.Name = pkg.slug + testScript.Source = fs.readFile(pkg.scriptPath) + testScript:AddTag("_BatchTest_" .. pkg.slug) + testScript.Parent = batchScripts end +-- Process first package +injectTestScript(packages[1]) + -- Process remaining packages: deserialize each and reparent ServerScriptService children. -- NOTE: ObjectValues (links) within each package's subtree are preserved when the -- whole subtree is reparented as a unit. This works because all ObjectValue targets @@ -69,11 +90,7 @@ for idx = 2, #packages do HttpService.HttpEnabled = true end - for _, descendant in packageServerScriptService:GetDescendants() do - if descendant:IsA("Script") then - descendant:AddTag("_BatchTest_" .. pkg.slug) - end - end + injectTestScript(pkg) -- Reparent all ServerScriptService children (non-recursively, just move the roots) for _, child in packageServerScriptService:GetChildren() do diff --git a/tools/nevermore-cli/templates/batch-test-runner.luau b/tools/nevermore-cli/templates/batch-test-runner.luau index 2f66b85d95..413aeeb1ec 100644 --- a/tools/nevermore-cli/templates/batch-test-runner.luau +++ b/tools/nevermore-cli/templates/batch-test-runner.luau @@ -16,7 +16,15 @@ local scriptSources = {} for _, slug in packageSlugs do local tagged = CollectionService:GetTagged(`_BatchTest_{slug}`) local inst = tagged[1] - if inst then + if #tagged > 1 then + -- Refused rather than picked. Running whichever one the tag listed first is how this fails + -- quietly: a script that returns without erroring reports as a pass having tested nothing. + local fullNames = {} + for _, taggedInstance in tagged do + table.insert(fullNames, taggedInstance:GetFullName()) + end + warn(`[BatchTest] {slug}: {#tagged} instances tagged _BatchTest_{slug}: {table.concat(fullNames, ", ")}`) + elseif inst then scriptSources[slug] = inst.Source else warn(`[BatchTest] Failed to find script source for {slug}`) @@ -42,7 +50,7 @@ for _, slug in packageSlugs do local scriptSource = scriptSources[slug] if not scriptSource then print("===BATCH_TEST_BEGIN " .. slug .. "===") - warn(`[BatchTest] {slug}: No test script found for {slug}. Is it tagged with _BatchTest_{slug}?`) + warn(`[BatchTest] {slug}: No test script to run. Is exactly one instance tagged _BatchTest_{slug}?`) print("===BATCH_TEST_END " .. slug .. " FAIL 0===") table.insert(results, { slug = slug, success = false, durationMs = 0, error = "No test script found" }) continue