Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 4 additions & 47 deletions .github/workflows/buildtest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,17 @@ jobs:
id: get-dev-ref
run: |
git fetch --depth=1 origin dev
echo "devref=$(git rev-parse origin/dev)" >> $GITHUB_OUTPUT
echo "devref=$(git rev-parse origin/dev)" >> "$GITHUB_OUTPUT"
- name: Download Dev branch cache
id: download-dev-ref-cache
uses: dawidd6/action-download-artifact@3ecf4024886f219d9290351234889bfb45d1b9da
with:
name: cache-devref-${{ steps.get-dev-ref.outputs.devref }}
path: /tmp/cache/
if_no_artifact_found: warn
search_artifacts: true
# Dev ref cache contains the build list and build xmls. Use that one to keep tests reproducible
- name: Update static builds list from cache
if: ${{ steps.download-dev-ref-cache.outputs.found_artifact == 'true' }}
run: cat /tmp/cache/builds.txt > spec/builds.txt
- name: Download latest build list
if: ${{ steps.download-dev-ref-cache.outputs.found_artifact == 'false' }}
id: download-build-list
uses: dawidd6/action-download-artifact@3ecf4024886f219d9290351234889bfb45d1b9da
with:
name: builds.txt
path: /tmp/latestbuildlist/
workflow: updatebuildlist.yml
if_no_artifact_found: warn
search_artifacts: true
- name: Update static builds list
if: ${{ steps.download-dev-ref-cache.outputs.found_artifact == 'false' && steps.download-build-list.outputs.found_artifact == 'true' }}
run: cat /tmp/latestbuildlist/builds.txt > spec/builds.txt
- name: Download latest build xmls
if: ${{ steps.download-dev-ref-cache.outputs.found_artifact == 'false' }}
uses: dawidd6/action-download-artifact@3ecf4024886f219d9290351234889bfb45d1b9da
with:
name: build-xmls
name: corpus-cache-devref-${{ steps.get-dev-ref.outputs.devref }}
path: /tmp/cache/
if_no_artifact_found: warn
search_artifacts: true
- name: Calculate build xmls and differences between them
shell: bash
run: |
mkdir /tmp/cache || true # Make sure /tmp/cache exists. Ignore exit code
chmod -R 777 /tmp/cache && docker compose run -v '/tmp/cache/:/cache' -e 'CACHEDIR=/cache' busted-diff | tee /tmp/dockerlog
Expand All @@ -66,28 +42,9 @@ jobs:
with:
name: build-diff-output
path: /tmp/artefact
- name: Save used build list into cache
if: ${{ steps.download-dev-ref-cache.outputs.found_artifact == 'false' }}
run: cp spec/builds.txt /tmp/cache/
- name: Move xmls found in builds.txt to a new directory
if: ${{ steps.download-dev-ref-cache.outputs.found_artifact == 'false' && steps.download-build-list.outputs.found_artifact == 'true' }}
run: |
mkdir new-build-xmls
while IFS= read -r line; do
FILENAME="/tmp/cache/${line//[^a-zA-Z0-9]/}.xml"
if [ -f "$FILENAME" ]; then
mv "$FILENAME" "./new-build-xmls/"
fi
done < "spec/builds.txt"
- name: Upload new build xmls
if: ${{ steps.download-dev-ref-cache.outputs.found_artifact == 'false' && steps.download-build-list.outputs.found_artifact == 'true' }}
uses: actions/upload-artifact@v4
with:
name: build-xmls
path: './new-build-xmls/*'
- name: Upload dev ref cache
if: ${{ steps.download-dev-ref-cache.outputs.found_artifact == 'false' }}
uses: actions/upload-artifact@v4
with:
name: cache-devref-${{ steps.get-dev-ref.outputs.devref }}
name: corpus-cache-devref-${{ steps.get-dev-ref.outputs.devref }}
path: /tmp/cache/
37 changes: 0 additions & 37 deletions .github/workflows/updatebuildlist.yml

This file was deleted.

2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ While both can be ran locally it's recommended to use the provided docker image
PoB uses the [Busted](https://lunarmodules.github.io/busted/) framework to run its tests. Tests are stored under `spec/System` and run automatically when a PR is modified.
More tests can be added to this folder to test specific functionality, or new test builds can be added to ensure nothing changed that wasn't intended.

### Running tests
To run the build difference tests run `docker compose up busted-diff`. `busted-diff` first downloads the build corpus from `https://api.pob.codes/test-builds/corpus` and computes it alongside `spec/TestBuilds/` using the current working changes. Then it uses git to checkout the branch specified by `DEVREF` or `dev` by default and computes the same builds. Lastly it finds the differences in the outputs generated from both, does some post processing, and prints all of the information out. The downloaded builds and list stay with the calculated `DEVREF` cache and are reused together until that cache changes.

1. Install [Docker](https://www.docker.com/get-started)
2. Run `docker-compose up` from the command line
Expand Down
25 changes: 25 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,28 @@ services:
working_dir: /workdir
volumes:
- ./:/workdir:ro
busted-diff:
#build: .
image: ghcr.io/pathofbuildingcommunity/pathofbuilding-tests:latest
environment: #Where in the container the folders are stored
WORKDIR: /workdir
HOME: /tmp
DEVREF: ${DEVREF:-origin/dev}
HEADREF: $HEADREF
LUA_PATH: "/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua;;"
LUA_CPATH: "/usr/lib/lua/5.1/?.so;;"
container_name: busted-diff
tty: true
# Dependencies used by the existing build-diff scripts; the image provides LuaJIT.
command:
- /bin/sh
- -ec
- |
apk add --no-cache git parallel libxml2-utils lua5.1-posix lua5.1-curl zlib-dev
luarocks install lua-zlib 1.4-0
exec su -p nobody -s /bin/sh -c 'dos2unix < /workdir/spec/BuildDiff.sh | /bin/sh'
security_opt:
- no-new-privileges:true
working_dir: /workdir
volumes:
- ./:/workdir:ro
14 changes: 11 additions & 3 deletions spec/BuildDiff.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/bin/sh
set -eo pipefail
umask 0

# If external cache dir has not been defined keep it inside the container
Expand All @@ -17,14 +18,21 @@ git config --global --add advice.detachedHead false
if [[ ! -z "$HEADREF" ]]
then
git diff --no-color "$HEADREF" -- /tmp/workdir/.busted /tmp/workdir/src/HeadlessWrapper.lua /tmp/workdir/spec/ > /tmp/HeadPatch &&
git reset --hard "$HEADREF" && git clean -fd && git apply --allow-empty /tmp/HeadPatch
git reset --hard "$HEADREF" && git clean -fd && git apply --allow-empty --index /tmp/HeadPatch
fi

headsha=$(git rev-parse HEAD)
devsha=$(git rev-parse "$DEVREF")

# Keep the input corpus with the calculated base, as in the existing workflow.
if [[ ! -f "$CACHEDIR/$devsha" ]]; then
curl --fail --show-error --silent https://api.pob.codes/test-builds/corpus -o "$CACHEDIR/corpus.json"
luajit spec/FetchTestBuilds.lua "$CACHEDIR"
fi
cp "$CACHEDIR/builds.txt" spec/builds.txt

rm -rf /tmp/headsha && mkdir /tmp/headsha
rm /tmp/workdir/src/Settings.xml
rm -f /tmp/workdir/src/Settings.xml
cat /tmp/workdir/spec/builds.txt | dos2unix | parallel --will-cite --ungroup --pipe -N50 'LINKSBATCH="$(mktemp){#}"; cat > $LINKSBATCH; BUILDLINKS="$LINKSBATCH" BUILDCACHEPREFIX="/tmp/headsha" busted --lua=luajit -r generate' && \
BUILDCACHEPREFIX='/tmp/headsha' busted --lua=luajit -r generate && date > "/tmp/headsha/$headsha" && echo "[+] Build cache computed for $headsha (headsha)" || exit $?

Expand All @@ -34,7 +42,7 @@ then

# Keep new changes to tests related files
git diff --no-color "$DEVREF" -- /tmp/workdir/.busted /tmp/workdir/src/HeadlessWrapper.lua /tmp/workdir/spec/ > /tmp/DevPatch && \
git reset --hard "$DEVREF" && git clean -fd && git apply --allow-empty /tmp/DevPatch && \
git reset --hard "$DEVREF" && git clean -fd && git apply --allow-empty --index /tmp/DevPatch && \
cat /tmp/workdir/spec/builds.txt | dos2unix | parallel --will-cite --ungroup --pipe -N50 'LINKSBATCH="$(mktemp){#}"; cat > $LINKSBATCH; BUILDLINKS="$LINKSBATCH" BUILDCACHEPREFIX="$CACHEDIR" busted --lua=luajit -r generate' && \
BUILDCACHEPREFIX="$CACHEDIR" busted --lua=luajit -r generate && date > "$CACHEDIR/$devsha" && echo "[+] Build cache computed for $devsha (devsha)" || exit $?
fi
Expand Down
6 changes: 4 additions & 2 deletions spec/DiffOutput.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ local function buildOutputMap(filecontent)
local playerOutput = {}
local minionOutput = {}
for line in splitLines(filecontent) do
local key, val = line:match('PlayerStat stat="(.-)" value="(.-)"')
local stat = line:match('<PlayerStat%s+([^>]+)')
local key, val = stat and stat:match('stat="(.-)"'), stat and stat:match('value="(.-)"')
if key then
playerOutput[key] = val
else
local key,val = line:match('MinionStat stat="(.-)" value="(.-)"')
local stat = line:match('<MinionStat%s+([^>]+)')
local key, val = stat and stat:match('stat="(.-)"'), stat and stat:match('value="(.-)"')
if key then
minionOutput[key] = val
end
Expand Down
21 changes: 21 additions & 0 deletions spec/FetchTestBuilds.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- Store the API corpus alongside the calculated base cache, outside Git.
package.path = "runtime/lua/?.lua;" .. package.path
local json = require("dkjson")
local base64 = require("base64")
local zlib = require("zlib")
local cache = assert(arg[1])
local input = assert(io.open(cache .. "/corpus.json", "r"))
local corpus = assert(json.decode(input:read("*a")))
input:close()
assert(corpus.schemaVersion == 2 and #corpus.builds > 0 and #corpus.builds == corpus.count, "Invalid build corpus")
local list = assert(io.open(cache .. "/builds.txt", "w"))
for _, entry in ipairs(corpus.builds) do
assert(#entry.sha256 == 64 and entry.sha256:match("^%x+$"), "Invalid build filename")
local xml = zlib.inflate()(base64.decode(entry.code:gsub("-", "+"):gsub("_", "/")))
local output = assert(io.open(cache .. "/" .. entry.sha256 .. ".xml", "w"))
output:write(xml)
output:close()
list:write(entry.sha256, "\n")
end
list:close()
print("[+] Downloaded " .. corpus.count .. " builds from corpus " .. corpus.snapshotId)
25 changes: 12 additions & 13 deletions spec/GenerateBuilds.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@ local function fetchBuilds(path)
fileHnd:close()
for line in splitLines(fileText) do
if line ~= "" then
for j = 1, #buildSites.websiteList do
if line:match(buildSites.websiteList[j].matchURL) then
local filename = line:gsub('%W', '')

-- Load from cache if downloaded already
local fileHnd = io.open( (os.getenv("CACHEDIR") or "/tmp") .. "/" .. filename .. ".xml", "r")
if fileHnd then
coroutine.yield({ xml = fileHnd:read("*a"), filename = filename, link = line })
fileHnd:close()
else
local filename = line:gsub('%W', '')
-- Check cached XML before matching a download provider.
local fileHnd = io.open((os.getenv("CACHEDIR") or "/tmp") .. "/" .. filename .. ".xml", "r")
if fileHnd then
coroutine.yield({ xml = fileHnd:read("*a"), filename = filename, link = line })
fileHnd:close()
else
for j = 1, #buildSites.websiteList do
if line:match(buildSites.websiteList[j].matchURL) then
-- Throttle build downloads to 15 per 10 seconds
local timeSinceLastDL = GetTime() - lastDLtime
if timeSinceLastDL < 666 then
Expand All @@ -35,10 +34,10 @@ local function fetchBuilds(path)
print("Failed to download build: " .. line)
end
end)
break
elseif j == #buildSites.websiteList then
print("Failed to match provider for: " .. line)
end
break
elseif j == #buildSites.websiteList then
print("Failed to match provider for: " .. line)
end
end
end
Expand Down