Tests: Add tests for image format saving and loading #104442

Closed
Jesse Yurkovich wants to merge 6 commits from deadpin:image-tests into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
4 changed files with 620 additions and 0 deletions
Showing only changes of commit 7c45daa405 - Show all commits

View File

@ -933,6 +933,49 @@ if(WITH_CODEC_FFMPEG)
)
endif()
if(NOT OPENIMAGEIO_IDIFF)
message(STATUS "Disabling ImBuf image format tests because OIIO idiff does not exist")
else()
SET(OPTIONAL_FORMATS "")
if(WITH_IMAGE_CINEON)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} CINEON")
endif()
if(WITH_IMAGE_HDR)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} HDR")
endif()
if(WITH_IMAGE_OPENEXR)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} OPENEXR")
endif()
if(WITH_IMAGE_OPENJPEG)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} OPENJPEG")
endif()
if(WITH_IMAGE_TIFF)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} TIFF")
endif()
if(WITH_IMAGE_WEBP)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} WEBP")
endif()
add_blender_test(
bf_imbuf_save
--python ${CMAKE_CURRENT_LIST_DIR}/bl_imbuf_save.py
--
-test_dir "${TEST_SRC_DIR}/imbuf_io"
-output_dir "${TEST_OUT_DIR}/imbuf_io/save"
-idiff "${OPENIMAGEIO_IDIFF}"
-optional_formats "${OPTIONAL_FORMATS}"
)
add_blender_test(
bf_imbuf_load
--python ${CMAKE_CURRENT_LIST_DIR}/bl_imbuf_load.py
--
-test_dir "${TEST_SRC_DIR}/imbuf_io"
-output_dir "${TEST_OUT_DIR}/imbuf_io/load"
-idiff "${OPENIMAGEIO_IDIFF}"
-optional_formats "${OPTIONAL_FORMATS}"
)
endif()
# ------------------------------------------------------------------------------
# SEQUENCER RENDER TESTS

View File

@ -0,0 +1,177 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import pathlib
import sys
import unittest
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
deadpin marked this conversation as resolved

We're trying to move away from os.path towards pathlib. I think removing the few usages in the new code in this patch is relatively straightforward.

We're trying to move away from os.path towards pathlib. I think removing the few usages in the new code in this patch is relatively straightforward.
from modules.imbuf_test import (
print_message,
AbstractImBufTest
)
args = None
class ImBufTest(AbstractImBufTest):
@classmethod
def setUpClass(cls):
AbstractImBufTest.init(args)
if cls.update:
os.makedirs(cls.reference_load_dir, exist_ok=True)
def _get_image_files(self, file_pattern):
return [f for f in pathlib.Path(self.reference_dir).glob(file_pattern)]
def _validate_metadata(self, img, ref_metadata_path, out_metadata_path):
channels = img.channels
is_float = img.is_float
colorspace = img.colorspace_settings.name
alpha_mode = img.alpha_mode
actual_metadata = f"{channels=} {is_float=} {colorspace=} {alpha_mode=}"
# Save actual metadata
with open(out_metadata_path, "w+") as meta:
deadpin marked this conversation as resolved

Can just be "w"?

Or better out_metadata_path.write_text(out_metadata_path) when using pathlib.

Can just be `"w"`? Or better `out_metadata_path.write_text(out_metadata_path)` when using `pathlib`.
meta.write(actual_metadata)
if os.path.exists(ref_metadata_path):
# Diff the metadata
expected_metadata = ""
try:
with open(ref_metadata_path, "r") as meta:
expected_metadata = meta.read()
failed = not (actual_metadata == expected_metadata)
except BaseException as e:
if self.verbose:
print_message(e.output.decode("utf-8", 'ignore'))
failed = True
else:
if not self.update:
return False
failed = True
if failed and self.update:
# Update reference metadata if requested.
with open(ref_metadata_path, "w+") as meta:
deadpin marked this conversation as resolved

Same comment.

Same comment.
meta.write(actual_metadata)
failed = False
return not failed
def _save_exr(self, img, out_exr_path):
scene = bpy.data.scenes[0]
image_settings = scene.render.image_settings
image_settings.file_format = "OPEN_EXR"
image_settings.color_mode = "RGBA"
image_settings.color_depth = "32"
image_settings.exr_codec = "ZIP"
img.save_render(str(out_exr_path), scene=scene)
def _validate_pixels(self, img, ref_exr_path, out_exr_path):
self._save_exr(img, out_exr_path)
return self.call_idiff(ref_exr_path, out_exr_path)
def check(self, file_pattern):
for image_path in self._get_image_files(file_pattern):
print_message(image_path.name, 'SUCCESS', 'RUN')
# Load the image under test
bpy.ops.image.open(filepath=str(image_path))
img = bpy.data.images[image_path.name]
# Compare the image with our exr/metadata references
exr_filename = image_path.with_suffix(".exr").name
metadata_filename = image_path.with_suffix(".txt").name
ref_exr_path = self.reference_load_dir.joinpath(exr_filename)
ref_metadata_path = self.reference_load_dir.joinpath(metadata_filename)
out_exr_path = self.output_dir.joinpath(exr_filename)
out_metadata_path = self.output_dir.joinpath(metadata_filename)
res1 = self._validate_metadata(img, ref_metadata_path, out_metadata_path)
res2 = self._validate_pixels(img, ref_exr_path, out_exr_path)
if not res1 or not res2:
self.errors += 1
print_message("Results are different from reference data")
print_message(image_path.name, 'FAILURE', 'FAILED')
else:
print_message(image_path.name, 'SUCCESS', 'OK')
class ImBufLoadTest(ImBufTest):
def test_load_bmp(self):
self.check("*.bmp")
def test_load_png(self):
self.check("*.png")
def test_load_exr(self):
self.skip_if_format_missing("OPENEXR")
self.check("*.exr")
def test_load_hdr(self):
self.skip_if_format_missing("HDR")
self.check("*.hdr")
def test_load_targa(self):
self.check("*.tga")
def test_load_tiff(self):
self.skip_if_format_missing("TIFF")
self.check("*.tif")
def test_load_jpeg(self):
self.check("*.jpg")
def test_load_jpeg2000(self):
self.skip_if_format_missing("OPENJPEG")
self.check("*.jp2")
self.check("*.j2c")
def test_load_dpx(self):
self.skip_if_format_missing("CINEON")
self.check("*.dpx")
def test_load_cineon(self):
self.skip_if_format_missing("CINEON")
self.check("*.cin")
def test_load_webp(self):
self.skip_if_format_missing("WEBP")
self.check("*.webp")
def main():
global args
import argparse
if '--' in sys.argv:
argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:]
else:
argv = sys.argv
parser = argparse.ArgumentParser()
parser.add_argument('-test_dir', required=True, type=pathlib.Path)
parser.add_argument('-output_dir', required=True, type=pathlib.Path)
parser.add_argument('-idiff', required=True, type=pathlib.Path)
parser.add_argument('-optional_formats', required=True)
args, remaining = parser.parse_known_args(argv)
unittest.main(argv=remaining)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,270 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import pathlib
import sys
import unittest
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from modules.imbuf_test import (
print_message,
AbstractImBufTest
)
args = None
TEMPLATE_RGBA08 = "template-rgba08.png"
TEMPLATE_RGBA32 = "template-rgba32.exr"
class ImBufTest(AbstractImBufTest):
@classmethod
def setUpClass(cls):
AbstractImBufTest.init(args)
if cls.update:
os.makedirs(cls.reference_dir, exist_ok=True)
def _load_template_image(self, name, template_name):
image_path = str(self.test_dir.joinpath(template_name))
bpy.ops.image.open(filepath=image_path)
img = bpy.data.images[template_name]
img.name = name
return img
def _setup_image(self, src, ext, settings):
scene = bpy.data.scenes[0]
image_settings = scene.render.image_settings
# Make an appropriate filename which embeds all relevant settings and
# set the file output parameters for the exact configuration we want
name = ""
for s in settings:
if s == "color_depth":
name += str(settings[s]).rjust(2, '0') + "-"
else:
name += str(settings[s]) + "-"
setattr(image_settings, s, settings[s])
image_name = name[:-1].lower() + "__from__" + src + "." + ext
return image_name
def _save_image(self, src, image_name):
loaders = {
"rgba08": lambda name: self._load_template_image(name, TEMPLATE_RGBA08),
"rgba32": lambda name: self._load_template_image(name, TEMPLATE_RGBA32),
}
# Load the template image and assign it the image name
img = loaders[src](image_name)
# Save the image in the desired format with the desired settings
scene = bpy.data.scenes[0]
ref_image_path = self.reference_dir.joinpath(img.name)
out_image_path = self.output_dir.joinpath(img.name)
img.save_render(str(out_image_path), scene=scene)
# Completely remove image in case it was modified during save
img.user_clear()
bpy.data.images.remove(img)
return ref_image_path, out_image_path
def _validate(self, ref_image_path, out_image_path):
return self.call_idiff(ref_image_path, out_image_path)
def check(self, src, ext, settings):
image_name = self._setup_image(src, ext, settings)
print_message(image_name, 'SUCCESS', 'RUN')
ref_image_path, out_image_path = self._save_image(src, image_name)
if not self._validate(ref_image_path, out_image_path):
self.errors += 1
print_message("Save result is different from reference image")
print_message(ref_image_path.name, 'FAILURE', 'FAILED')
else:
print_message(ref_image_path.name, 'SUCCESS', 'OK')
class ImBufSaveTest(ImBufTest):
def test_save_bmp(self):
self.check(src="rgba08", ext="bmp", settings={"file_format":"BMP", "color_mode":"BW"})
self.check(src="rgba08", ext="bmp", settings={"file_format":"BMP", "color_mode":"RGB"})
self.check(src="rgba32", ext="bmp", settings={"file_format":"BMP", "color_mode":"BW"})
self.check(src="rgba32", ext="bmp", settings={"file_format":"BMP", "color_mode":"RGB"})
def test_save_png(self):
self.check(src="rgba08", ext="png", settings={"file_format":"PNG", "color_mode":"BW", "color_depth":"8", "compression":15})
self.check(src="rgba08", ext="png", settings={"file_format":"PNG", "color_mode":"RGB", "color_depth":"8", "compression":15})
self.check(src="rgba08", ext="png", settings={"file_format":"PNG", "color_mode":"RGBA", "color_depth":"8", "compression":15})
self.check(src="rgba08", ext="png", settings={"file_format":"PNG", "color_mode":"BW", "color_depth":"16", "compression":25})
self.check(src="rgba08", ext="png", settings={"file_format":"PNG", "color_mode":"RGB", "color_depth":"16", "compression":25})
self.check(src="rgba08", ext="png", settings={"file_format":"PNG", "color_mode":"RGBA", "color_depth":"16", "compression":25})
self.check(src="rgba32", ext="png", settings={"file_format":"PNG", "color_mode":"BW", "color_depth":"8", "compression":15})
self.check(src="rgba32", ext="png", settings={"file_format":"PNG", "color_mode":"RGB", "color_depth":"8", "compression":15})
self.check(src="rgba32", ext="png", settings={"file_format":"PNG", "color_mode":"RGBA", "color_depth":"8", "compression":15})
self.check(src="rgba32", ext="png", settings={"file_format":"PNG", "color_mode":"BW", "color_depth":"16", "compression":25})
self.check(src="rgba32", ext="png", settings={"file_format":"PNG", "color_mode":"RGB", "color_depth":"16", "compression":25})
self.check(src="rgba32", ext="png", settings={"file_format":"PNG", "color_mode":"RGBA", "color_depth":"16", "compression":25})
def test_save_exr(self):
self.skip_if_format_missing("OPENEXR")
self.check(src="rgba08", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"BW", "color_depth":"16", "exr_codec":"ZIP"})
self.check(src="rgba08", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"RGB", "color_depth":"16", "exr_codec":"DWAA"})
self.check(src="rgba08", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"RGBA", "color_depth":"16", "exr_codec":"DWAB"})
self.check(src="rgba08", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"BW", "color_depth":"32", "exr_codec":"DWAB"})
self.check(src="rgba08", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"RGB", "color_depth":"32", "exr_codec":"DWAA"})
self.check(src="rgba08", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"RGBA", "color_depth":"32", "exr_codec":"ZIP"})
self.check(src="rgba32", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"BW", "color_depth":"16", "exr_codec":"ZIP"})
self.check(src="rgba32", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"RGB", "color_depth":"16", "exr_codec":"DWAA"})
self.check(src="rgba32", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"RGBA", "color_depth":"16", "exr_codec":"DWAB"})
self.check(src="rgba32", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"BW", "color_depth":"32", "exr_codec":"DWAB"})
self.check(src="rgba32", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"RGB", "color_depth":"32", "exr_codec":"DWAA"})
self.check(src="rgba32", ext="exr", settings={"file_format":"OPEN_EXR", "color_mode":"RGBA", "color_depth":"32", "exr_codec":"ZIP"})
def test_save_hdr(self):
self.skip_if_format_missing("HDR")
self.check(src="rgba08", ext="hdr", settings={"file_format":"HDR", "color_mode":"BW"})
self.check(src="rgba08", ext="hdr", settings={"file_format":"HDR", "color_mode":"RGB"})
self.check(src="rgba32", ext="hdr", settings={"file_format":"HDR", "color_mode":"BW"})
self.check(src="rgba32", ext="hdr", settings={"file_format":"HDR", "color_mode":"RGB"})
def test_save_targa(self):
self.check(src="rgba08", ext="tga", settings={"file_format":"TARGA", "color_mode":"BW"})
self.check(src="rgba08", ext="tga", settings={"file_format":"TARGA", "color_mode":"RGB"})
self.check(src="rgba08", ext="tga", settings={"file_format":"TARGA", "color_mode":"RGBA"})
self.check(src="rgba32", ext="tga", settings={"file_format":"TARGA", "color_mode":"BW"})
self.check(src="rgba32", ext="tga", settings={"file_format":"TARGA", "color_mode":"RGB"})
self.check(src="rgba32", ext="tga", settings={"file_format":"TARGA", "color_mode":"RGBA"})
def test_save_targa_raw(self):
self.check(src="rgba08", ext="tga", settings={"file_format":"TARGA_RAW", "color_mode":"BW"})
self.check(src="rgba08", ext="tga", settings={"file_format":"TARGA_RAW", "color_mode":"RGB"})
self.check(src="rgba08", ext="tga", settings={"file_format":"TARGA_RAW", "color_mode":"RGBA"})
self.check(src="rgba32", ext="tga", settings={"file_format":"TARGA_RAW", "color_mode":"BW"})
self.check(src="rgba32", ext="tga", settings={"file_format":"TARGA_RAW", "color_mode":"RGB"})
self.check(src="rgba32", ext="tga", settings={"file_format":"TARGA_RAW", "color_mode":"RGBA"})
def test_save_tiff(self):
self.skip_if_format_missing("TIFF")
self.check(src="rgba08", ext="tif", settings={"file_format":"TIFF", "color_mode":"BW", "color_depth":"8", "tiff_codec":"DEFLATE"})
self.check(src="rgba08", ext="tif", settings={"file_format":"TIFF", "color_mode":"RGB", "color_depth":"8", "tiff_codec":"LZW"})
self.check(src="rgba08", ext="tif", settings={"file_format":"TIFF", "color_mode":"RGBA", "color_depth":"8", "tiff_codec":"PACKBITS"})
self.check(src="rgba08", ext="tif", settings={"file_format":"TIFF", "color_mode":"BW", "color_depth":"16", "tiff_codec":"PACKBITS"})
self.check(src="rgba08", ext="tif", settings={"file_format":"TIFF", "color_mode":"RGB", "color_depth":"16", "tiff_codec":"LZW"})
self.check(src="rgba08", ext="tif", settings={"file_format":"TIFF", "color_mode":"RGBA", "color_depth":"16", "tiff_codec":"DEFLATE"})
self.check(src="rgba32", ext="tif", settings={"file_format":"TIFF", "color_mode":"BW", "color_depth":"8", "tiff_codec":"DEFLATE"})
self.check(src="rgba32", ext="tif", settings={"file_format":"TIFF", "color_mode":"RGB", "color_depth":"8", "tiff_codec":"LZW"})
self.check(src="rgba32", ext="tif", settings={"file_format":"TIFF", "color_mode":"RGBA", "color_depth":"8", "tiff_codec":"PACKBITS"})
self.check(src="rgba32", ext="tif", settings={"file_format":"TIFF", "color_mode":"BW", "color_depth":"16", "tiff_codec":"PACKBITS"})
self.check(src="rgba32", ext="tif", settings={"file_format":"TIFF", "color_mode":"RGB", "color_depth":"16", "tiff_codec":"LZW"})
self.check(src="rgba32", ext="tif", settings={"file_format":"TIFF", "color_mode":"RGBA", "color_depth":"16", "tiff_codec":"DEFLATE"})
def test_save_jpeg(self):
self.check(src="rgba08", ext="jpg", settings={"file_format":"JPEG", "color_mode":"BW", "quality":90})
self.check(src="rgba08", ext="jpg", settings={"file_format":"JPEG", "color_mode":"RGB", "quality":90})
self.check(src="rgba32", ext="jpg", settings={"file_format":"JPEG", "color_mode":"BW", "quality":70})
self.check(src="rgba32", ext="jpg", settings={"file_format":"JPEG", "color_mode":"RGB", "quality":70})
def test_save_jpeg2000(self):
self.skip_if_format_missing("OPENJPEG")
# Is there a better combination of settings we can use so there's not so many?
# Is this a good mix?
self.check(src="rgba08", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"BW", "color_depth":"8", "jpeg2k_codec":"JP2", "quality":90})
self.check(src="rgba08", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"BW", "color_depth":"12", "jpeg2k_codec":"JP2", "quality":90})
self.check(src="rgba08", ext="j2c", settings={"file_format":"JPEG2000", "color_mode":"BW", "color_depth":"16", "jpeg2k_codec":"J2K", "quality":90})
self.check(src="rgba08", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGB", "color_depth":"8", "jpeg2k_codec":"JP2", "quality":90})
self.check(src="rgba08", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGB", "color_depth":"12", "jpeg2k_codec":"JP2", "quality":90})
self.check(src="rgba08", ext="j2c", settings={"file_format":"JPEG2000", "color_mode":"RGB", "color_depth":"16", "jpeg2k_codec":"J2K", "quality":90})
self.check(src="rgba08", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"8", "jpeg2k_codec":"JP2", "quality":90})
self.check(src="rgba08", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"12", "jpeg2k_codec":"JP2", "quality":90})
self.check(src="rgba08", ext="j2c", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"16", "jpeg2k_codec":"J2K", "quality":90})
self.check(src="rgba32", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"BW", "color_depth":"8", "jpeg2k_codec":"JP2", "quality":70})
self.check(src="rgba32", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"BW", "color_depth":"12", "jpeg2k_codec":"JP2", "quality":70})
self.check(src="rgba32", ext="j2c", settings={"file_format":"JPEG2000", "color_mode":"BW", "color_depth":"16", "jpeg2k_codec":"J2K", "quality":70})
self.check(src="rgba32", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGB", "color_depth":"8", "jpeg2k_codec":"JP2", "quality":70})
self.check(src="rgba32", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGB", "color_depth":"12", "jpeg2k_codec":"JP2", "quality":70})
self.check(src="rgba32", ext="j2c", settings={"file_format":"JPEG2000", "color_mode":"RGB", "color_depth":"16", "jpeg2k_codec":"J2K", "quality":70})
self.check(src="rgba32", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"8", "jpeg2k_codec":"JP2", "quality":70})
self.check(src="rgba32", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"12", "jpeg2k_codec":"JP2", "quality":70})
self.check(src="rgba32", ext="j2c", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"16", "jpeg2k_codec":"J2K", "quality":70})
# Note: The 'use_jpeg2k_cinema_preset' option mandates very large images
# self.check(src="rgba08", ext="jpg", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"8", "jpeg2k_codec":"JP2", "use_jpeg2k_cinema_preset":True, "use_jpeg2k_cinema_48":False, "use_jpeg2k_ycc":False, "quality":70})
# self.check(src="rgba32", ext="jpg", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"8", "jpeg2k_codec":"JP2", "use_jpeg2k_cinema_preset":True, "use_jpeg2k_cinema_48":False, "use_jpeg2k_ycc":False, "quality":70})
self.check(src="rgba08", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"12", "jpeg2k_codec":"JP2", "use_jpeg2k_cinema_preset":False, "use_jpeg2k_cinema_48":True, "use_jpeg2k_ycc":False, "quality":70})
self.check(src="rgba32", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"12", "jpeg2k_codec":"JP2", "use_jpeg2k_cinema_preset":False, "use_jpeg2k_cinema_48":True, "use_jpeg2k_ycc":False, "quality":70})
self.check(src="rgba08", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"16", "jpeg2k_codec":"JP2", "use_jpeg2k_cinema_preset":False, "use_jpeg2k_cinema_48":False, "use_jpeg2k_ycc":True, "quality":70})
self.check(src="rgba32", ext="jp2", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"16", "jpeg2k_codec":"JP2", "use_jpeg2k_cinema_preset":False, "use_jpeg2k_cinema_48":False, "use_jpeg2k_ycc":True, "quality":70})
def test_save_dpx(self):
self.skip_if_format_missing("CINEON")
self.check(src="rgba08", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGB", "color_depth":"8", "use_cineon_log":False})
self.check(src="rgba08", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGB", "color_depth":"12", "use_cineon_log":False})
self.check(src="rgba08", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGB", "color_depth":"16", "use_cineon_log":False})
self.check(src="rgba08", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGBA", "color_depth":"10", "use_cineon_log":True})
self.check(src="rgba08", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGBA", "color_depth":"12", "use_cineon_log":True})
self.check(src="rgba08", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGBA", "color_depth":"16", "use_cineon_log":True})
self.check(src="rgba32", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGB", "color_depth":"8", "use_cineon_log":False})
self.check(src="rgba32", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGB", "color_depth":"12", "use_cineon_log":False})
self.check(src="rgba32", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGB", "color_depth":"16", "use_cineon_log":False})
self.check(src="rgba32", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGBA", "color_depth":"10", "use_cineon_log":True})
self.check(src="rgba32", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGBA", "color_depth":"12", "use_cineon_log":True})
self.check(src="rgba32", ext="dpx", settings={"file_format":"DPX", "color_mode":"RGBA", "color_depth":"16", "use_cineon_log":True})
def test_save_cineon(self):
self.skip_if_format_missing("CINEON")
# Note: idiff crashes on these files. Fixed with OIIO 2.4.5+
# self.check(src="rgba08", ext="cin", settings={"file_format":"CINEON", "color_mode":"RGB"})
# self.check(src="rgba32", ext="cin", settings={"file_format":"CINEON", "color_mode":"RGB"})
def test_save_webp(self):
self.skip_if_format_missing("WEBP")
self.check(src="rgba08", ext="webp", settings={"file_format":"WEBP", "color_mode":"RGB", "quality":90})
self.check(src="rgba08", ext="webp", settings={"file_format":"WEBP", "color_mode":"RGBA", "quality":90})
self.check(src="rgba32", ext="webp", settings={"file_format":"WEBP", "color_mode":"RGB", "quality":70})
self.check(src="rgba32", ext="webp", settings={"file_format":"WEBP", "color_mode":"RGBA", "quality":70})
def main():
global args
import argparse
if '--' in sys.argv:
argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:]
else:
argv = sys.argv
parser = argparse.ArgumentParser()
parser.add_argument('-test_dir', required=True, type=pathlib.Path)
parser.add_argument('-output_dir', required=True, type=pathlib.Path)
parser.add_argument('-idiff', required=True, type=pathlib.Path)
parser.add_argument('-optional_formats', required=True)
args, remaining = parser.parse_known_args(argv)
unittest.main(argv=remaining)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,130 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import pathlib
import shutil
import subprocess
import sys
import unittest
args = None
class COLORS_ANSI:
deadpin marked this conversation as resolved

Can we deduplicate this colored printing code with other test modules?

Can we deduplicate this colored printing code with other test modules?
RED = '\033[00;31m'
GREEN = '\033[00;32m'
ENDC = '\033[0m'
class COLORS_DUMMY:
RED = ''
GREEN = ''
ENDC = ''
COLORS = COLORS_DUMMY
def print_message(message, type=None, status=''):
if type == 'SUCCESS':
print(COLORS.GREEN, end="")
elif type == 'FAILURE':
print(COLORS.RED, end="")
status_text = ...
if status == 'RUN':
status_text = " RUN "
elif status == 'OK':
status_text = " OK "
elif status == 'PASSED':
status_text = " PASSED "
elif status == 'FAILED':
status_text = " FAILED "
else:
status_text = status
if status_text:
print("[{}]" . format(status_text), end="")
print(COLORS.ENDC, end="")
print(" {}" . format(message))
sys.stdout.flush()
class AbstractImBufTest(unittest.TestCase):
@classmethod
def init(cls, args):
cls.test_dir = pathlib.Path(args.test_dir)
cls.reference_dir = pathlib.Path(args.test_dir).joinpath("reference")
cls.reference_load_dir = pathlib.Path(args.test_dir).joinpath("reference_load")
cls.output_dir = pathlib.Path(args.output_dir)
cls.diff_dir = pathlib.Path(args.output_dir).joinpath("diff")
cls.idiff = pathlib.Path(args.idiff)
cls.optional_formats = args.optional_formats
os.makedirs(cls.diff_dir, exist_ok=True)
cls.errors = 0
cls.fail_threshold = 0.001
cls.fail_percent = 1
cls.verbose = os.environ.get("BLENDER_VERBOSE") is not None
cls.update = os.getenv('BLENDER_TEST_UPDATE') is not None
if os.environ.get("BLENDER_TEST_COLOR") is not None:
global COLORS, COLORS_ANSI
COLORS = COLORS_ANSI
def setUp(self):
self.errors = 0
print_message("")
def tearDown(self):
if self.errors > 0:
self.fail("{} errors encountered" . format(self.errors))
def skip_if_format_missing(self, format):
if self.optional_formats.find(format) < 0:
self.skipTest("format not available")
def call_idiff(self, ref_path, out_path):
ref_filepath = str(ref_path)
out_filepath = str(out_path)
out_name = out_path.name
if os.path.exists(ref_filepath):
# Diff images test with threshold.
command = (
str(self.idiff),
"-fail", str(self.fail_threshold),
"-failpercent", str(self.fail_percent),
ref_filepath,
out_filepath,
)
try:
subprocess.check_output(command)
failed = False
except subprocess.CalledProcessError as e:
if self.verbose:
print_message(e.output.decode("utf-8", 'ignore'))
failed = e.returncode != 1
else:
if not self.update:
return False
failed = True
if failed and self.update:
# Update reference image if requested.
shutil.copy(out_filepath, ref_filepath)
failed = False
# Generate diff image.
diff_img = str(self.diff_dir.joinpath(out_name + ".diff.png"))
command = (
str(self.idiff),
"-o", diff_img,
"-abs", "-scale", "16",
ref_filepath,
out_filepath
)
try:
subprocess.check_output(command)
except subprocess.CalledProcessError as e:
if self.verbose:
print_message(e.output.decode("utf-8", 'ignore'))
return not failed