Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use separate reference images per backend #3520

Merged
merged 19 commits into from
Jan 1, 2024
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
92 changes: 68 additions & 24 deletions .github/workflows/reference_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,10 @@ jobs:
julia --color=yes --project=monorepo -e 'using Pkg; Pkg.test("CairoMakie", coverage=true)'
&& echo "TESTS_SUCCESSFUL=true" >> $GITHUB_ENV
- name: Upload test Artifacts
if: matrix.version == '1'
uses: actions/upload-artifact@v4
with:
name: ReferenceImages_CairoMakie
path: ./CairoMakie/test/recorded_reference_images/
name: ReferenceImages_CairoMakie_${{ matrix.version }}
path: ./CairoMakie/test/reference_images/
- name: Fail after artifacts if tests failed
if: ${{ env.TESTS_SUCCESSFUL != 'true' }}
run: exit 1
Expand Down Expand Up @@ -100,27 +99,11 @@ jobs:
DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --color=yes --project=monorepo -e 'using Pkg; Pkg.test("GLMakie", coverage=true)'
&& echo "TESTS_SUCCESSFUL=true" >> $GITHUB_ENV
- name: Upload test Artifacts
if: matrix.version == '1'
uses: actions/upload-artifact@v4
with:
name: ReferenceImages_GLMakie
name: ReferenceImages_GLMakie_${{ matrix.version }}
path: |
./GLMakie/test/recorded_reference_images/
- name: Save number of missing refimages to file
if: matrix.version == '1'
env:
N_MISSING: ${{ steps.referencetests.outputs.n_missing_refimages }}
COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
run: |
mkdir -p ./n_missing
echo $N_MISSING > ./n_missing/n_missing_refimages
echo $COMMIT_SHA >> ./n_missing/n_missing_refimages
- name: Upload artifact with number of missing refimages
if: matrix.version == '1'
uses: actions/upload-artifact@v4
with:
name: n_missing_refimages
path: n_missing/
./GLMakie/test/reference_images/
- name: Fail after artifacts if tests failed
if: ${{ env.TESTS_SUCCESSFUL != 'true' }}
run: exit 1
Expand Down Expand Up @@ -163,15 +146,76 @@ jobs:
DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' julia --color=yes --project=monorepo -e 'using Pkg; Pkg.test("WGLMakie", coverage=true)'
&& echo "TESTS_SUCCESSFUL=true" >> $GITHUB_ENV
- name: Upload test Artifacts
if: matrix.version == '1'
uses: actions/upload-artifact@v4
with:
name: ReferenceImages_WGLMakie
path: ./WGLMakie/test/recorded_reference_images/
name: ReferenceImages_WGLMakie_${{ matrix.version }}
path: ./WGLMakie/test/reference_images/
- name: Fail after artifacts if tests failed
if: ${{ env.TESTS_SUCCESSFUL != 'true' }}
run: exit 1
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v3
with:
file: lcov.info

consolidation:
name: Merge artifacts
runs-on: ubuntu-20.04
if: ${{ always() }} # run even if any of the three backend test jobs failed
needs: [cairomakie, glmakie, wglmakie]
steps:
- uses: actions/download-artifact@v4
with:
name: ReferenceImages_WGLMakie_1
path: ./ReferenceImages/WGLMakie
- uses: actions/download-artifact@v4
with:
name: ReferenceImages_CairoMakie_1
path: ./ReferenceImages/CairoMakie
- uses: actions/download-artifact@v4
with:
name: ReferenceImages_GLMakie_1
path: ./ReferenceImages/GLMakie
- name: Consolidate reference image folders
run: |
baseDir="./ReferenceImages"

# Create new top-level directory for combined files
mkdir -p "./ReferenceImagesCombined"

# Copy the reference folder from GLMakie, it's the same for all backends
cp -r "${baseDir}/GLMakie/reference/." "./ReferenceImagesCombined/reference/"

# Initialize empty files for concatenation
> "./ReferenceImagesCombined/scores.tsv"
> "./ReferenceImagesCombined/new_files.txt"

# Loop through the directories and concatenate the files, and copy recorded folders
for dir in WGLMakie CairoMakie GLMakie; do
# Concatenate scores.tsv and new_files.txt
cat "${baseDir}/${dir}/scores.tsv" >> "./ReferenceImagesCombined/scores.tsv"
cat "${baseDir}/${dir}/new_files.txt" >> "./ReferenceImagesCombined/new_files.txt"

# Copy recorded folder
mkdir -p "./ReferenceImagesCombined/recorded/${dir}/"
cp -r "${baseDir}/${dir}/recorded/${dir}/." "./ReferenceImagesCombined/recorded/${dir}/"
done

echo "Files and folders have been successfully combined into ReferenceImagesCombined."
- uses: actions/upload-artifact@v4
with:
name: ReferenceImages
path: ./ReferenceImagesCombined/
- name: Save number of missing refimages to file
env:
COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
run: |
mkdir -p ./n_missing
N_MISSING=$(wc -l < ./ReferenceImagesCombined/new_files.txt)
echo $N_MISSING > ./n_missing/n_missing_refimages
echo $COMMIT_SHA >> ./n_missing/n_missing_refimages
- name: Upload artifact with number of missing refimages
uses: actions/upload-artifact@v4
with:
name: n_missing_refimages
path: n_missing/
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Manifest.toml

\.DS_Store

WGLMakie/test/recorded_reference_images/
GLMakie/test/recorded_reference_images/
CairoMakie/test/recorded_reference_images/
WGLMakie/test/reference_images/
GLMakie/test/reference_images/
CairoMakie/test/reference_images/
RPRMakie/test/recorded/

CairoMakie/src/display.svg
Expand Down
4 changes: 2 additions & 2 deletions CairoMakie/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ functions = [:volume, :volume!, :uv_mesh]
@testset "refimages" begin
CairoMakie.activate!(type = "png", px_per_unit = 1)
ReferenceTests.mark_broken_tests(excludes, functions=functions)
recorded_files, recording_dir = @include_reference_tests "refimages.jl"
recorded_files, recording_dir = @include_reference_tests CairoMakie "refimages.jl"
missing_images, scores = ReferenceTests.record_comparison(recording_dir)
ReferenceTests.test_comparison(scores; threshold = 0.032)
ReferenceTests.test_comparison(scores; threshold = 0.05)
end
14 changes: 2 additions & 12 deletions GLMakie/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,13 @@ end
include("unit_tests.jl")

@testset "Reference Tests" begin
n_missing_images = 0
@testset "refimages" begin
ReferenceTests.mark_broken_tests()
recorded_files, recording_dir = @include_reference_tests "refimages.jl"
recorded_files, recording_dir = @include_reference_tests GLMakie "refimages.jl" joinpath(@__DIR__, "glmakie_refimages.jl")
missing_images, scores = ReferenceTests.record_comparison(recording_dir)
n_missing_images += length(missing_images)
ReferenceTests.test_comparison(scores; threshold = 0.032)
ReferenceTests.test_comparison(scores; threshold = 0.05)
end

@testset "glmakie_refimages" begin
recorded_files, recording_dir = @include_reference_tests joinpath(@__DIR__, "glmakie_refimages.jl")
missing_images, scores = ReferenceTests.record_comparison(recording_dir)
n_missing_images += length(missing_images)
ReferenceTests.test_comparison(scores; threshold = 0.01)
end
GLMakie.closeall()
GC.gc(true) # make sure no finalizers act up!
# pass on status for Github Actions
println("::set-output name=n_missing_refimages::$n_missing_images")
end
6 changes: 2 additions & 4 deletions ReferenceTests/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
recorded/
refimages/
refimages.tar
glmakie_refimages/
glmakie_refimages.tar
reference_images/
reference_images.tar
20 changes: 12 additions & 8 deletions ReferenceTests/src/database.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,21 +88,25 @@ function mark_broken_tests(title_excludes = []; functions=[])
union!(SKIP_FUNCTIONS, functions)
end

macro include_reference_tests(path)
macro include_reference_tests(backend::Symbol, path, paths...)
toplevel_folder = dirname(string(__source__.file))
return esc(quote
using ReferenceTests: @reference_test
name = splitext(basename($(path)))[1]
include_path = isdir($path) ? $path : joinpath(@__DIR__, "tests", $path)
recording_dir = joinpath($toplevel_folder, "recorded_reference_images", name)
include_paths = map([$path, $(paths...)]) do p
isdir(p) ? p : joinpath(@__DIR__, "tests", p)
end
recording_dir = joinpath($toplevel_folder, "reference_images")
if isdir(recording_dir)
rm(recording_dir; force=true, recursive=true)
end
ReferenceTests.RECORDING_DIR[] = joinpath(recording_dir, "recorded")
mkpath(joinpath(recording_dir, "recorded"))
@testset "$name" begin
# prefix the recordings with the backend name so that each backend has its own versions
ReferenceTests.RECORDING_DIR[] = joinpath(recording_dir, "recorded", $(string(backend)))
mkpath(ReferenceTests.RECORDING_DIR[])
@testset "Reference tests $($(string(backend)))" begin
empty!(ReferenceTests.REGISTERED_TESTS)
include(include_path)
for include_path in include_paths
include(include_path)
end
end
recorded_files = collect(ReferenceTests.REGISTERED_TESTS)
recording_dir = recording_dir
Expand Down
8 changes: 4 additions & 4 deletions ReferenceTests/src/image_download.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ function last_major_version()
return "v" * string(VersionNumber(version.major, version.minor))
end

function download_refimages(tag=last_major_version(); name="refimages")
url = "https://github.com/MakieOrg/Makie.jl/releases/download/$(tag)/$(name).tar"
images_tar = basedir("$(name).tar")
images = basedir(name)
function download_refimages(tag=last_major_version())
url = "https://github.com/MakieOrg/Makie.jl/releases/download/$(tag)/reference_images.tar"
images_tar = basedir("reference_images.tar")
images = basedir("reference_images")
if isfile(images_tar)
if Bool(parse(Int, get(ENV, "REUSE_IMAGES_TAR", "0")))
@info "$images_tar already exists, skipping download as requested"
Expand Down
62 changes: 46 additions & 16 deletions ReferenceTests/src/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ function get_frames(a, b)
return (get_frames(a), get_frames(b))
end

function get_frames(video)
rgbf_convert(x::AbstractMatrix{<:RGB}) = convert(Matrix{RGBf}, x)
rgbf_convert(x::AbstractMatrix{<:RGBA}) = convert(Matrix{RGBAf}, x)

function get_frames(video::AbstractString)
mktempdir() do folder
afolder = joinpath(folder, "a")
mkpath(afolder)
Expand All @@ -20,31 +23,53 @@ function get_frames(video)
end
end

function compare_media(a::Matrix, b::Matrix; sigma=[1,1])
Images.test_approx_eq_sigma_eps(a, b, sigma, Inf)
function compare_images(a::AbstractMatrix{<:Union{RGB,RGBA}}, b::AbstractMatrix{<:Union{RGB,RGBA}})

a = rgbf_convert(a)
b = rgbf_convert(b)

if size(a) != size(b)
@warn "images don't have the same size, difference will be Inf"
return Inf
end

approx_tile_size_px = 30

range_dim1 = round.(Int, range(0, size(a, 1), length = ceil(Int, size(a, 1) / approx_tile_size_px)))
range_dim2 = round.(Int, range(0, size(a, 2), length = ceil(Int, size(a, 2) / approx_tile_size_px)))

boundary_iter(boundaries) = zip(boundaries[1:end-1] .+ 1, boundaries[2:end])

_norm(rgb1::RGBf, rgb2::RGBf) = sqrt(sum(((rgb1.r - rgb2.r)^2, (rgb1.g - rgb2.g)^2, (rgb1.b - rgb2.b)^2)))
_norm(rgba1::RGBAf, rgba2::RGBAf) = sqrt(sum(((rgba1.r - rgba2.r)^2, (rgba1.g - rgba2.g)^2, (rgba1.b - rgba2.b)^2, (rgba1.alpha - rgba2.alpha)^2)))

# compute the difference score as the maximum of the mean squared differences over the color
# values of tiles over the image. using tiles is a simple way to increase the local sensitivity
# without directly going to pixel-based comparison
# it also makes the scores more comparable between reference images of different sizes, because the same
# local differences would be normed to different mean scores if the images have different numbers of pixels
return maximum(Iterators.product(boundary_iter(range_dim1), boundary_iter(range_dim2))) do ((mi1, ma1), (mi2, ma2))
@views mean(_norm.(a[mi1:ma1, mi2:ma2], b[mi1:ma1, mi2:ma2]))
end
end

function compare_media(a, b; sigma=[1,1])
file, ext = splitext(a)
function compare_media(a::AbstractString, b::AbstractString)
_, ext = splitext(a)
if ext in (".png", ".jpg", ".jpeg", ".JPEG", ".JPG")
imga = load(a)
imgb = load(b)
if size(imga) != size(imgb)
@warn "images don't have the same size, difference will be Inf"
return Inf
end
conv(x) = convert(Matrix{RGBf}, x)
return compare_media(conv(imga), conv(imgb), sigma=sigma)
return compare_images(imga, imgb)
elseif ext in (".mp4", ".gif")
aframes, bframes = get_frames(a, b)
aframes = get_frames(a)
bframes = get_frames(b)
# Frames can differ in length, which usually shouldn't be the case but can happen
# when the implementation of record changes, or when the example changes its number of frames
# In that case, we just return inf + warn
if length(aframes) != length(bframes)
@warn "not the same number of frames in video, difference will be Inf"
return Inf
end
return mean(compare_media.(aframes, bframes; sigma=sigma))
return maximum(compare_images.(aframes, bframes))
else
error("Unknown media extension: $ext")
end
Expand All @@ -56,11 +81,16 @@ function get_all_relative_filepaths_recursively(dir)
end
end

function record_comparison(base_folder::String; record_folder_name="recorded", reference_name = basename(base_folder), tag=last_major_version())
function record_comparison(base_folder::String; record_folder_name="recorded", tag=last_major_version())
record_folder = joinpath(base_folder, record_folder_name)
reference_folder = download_refimages(tag; name=reference_name)
@info "Downloading reference images"
reference_folder = download_refimages(tag)
# we copy the reference images into the output folder, since we want to upload it all as an artifact, to know against what images we compared
cp(reference_folder, joinpath(base_folder, "reference"))
local_reference_copy_dir = joinpath(base_folder, "reference")
if isdir(local_reference_copy_dir)
rm(local_reference_copy_dir, recursive = true)
end
cp(reference_folder, local_reference_copy_dir)
testimage_paths = get_all_relative_filepaths_recursively(record_folder)
missing_refimages, scores = compare(testimage_paths, reference_folder, record_folder)

Expand Down
Loading