2203 lines
73 KiB
Python
2203 lines
73 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
# -----------------------------------------------------------------------------
|
|
# Copyright 2020-2025 Arm Limited
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
# use this file except in compliance with the License. You may obtain a copy
|
|
# of the License at:
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
# -----------------------------------------------------------------------------
|
|
"""
|
|
The functional test runner is a set of tests that validate the ``astcenc``
|
|
command line is correctly handled, under both valid and invalid usage
|
|
scenarios. These tests do NOT validate the compression codec itself, beyond
|
|
some very basic incidental usage needed to validate the command line.
|
|
|
|
Due to the need to validate pixel colors in test images for both LDR and HDR
|
|
images, these tests rely on an HDRI-enabled build of ImageMagic being available
|
|
on the system path. To test if the version of ImageMagic on your system is
|
|
HDRI-enabled run:
|
|
|
|
convert --version
|
|
|
|
... and check that the string "HDRI" is present in the listed features.
|
|
|
|
Test Tiles
|
|
==========
|
|
|
|
Some basic test images, each 8x8 texels and built up from 4 no. 4x4 texel
|
|
constant color blocks, are used to help determine that the command line is
|
|
being processed correctly.
|
|
|
|
LDR Test Pattern
|
|
----------------
|
|
|
|
LDR images are an 8x8 image containing 4 4x4 constant color blocks. Assuming
|
|
(0, 0) is the top left (TL), the component uncompressed block colors are:
|
|
|
|
* (0, 0) TL = Black, opaque = (0.00, 0.00, 0.00, 1.00)
|
|
* (7, 0) TR = Red, opaque = (1.00, 0.00, 0.00, 1.00)
|
|
* (0, 7) BL = White, opaque = (1.00, 1.00, 1.00, 1.00)
|
|
* (7, 7) BR = Green, trans = (0.25, 0.75, 0.00, 0.87)
|
|
|
|
HDR Test Pattern
|
|
----------------
|
|
|
|
HDR images are an 8x8 image containing 4 4x4 constant color blocks. Assuming
|
|
(0, 0) is the top left (TL), the component uncompressed block colors are:
|
|
|
|
* (0, 0) TL = LDR Black, opaque = (0.00, 0.00, 0.00, 1.00)
|
|
* (7, 0) TR = HDR Red, opaque = (8.00, 0.00, 0.00, 1.00)
|
|
* (0, 7) BL = HDR White, opaque = (3.98, 3.98, 3.98, 1.00)
|
|
* (7, 7) BR = LDR Green, trans = (0.25, 0.75, 0.00, 0.87)
|
|
"""
|
|
|
|
import argparse
|
|
import filecmp
|
|
import os
|
|
import re
|
|
import signal
|
|
import string
|
|
import subprocess as sp
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
import unittest
|
|
|
|
import numpy
|
|
from PIL import Image
|
|
|
|
import testlib.encoder as te
|
|
import testlib.image as tli
|
|
|
|
# Enable these to always write out, irrespective of test result
|
|
ASTCENC_CLI_ALWAYS = False
|
|
ASTCENC_LOG_ALWAYS = False
|
|
|
|
# Enable these to write out on failure for positive tests
|
|
ASTCENC_CLI_ON_ERROR = True
|
|
ASTCENC_LOG_ON_ERROR = True
|
|
|
|
# Enable these to write out on failure for negative tests
|
|
ASTCENC_CLI_ON_ERROR_NEG = True
|
|
ASTCENC_LOG_ON_ERROR_NEG = True
|
|
|
|
# LDR test pattern
|
|
ASTCENC_TEST_PATTERN_LDR = {
|
|
"TL": (0.00, 0.00, 0.00, 1.00),
|
|
"TR": (1.00, 0.00, 0.00, 1.00),
|
|
"BL": (1.00, 1.00, 1.00, 1.00),
|
|
"BR": (0.25, 0.75, 0.00, 0.87)
|
|
}
|
|
|
|
# HDR test pattern
|
|
ASTCENC_TEST_PATTERN_HDR = {
|
|
"TL": (0.00, 0.00, 0.00, 1.00),
|
|
"TR": (8.00, 0.00, 0.00, 1.00),
|
|
"BL": (3.98, 3.98, 3.98, 1.00),
|
|
"BR": (0.25, 0.75, 0.00, 0.87)
|
|
}
|
|
|
|
LDR_RGB_PSNR_PATTERN = re.compile(r"\s*PSNR \(LDR-RGB\): (.*) dB")
|
|
|
|
g_TestEncoder = "avx2"
|
|
|
|
class CLITestBase(unittest.TestCase):
|
|
"""
|
|
Command line interface base class.
|
|
|
|
These tests are designed to test the command line is handled correctly.
|
|
They are not detailed tests of the codec itself; only basic sanity checks
|
|
that some type of processing occurred are used.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
encoder = te.Encoder2x(g_TestEncoder)
|
|
self.binary = encoder.binary
|
|
|
|
def setUp(self):
|
|
"""
|
|
Set up a test case.
|
|
|
|
Create a new temporary directory for output files.
|
|
"""
|
|
self.tempDir = tempfile.TemporaryDirectory()
|
|
|
|
def tearDown(self):
|
|
"""
|
|
Tear down a test case.
|
|
|
|
Clean up the temporary directory created for output files.
|
|
"""
|
|
self.tempDir.cleanup()
|
|
self.tempDir = None
|
|
|
|
@staticmethod
|
|
def get_ref_image_path(profile, mode, image):
|
|
"""
|
|
Get the path of a reference image on disk.
|
|
|
|
Args:
|
|
profile (str): The color profile.
|
|
mode (str): The type of image to load.
|
|
image (str): The image variant to load.
|
|
|
|
Returns:
|
|
str: The path to the test image file on disk.
|
|
"""
|
|
nameMux = {
|
|
"LDR": {
|
|
"input": "png",
|
|
"comp": "astc"
|
|
},
|
|
"LDRS": {
|
|
"input": "png",
|
|
"comp": "astc"
|
|
},
|
|
"HDR": {
|
|
"input": "exr",
|
|
"comp": "astc"
|
|
}
|
|
}
|
|
|
|
assert profile in nameMux.keys()
|
|
assert mode in nameMux["LDR"].keys()
|
|
|
|
scriptDir = os.path.dirname(__file__)
|
|
fileName = "%s-%s-1x1.%s" % (profile, image, nameMux[profile][mode])
|
|
return os.path.join(scriptDir, "Data", fileName)
|
|
|
|
def get_tmp_image_path(self, profile, mode):
|
|
"""
|
|
Get the path of a temporary output image on disk.
|
|
|
|
Temporary files are automatically cleaned up when the test tearDown
|
|
occurs.
|
|
|
|
Args:
|
|
profile (str): The color profile. "EXP" means explicit which means
|
|
the "mode" parameter is interpreted as a literal file
|
|
extension not a symbolic mode.
|
|
mode (str): The type of image to load.
|
|
|
|
Returns:
|
|
str: The path to the test image file on disk.
|
|
"""
|
|
# Handle explicit mode
|
|
if profile == "EXP":
|
|
tmpFile, tmpPath = tempfile.mkstemp(mode, dir=self.tempDir.name)
|
|
os.close(tmpFile)
|
|
os.remove(tmpPath)
|
|
return tmpPath
|
|
|
|
# Handle symbolic modes
|
|
nameMux = {
|
|
"LDR": {
|
|
"comp": ".astc",
|
|
"decomp": ".png",
|
|
"bad": ".foo"
|
|
},
|
|
"LDRS": {
|
|
"comp": ".astc",
|
|
"decomp": ".png",
|
|
"bad": ".foo"
|
|
},
|
|
"HDR": {
|
|
"comp": ".astc",
|
|
"decomp": ".exr",
|
|
"bad": ".foo"
|
|
}
|
|
}
|
|
|
|
assert profile in nameMux.keys()
|
|
assert mode in nameMux["LDR"].keys()
|
|
|
|
suffix = nameMux[profile][mode]
|
|
tmpFile, tmpPath = tempfile.mkstemp(suffix, dir=self.tempDir.name)
|
|
os.close(tmpFile)
|
|
os.remove(tmpPath)
|
|
return tmpPath
|
|
|
|
|
|
class CLIPTest(CLITestBase):
|
|
"""
|
|
Command line interface positive tests.
|
|
|
|
These tests are designed to test the command line is handled correctly.
|
|
They are not detailed tests of the codec itself; only basic sanity checks
|
|
that some type of processing occurred are used.
|
|
"""
|
|
|
|
def compare(self, image1, image2):
|
|
"""
|
|
Utility function to compare two images.
|
|
|
|
Note that this comparison tests only decoded color values; any file
|
|
metadata is ignored, and encoding methods are not compared.
|
|
|
|
Args:
|
|
image1 (str): Path to the first image.
|
|
image2 (str): Path to the second image.
|
|
|
|
Returns:
|
|
bool: ``True` if the images are the same, ``False`` otherwise.
|
|
"""
|
|
img1 = Image.open(image1)
|
|
img2 = Image.open(image2)
|
|
|
|
# Images must have same size
|
|
if img1.size != img2.size:
|
|
print("Size")
|
|
return False
|
|
|
|
# Images must have same number of color channels
|
|
if img1.getbands() != img2.getbands():
|
|
# ... unless the only different is alpha
|
|
self.assertEqual(img1.getbands(), ("R", "G", "B"))
|
|
self.assertEqual(img2.getbands(), ("R", "G", "B", "A"))
|
|
|
|
# ... and the alpha is always one
|
|
bands = img2.split()
|
|
alphaHist = bands[3].histogram()
|
|
self.assertEqual(sum(alphaHist[:-1]), 0)
|
|
|
|
# Generate a version of img2 without alpha
|
|
img2 = Image.merge("RGB", (bands[0], bands[1], bands[2]))
|
|
|
|
# Compute sum of absolute differences
|
|
dat1 = numpy.array(img1)
|
|
dat2 = numpy.array(img2)
|
|
sad = numpy.sum(numpy.abs(dat1 - dat2))
|
|
|
|
if sad != 0:
|
|
print(img1.load()[0, 0])
|
|
print(img2.load()[0, 0])
|
|
|
|
return sad == 0
|
|
|
|
def get_channel_rmse(self, image1, image2):
|
|
"""
|
|
Get the channel-by-channel root mean square error.
|
|
|
|
Args:
|
|
image1 (str): Path to the first image.
|
|
image2 (str): Path to the second image.
|
|
|
|
Returns:
|
|
tuple: Tuple of floats containing the RMSE per channel.
|
|
None: Images could not be compared because they are different size.
|
|
"""
|
|
img1 = Image.open(image1)
|
|
img2 = Image.open(image2)
|
|
|
|
# Images must have same size
|
|
if img1.size != img2.size:
|
|
return None
|
|
|
|
# Images must have same number of color channels
|
|
if img1.getbands() != img2.getbands():
|
|
# ... unless the only different is alpha
|
|
self.assertEqual(img1.getbands(), ("R", "G", "B"))
|
|
self.assertEqual(img2.getbands(), ("R", "G", "B", "A"))
|
|
|
|
# ... and the alpha is always one
|
|
bands = img2.split()
|
|
alphaHist = bands[3].histogram()
|
|
self.assertEqual(sum(alphaHist[:-1]), 0)
|
|
|
|
# Generate a version of img2 without alpha
|
|
img2 = Image.merge("RGB", (bands[0], bands[1], bands[2]))
|
|
|
|
# Compute root mean square error
|
|
img1bands = img1.split()
|
|
img2bands = img2.split()
|
|
|
|
rmseVals = []
|
|
imgBands = zip(img1bands, img2bands)
|
|
for img1Ch, img2Ch in imgBands:
|
|
imSz = numpy.prod(img1Ch.size)
|
|
dat1 = numpy.array(img1Ch)
|
|
dat2 = numpy.array(img2Ch)
|
|
|
|
sad = numpy.sum(numpy.square(dat1 - dat2))
|
|
mse = numpy.divide(sad, imSz)
|
|
rmse = numpy.sqrt(mse)
|
|
rmseVals.append(rmse)
|
|
|
|
return rmseVals
|
|
|
|
@staticmethod
|
|
def get_color_refs(mode, corners):
|
|
"""
|
|
Build a set of reference colors from apriori color list.
|
|
|
|
Args:
|
|
mode (str): The color mode (LDR, or HDR)
|
|
corners (str or list): The corner or list of corners -- named TL,
|
|
TR, BL, and BR -- to return.
|
|
|
|
Returns:
|
|
tuple: The color value, if corners was a name.
|
|
[tuple]: List of color values, if corners was a list of names.
|
|
"""
|
|
modes = {
|
|
"LDR": ASTCENC_TEST_PATTERN_LDR,
|
|
"HDR": ASTCENC_TEST_PATTERN_HDR
|
|
}
|
|
|
|
if isinstance(corners, str):
|
|
return [modes[mode][corners]]
|
|
|
|
return [modes[mode][corner] for corner in corners]
|
|
|
|
def assertColorSame(self, colorRef, colorNew, threshold=0.02, swiz=None):
|
|
"""
|
|
Test if a color is the similar to a reference.
|
|
|
|
Will trigger a test failure if the colors are not within threshold.
|
|
|
|
Args:
|
|
colorRef (tuple): The reference color to compare with.
|
|
colorNew (tuple): The new color.
|
|
threshold (float): The allowed deviation from colorRef (ratio).
|
|
swiz (str): The swizzle string (4 characters from the set
|
|
`rgba01`), applied to the reference color.
|
|
"""
|
|
self.assertEqual(len(colorRef), len(colorNew))
|
|
|
|
# Swizzle the reference color if needed
|
|
if swiz:
|
|
self.assertEqual(len(swiz), len(colorRef))
|
|
|
|
remap = {
|
|
"0": len(colorRef),
|
|
"1": len(colorRef) + 1
|
|
}
|
|
|
|
if len(colorRef) >= 1:
|
|
remap["r"] = 0
|
|
if len(colorRef) >= 2:
|
|
remap["g"] = 1
|
|
if len(colorRef) >= 3:
|
|
remap["b"] = 2
|
|
if len(colorRef) >= 4:
|
|
remap["a"] = 3
|
|
|
|
colorRefExt = list(colorRef) + [0.0, 1.0]
|
|
colorRef = [colorRefExt[remap[s]] for s in swiz]
|
|
|
|
for chRef, chNew in zip(colorRef, colorNew):
|
|
deltaMax = chRef * threshold
|
|
self.assertAlmostEqual(chRef, chNew, delta=deltaMax)
|
|
|
|
def exec(self, command, pattern=None):
|
|
"""
|
|
Execute a positive test.
|
|
|
|
Will trigger a test failure if the subprocess return code is any value
|
|
other than zero.
|
|
|
|
Args:
|
|
command (list(str)): The command to execute.
|
|
pattern (re.Pattern): The regex pattern to search for, must
|
|
contain a single group (this is returned to the caller). The
|
|
test will fail if no pattern match is found.
|
|
|
|
Returns:
|
|
str: The stdout output of the child process, or the first group
|
|
from the passed regex pattern.
|
|
"""
|
|
try:
|
|
result = sp.run(command, stdout=sp.PIPE, stderr=sp.PIPE,
|
|
universal_newlines=True, check=True)
|
|
error = False
|
|
except sp.CalledProcessError as ex:
|
|
result = ex
|
|
error = True
|
|
|
|
# Emit debug logging if needed
|
|
if ASTCENC_CLI_ALWAYS or (error and ASTCENC_CLI_ON_ERROR):
|
|
# Format for shell replay
|
|
print("\n" + " ".join(command))
|
|
# Format for script command list replay
|
|
print("\n" + ", ".join(("\"%s\"" % x for x in command)))
|
|
|
|
if ASTCENC_LOG_ALWAYS or (error and ASTCENC_LOG_ON_ERROR):
|
|
print(result.stdout)
|
|
|
|
rcode = result.returncode
|
|
|
|
if rcode < 0:
|
|
msg = "Exec died with signal %s" % signal.Signals(-rcode).name
|
|
self.assertGreaterEqual(rcode, 0, msg)
|
|
|
|
if rcode > 0:
|
|
msg = "Exec died with application error %u" % rcode
|
|
self.assertEqual(rcode, 0, msg)
|
|
|
|
# If there is a regex pattern provided, then search for it
|
|
if pattern:
|
|
match = pattern.search(result.stdout)
|
|
self.assertIsNotNone(match)
|
|
return match.group(1)
|
|
|
|
return result.stdout
|
|
|
|
def test_ldr_compress(self):
|
|
"""
|
|
Test basic LDR compression.
|
|
"""
|
|
imIn = self.get_ref_image_path("LDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("LDR", "comp")
|
|
imRef = self.get_ref_image_path("LDR", "comp", "A")
|
|
|
|
command = [self.binary, "-cl", imIn, imOut, "6x6", "-exhaustive"]
|
|
self.exec(command)
|
|
self.assertTrue(filecmp.cmp(imRef, imOut, False))
|
|
|
|
def test_srgb_compress(self):
|
|
"""
|
|
Test basic LDR sRGB compression.
|
|
"""
|
|
imIn = self.get_ref_image_path("LDRS", "input", "A")
|
|
imOut = self.get_tmp_image_path("LDRS", "comp")
|
|
imRef = self.get_ref_image_path("LDRS", "comp", "A")
|
|
|
|
command = [self.binary, "-cs", imIn, imOut, "6x6", "-exhaustive"]
|
|
self.exec(command)
|
|
self.assertTrue(filecmp.cmp(imRef, imOut, False))
|
|
|
|
def test_hdr_compress1(self):
|
|
"""
|
|
Test basic HDR + LDR alpha compression.
|
|
"""
|
|
imIn = self.get_ref_image_path("HDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("HDR", "comp")
|
|
imRef = self.get_ref_image_path("HDR", "comp", "A")
|
|
|
|
command = [self.binary, "-ch", imIn, imOut, "6x6", "-exhaustive"]
|
|
self.exec(command)
|
|
self.assertTrue(filecmp.cmp(imRef, imOut, False))
|
|
|
|
def test_hdr_compress2(self):
|
|
"""
|
|
Test basic HDR + HDR alpha compression.
|
|
"""
|
|
imIn = self.get_ref_image_path("HDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("HDR", "comp")
|
|
imRef = self.get_ref_image_path("HDR", "comp", "A")
|
|
|
|
command = [self.binary, "-cH", imIn, imOut, "6x6", "-exhaustive"]
|
|
self.exec(command)
|
|
self.assertTrue(filecmp.cmp(imRef, imOut, False))
|
|
|
|
def test_ldr_decompress(self):
|
|
"""
|
|
Test basic LDR decompression.
|
|
"""
|
|
imIn = self.get_ref_image_path("LDR", "comp", "A")
|
|
imOut = self.get_tmp_image_path("LDR", "decomp")
|
|
imRef = self.get_ref_image_path("LDR", "input", "A")
|
|
|
|
command = [self.binary, "-dl", imIn, imOut]
|
|
self.exec(command)
|
|
self.assertTrue(self.compare(imRef, imOut))
|
|
|
|
def test_srgb_decompress(self):
|
|
"""
|
|
Test basic LDR sRGB decompression.
|
|
"""
|
|
imIn = self.get_ref_image_path("LDRS", "comp", "A")
|
|
imOut = self.get_tmp_image_path("LDRS", "decomp")
|
|
imRef = self.get_ref_image_path("LDRS", "input", "A")
|
|
|
|
command = [self.binary, "-ds", imIn, imOut]
|
|
self.exec(command)
|
|
self.assertTrue(self.compare(imRef, imOut))
|
|
|
|
def test_hdr_decompress1(self):
|
|
"""
|
|
Test basic HDR + LDR alpha decompression.
|
|
"""
|
|
imIn = self.get_ref_image_path("HDR", "comp", "A")
|
|
imOut = self.get_tmp_image_path("HDR", "decomp")
|
|
imRef = self.get_ref_image_path("HDR", "input", "A")
|
|
|
|
command = [self.binary, "-dh", imIn, imOut]
|
|
self.exec(command)
|
|
|
|
colRef = tli.Image(imRef).get_colors((0, 0))
|
|
colOut = tli.Image(imOut).get_colors((0, 0))
|
|
self.assertColorSame(colRef, colOut)
|
|
|
|
def test_hdr_decompress2(self):
|
|
"""
|
|
Test basic HDR + HDR alpha decompression.
|
|
"""
|
|
imIn = self.get_ref_image_path("HDR", "comp", "A")
|
|
imOut = self.get_tmp_image_path("HDR", "decomp")
|
|
imRef = self.get_ref_image_path("HDR", "input", "A")
|
|
|
|
command = [self.binary, "-dH", imIn, imOut]
|
|
self.exec(command)
|
|
|
|
colRef = tli.Image(imRef).get_colors((0, 0))
|
|
colOut = tli.Image(imOut).get_colors((0, 0))
|
|
self.assertColorSame(colRef, colOut)
|
|
|
|
def test_ldr_roundtrip(self):
|
|
"""
|
|
Test basic LDR round-trip
|
|
"""
|
|
imIn = self.get_ref_image_path("LDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [self.binary, "-tl", imIn, imOut, "6x6", "-exhaustive"]
|
|
self.exec(command)
|
|
self.assertTrue(self.compare(imIn, imOut))
|
|
|
|
def test_srgb_roundtrip(self):
|
|
"""
|
|
Test basic LDR sRGB round-trip
|
|
"""
|
|
imIn = self.get_ref_image_path("LDRS", "input", "A")
|
|
imOut = self.get_tmp_image_path("LDRS", "decomp")
|
|
|
|
command = [self.binary, "-ts", imIn, imOut, "6x6", "-exhaustive"]
|
|
self.exec(command)
|
|
self.assertTrue(self.compare(imIn, imOut))
|
|
|
|
def test_hdr_roundtrip1(self):
|
|
"""
|
|
Test basic HDR + LDR alpha round-trip.
|
|
"""
|
|
imIn = self.get_ref_image_path("HDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("HDR", "decomp")
|
|
|
|
command = [self.binary, "-th", imIn, imOut, "6x6", "-exhaustive"]
|
|
self.exec(command)
|
|
colIn = tli.Image(imIn).get_colors((0, 0))
|
|
colOut = tli.Image(imOut).get_colors((0, 0))
|
|
self.assertColorSame(colIn, colOut)
|
|
|
|
def test_hdr_roundtrip2(self):
|
|
"""
|
|
Test basic HDR + HDR alpha round-trip.
|
|
"""
|
|
imIn = self.get_ref_image_path("HDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("HDR", "decomp")
|
|
|
|
command = [self.binary, "-tH", imIn, imOut, "6x6", "-exhaustive"]
|
|
self.exec(command)
|
|
colIn = tli.Image(imIn).get_colors((0, 0))
|
|
colOut = tli.Image(imOut).get_colors((0, 0))
|
|
self.assertColorSame(colIn, colOut)
|
|
|
|
def test_valid_2d_block_sizes(self):
|
|
"""
|
|
Test all valid block sizes are accepted (2D images).
|
|
"""
|
|
blockSizes = [
|
|
"4x4", "5x4", "5x5", "6x5", "6x6", "8x5", "8x6",
|
|
"10x5", "10x6", "8x8", "10x8", "10x10", "12x10", "12x12"
|
|
]
|
|
|
|
imIn = self.get_ref_image_path("LDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
for blk in blockSizes:
|
|
with self.subTest(blockSize=blk):
|
|
command = [self.binary, "-tl", imIn, imOut, blk, "-exhaustive"]
|
|
self.exec(command)
|
|
colIn = tli.Image(imIn).get_colors((0, 0))
|
|
colOut = tli.Image(imOut).get_colors((0, 0))
|
|
self.assertColorSame(colIn, colOut)
|
|
|
|
def test_valid_3d_block_sizes(self):
|
|
"""
|
|
Test all valid block sizes are accepted (3D images).
|
|
"""
|
|
blockSizes = [
|
|
"3x3x3",
|
|
"4x3x3", "4x4x3", "4x4x4",
|
|
"5x4x4", "5x5x4", "5x5x5",
|
|
"6x5x5", "6x6x5", "6x6x6"
|
|
]
|
|
|
|
imIn = self.get_ref_image_path("LDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
for blk in blockSizes:
|
|
with self.subTest(blockSize=blk):
|
|
command = [self.binary, "-tl", imIn, imOut, blk, "-exhaustive"]
|
|
self.exec(command)
|
|
colIn = tli.Image(imIn).get_colors((0, 0))
|
|
colOut = tli.Image(imOut).get_colors((0, 0))
|
|
self.assertColorSame(colIn, colOut)
|
|
|
|
def test_valid_presets(self):
|
|
"""
|
|
Test all valid presets are accepted
|
|
"""
|
|
presets = ["-fastest", "-fast", "-medium",
|
|
"-thorough", "-verythorough", "-exhaustive"]
|
|
|
|
imIn = self.get_ref_image_path("LDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
for preset in presets:
|
|
with self.subTest(preset=preset):
|
|
command = [self.binary, "-tl", imIn, imOut, "4x4", preset]
|
|
self.exec(command)
|
|
colIn = tli.Image(imIn).get_colors((0, 0))
|
|
colOut = tli.Image(imOut).get_colors((0, 0))
|
|
self.assertColorSame(colIn, colOut)
|
|
|
|
def test_valid_ldr_input_formats(self):
|
|
"""
|
|
Test valid LDR input file formats.
|
|
"""
|
|
imgFormats = ["bmp", "dds", "jpg", "ktx", "png", "tga"]
|
|
|
|
for imgFormat in imgFormats:
|
|
with self.subTest(imgFormat=imgFormat):
|
|
imIn = "./Test/Data/Tiles/ldr.%s" % imgFormat
|
|
imOut = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [self.binary, "-tl", imIn, imOut, "4x4", "-fast"]
|
|
self.exec(command)
|
|
|
|
# Check colors if image wrapper supports it
|
|
if tli.Image.is_format_supported(imgFormat):
|
|
colIn = tli.Image(imIn).get_colors((7, 7))
|
|
colOut = tli.Image(imOut).get_colors((7, 7))
|
|
|
|
# Catch exception and add fallback for tga handling
|
|
# having unstable origin in ImageMagick
|
|
try:
|
|
self.assertColorSame(colIn, colOut)
|
|
continue
|
|
except AssertionError as ex:
|
|
if imgFormat != "tga":
|
|
raise ex
|
|
|
|
# Try yflipped TGA image
|
|
colIn = tli.Image(imIn).get_colors((7, 7))
|
|
colOut = tli.Image(imOut).get_colors((7, 1))
|
|
self.assertColorSame(colIn, colOut)
|
|
|
|
def test_valid_uncomp_ldr_output_formats(self):
|
|
"""
|
|
Test valid uncompressed LDR output file formats.
|
|
"""
|
|
imgFormats = ["bmp", "dds", "ktx", "png", "tga"]
|
|
|
|
for imgFormat in imgFormats:
|
|
with self.subTest(imgFormat=imgFormat):
|
|
imIn = self.get_ref_image_path("LDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("EXP", ".%s" % imgFormat)
|
|
|
|
command = [self.binary, "-tl", imIn, imOut, "4x4", "-fast"]
|
|
self.exec(command)
|
|
|
|
# Check colors if image wrapper supports it
|
|
if tli.Image.is_format_supported(imgFormat):
|
|
colIn = tli.Image(imIn).get_colors((7, 7))
|
|
colOut = tli.Image(imOut).get_colors((7, 7))
|
|
self.assertColorSame(colIn, colOut)
|
|
|
|
def test_valid_comp_ldr_output_formats(self):
|
|
"""
|
|
Test valid compressed LDR output file formats.
|
|
"""
|
|
imgFormats = ["astc", "ktx"]
|
|
|
|
for imgFormat in imgFormats:
|
|
with self.subTest(imgFormat=imgFormat):
|
|
imIn = self.get_ref_image_path("LDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("EXP", ".%s" % imgFormat)
|
|
imOut2 = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [self.binary, "-cl", imIn, imOut, "4x4", "-fast"]
|
|
self.exec(command)
|
|
|
|
command = [self.binary, "-dl", imOut, imOut2]
|
|
self.exec(command)
|
|
|
|
# Check colors if image wrapper supports it
|
|
if tli.Image.is_format_supported(imgFormat):
|
|
colIn = tli.Image(imIn).get_colors((7, 7))
|
|
colOut = tli.Image(imOut2).get_colors((7, 7))
|
|
self.assertColorSame(colIn, colOut2)
|
|
|
|
def test_valid_hdr_input_formats(self):
|
|
"""
|
|
Test valid HDR input file formats.
|
|
"""
|
|
imgFormats = ["exr", "hdr"]
|
|
|
|
for imgFormat in imgFormats:
|
|
with self.subTest(imgFormat=imgFormat):
|
|
imIn = "./Test/Data/Tiles/hdr.%s" % imgFormat
|
|
imOut = self.get_tmp_image_path("HDR", "decomp")
|
|
|
|
command = [self.binary, "-th", imIn, imOut, "4x4", "-fast"]
|
|
self.exec(command)
|
|
|
|
# Check colors if image wrapper supports it
|
|
if tli.Image.is_format_supported(imgFormat, profile="hdr"):
|
|
colIn = tli.Image(imIn).get_colors((7, 7))
|
|
colOut = tli.Image(imOut).get_colors((7, 7))
|
|
self.assertColorSame(colIn, colOut)
|
|
|
|
def test_valid_uncomp_hdr_output_formats(self):
|
|
"""
|
|
Test valid uncompressed HDR output file formats.
|
|
"""
|
|
imgFormats = ["dds", "exr", "hdr", "ktx"]
|
|
|
|
for imgFormat in imgFormats:
|
|
with self.subTest(imgFormat=imgFormat):
|
|
imIn = self.get_ref_image_path("HDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("EXP", ".%s" % imgFormat)
|
|
|
|
command = [self.binary, "-th", imIn, imOut, "4x4", "-fast"]
|
|
self.exec(command)
|
|
|
|
# Check colors if image wrapper supports it
|
|
if tli.Image.is_format_supported(imgFormat, profile="hdr"):
|
|
colIn = tli.Image(imIn).get_colors((7, 7))
|
|
colOut = tli.Image(imOut).get_colors((7, 7))
|
|
self.assertColorSame(colIn, colOut)
|
|
|
|
def test_valid_comp_hdr_output_formats(self):
|
|
"""
|
|
Test valid compressed HDR output file formats.
|
|
"""
|
|
imgFormats = ["astc", "ktx"]
|
|
|
|
for imgFormat in imgFormats:
|
|
with self.subTest(imgFormat=imgFormat):
|
|
imIn = self.get_ref_image_path("HDR", "input", "A")
|
|
imOut = self.get_tmp_image_path("EXP", ".%s" % imgFormat)
|
|
imOut2 = self.get_tmp_image_path("HDR", "decomp")
|
|
|
|
command = [self.binary, "-ch", imIn, imOut, "4x4", "-fast"]
|
|
self.exec(command)
|
|
|
|
command = [self.binary, "-dh", imOut, imOut2]
|
|
self.exec(command)
|
|
|
|
# Check colors if image wrapper supports it
|
|
if tli.Image.is_format_supported(imgFormat):
|
|
colIn = tli.Image(imIn).get_colors((7, 7))
|
|
colOut = tli.Image(imOut2).get_colors((7, 7))
|
|
self.assertColorSame(colIn, colOut2)
|
|
|
|
def test_compress_normal_psnr(self):
|
|
"""
|
|
Test compression of normal textures using PSNR error metrics.
|
|
"""
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [
|
|
self.binary, "-tl",
|
|
"./Test/Images/Small/LDR-XY/ldr-xy-00.png",
|
|
decompFile, "5x5", "-exhaustive"]
|
|
|
|
refdB = float(self.exec(command, LDR_RGB_PSNR_PATTERN))
|
|
|
|
command.append("-normal")
|
|
testdB = float(self.exec(command, LDR_RGB_PSNR_PATTERN))
|
|
|
|
# Note that this test simply asserts that the "-normal_psnr" is
|
|
# connected and affects the output. We don't test it does something
|
|
# useful; that it outside the scope of this test case.
|
|
self.assertNotEqual(refdB, testdB)
|
|
|
|
def test_compress_normal_percep(self):
|
|
"""
|
|
Test compression of normal textures using perceptual error metrics.
|
|
"""
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [
|
|
self.binary, "-tl",
|
|
"./Test/Images/Small/LDR-XY/ldr-xy-00.png",
|
|
decompFile, "4x4", "-exhaustive"]
|
|
|
|
refdB = float(self.exec(command, LDR_RGB_PSNR_PATTERN))
|
|
|
|
command.append("-normal")
|
|
command.append("-perceptual")
|
|
testdB = float(self.exec(command, LDR_RGB_PSNR_PATTERN))
|
|
|
|
# Note that this test simply asserts that the "-normal -percep" is
|
|
# connected and affects the output. We don't test it does something
|
|
# useful; that it outside the scope of this test case.
|
|
self.assertNotEqual(refdB, testdB)
|
|
|
|
def test_compress_esw(self):
|
|
"""
|
|
Test compression swizzles.
|
|
"""
|
|
# The swizzles to test
|
|
swizzles = ["rgba", "g0r1", "rrrg"]
|
|
|
|
# Compress a swizzled image
|
|
for swizzle in swizzles:
|
|
with self.subTest(swizzle=swizzle):
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [
|
|
self.binary, "-tl",
|
|
"./Test/Data/Tiles/ldr.png",
|
|
decompFile, "4x4", "-exhaustive",
|
|
"-esw", swizzle]
|
|
|
|
self.exec(command)
|
|
|
|
# Fetch the three color
|
|
img = tli.Image(decompFile)
|
|
colorVal = img.get_colors([(7, 7)])
|
|
colorRef = self.get_color_refs("LDR", "BR")
|
|
self.assertColorSame(colorRef[0], colorVal[0], swiz=swizzle)
|
|
|
|
def test_compress_dsw(self):
|
|
"""
|
|
Test decompression swizzles.
|
|
"""
|
|
# The swizzles to test
|
|
swizzles = ["rgba", "g0r1", "rrrg"]
|
|
|
|
# Decompress a swizzled image
|
|
for swizzle in swizzles:
|
|
with self.subTest(swizzle=swizzle):
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [
|
|
self.binary, "-tl",
|
|
"./Test/Data/Tiles/ldr.png",
|
|
decompFile, "4x4", "-exhaustive",
|
|
"-dsw", swizzle]
|
|
|
|
self.exec(command)
|
|
|
|
# Fetch the three color
|
|
img = tli.Image(decompFile)
|
|
colorVal = img.get_colors([(7, 7)])
|
|
colorRef = self.get_color_refs("LDR", "BR")
|
|
self.assertColorSame(colorRef[0], colorVal[0], swiz=swizzle)
|
|
|
|
def test_compress_esw_dsw(self):
|
|
"""
|
|
Test compression and decompression swizzles
|
|
"""
|
|
# Compress a swizzled image, and swizzle back in decompression
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [
|
|
self.binary, "-tl",
|
|
"./Test/Data/Tiles/ldr.png",
|
|
decompFile, "4x4", "-exhaustive",
|
|
"-esw", "gbar", "-dsw", "argb"]
|
|
|
|
self.exec(command)
|
|
|
|
# Fetch the three color
|
|
img = tli.Image(decompFile)
|
|
colorVal = img.get_colors([(7, 7)])
|
|
colorRef = self.get_color_refs("LDR", "BR")
|
|
self.assertColorSame(colorRef[0], colorVal[0])
|
|
|
|
def test_compress_flip(self):
|
|
"""
|
|
Test LDR image flip on compression.
|
|
"""
|
|
# Compress a flipped image
|
|
compFile = self.get_tmp_image_path("LDR", "comp")
|
|
|
|
command = [
|
|
self.binary, "-cl",
|
|
"./Test/Data/Tiles/ldr.png",
|
|
compFile, "4x4", "-fast", "-yflip"]
|
|
|
|
self.exec(command)
|
|
|
|
# Decompress a non-flipped image
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [
|
|
self.binary, "-dl",
|
|
compFile,
|
|
decompFile]
|
|
|
|
self.exec(command)
|
|
|
|
# Compare TL (0, 0) with BL - should match
|
|
colorRef = self.get_color_refs("LDR", "BL")
|
|
|
|
img = tli.Image(decompFile)
|
|
colorVal = img.get_colors([(0, 0)])
|
|
self.assertColorSame(colorRef[0], colorVal[0])
|
|
|
|
def test_decompress_flip(self):
|
|
"""
|
|
Test LDR image flip on decompression.
|
|
"""
|
|
# Compress a non-flipped image
|
|
compFile = self.get_tmp_image_path("LDR", "comp")
|
|
|
|
command = [
|
|
self.binary, "-cl",
|
|
"./Test/Data/Tiles/ldr.png",
|
|
compFile, "4x4", "-fast"]
|
|
|
|
self.exec(command)
|
|
|
|
# Decompress a flipped image
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [
|
|
self.binary, "-dl",
|
|
compFile,
|
|
decompFile, "-yflip"]
|
|
|
|
self.exec(command)
|
|
|
|
# Compare TL (0, 0) with BL - should match
|
|
colorRef = self.get_color_refs("LDR", "BL")
|
|
|
|
img = tli.Image(decompFile)
|
|
colorVal = img.get_colors([(0, 0)])
|
|
self.assertColorSame(colorRef[0], colorVal[0])
|
|
|
|
def test_roundtrip_flip(self):
|
|
"""
|
|
Test LDR image flip on roundtrip (no flip should occur).
|
|
"""
|
|
# Compress and decompressed a flipped LDR image
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [
|
|
self.binary, "-tl",
|
|
"./Test/Data/Tiles/ldr.png",
|
|
decompFile, "4x4", "-fast", "-yflip"]
|
|
|
|
self.exec(command)
|
|
|
|
# Compare TL (0, 0) with TL - should match - i.e. no flip
|
|
colorRef = self.get_color_refs("LDR", "TL")
|
|
|
|
img = tli.Image(decompFile)
|
|
colorVal = img.get_colors([(0, 0)])
|
|
|
|
self.assertColorSame(colorRef[0], colorVal[0])
|
|
|
|
def test_channel_weighting(self):
|
|
"""
|
|
Test channel weighting.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
baseRMSE = self.get_channel_rmse(inputFile, decompFile)
|
|
|
|
# Note: Using -cw can result in a worse result than not using -cw,
|
|
# with regressions in RMSE for the high-weighted channel. This is
|
|
# particularly an issue in synthetic images, as they are more likely to
|
|
# hit corner cases in the heuristics. It happens to "work" for the
|
|
# selected test image and these settings, but might start to fail in
|
|
# future due to compressor changes.
|
|
|
|
# Test each channel with a high weight
|
|
for chIdx, chName in ((0, "R"), (1, "G"), (2, "B"), (3, "A")):
|
|
with self.subTest(channel=chName):
|
|
cwArg = ["%s" % (10 if x == chIdx else 1) for x in range(0, 4)]
|
|
command2 = command + ["-cw"] + cwArg
|
|
self.exec(command2)
|
|
chRMSE = self.get_channel_rmse(inputFile, decompFile)
|
|
self.assertLess(chRMSE[chIdx], baseRMSE[chIdx])
|
|
|
|
def test_partition_count_limit(self):
|
|
"""
|
|
Test partition count limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-partitioncountlimit", "1"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_2partition_index_limit(self):
|
|
"""
|
|
Test partition index limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-2partitionindexlimit", "1"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_3partition_index_limit(self):
|
|
"""
|
|
Test partition index limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-3partitionindexlimit", "1"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_4partition_index_limit(self):
|
|
"""
|
|
Test partition index limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-4partitionindexlimit", "1"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_blockmode_limit(self):
|
|
"""
|
|
Test block mode limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-blockmodelimit", "25"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_refinement_limit(self):
|
|
"""
|
|
Test refinement limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-refinementlimit", "1"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_candidate_limit(self):
|
|
"""
|
|
Test candidate limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-candidatelimit", "1"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_db_cutoff_limit(self):
|
|
"""
|
|
Test db cutoff limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-dblimit", "10"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce cutoff quality
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_2partition_early_limit(self):
|
|
"""
|
|
Test 2 partition early limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-2partitionlimitfactor", "1.0"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_3partition_early_limit(self):
|
|
"""
|
|
Test 3 partition early limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-3partitionlimitfactor", "1.0"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertNotEqual(testRMSE, refRMSE)
|
|
|
|
def test_2plane_correlation_limit(self):
|
|
"""
|
|
Test 2 plane correlation limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-2planelimitcorrelation", "0.1"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_2partition_candidate_limit(self):
|
|
"""
|
|
Test 2 partition partitioning candidate limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-2partitioncandidatelimit", "1"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_3partition_candidate_limit(self):
|
|
"""
|
|
Test 3 partition partitioning candidate limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-3partitioncandidatelimit", "1"]
|
|
self.exec(command)
|
|
testRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
self.assertGreater(testRMSE, refRMSE)
|
|
|
|
def test_4partition_candidate_limit(self):
|
|
"""
|
|
Test 4 partition partitioning candidate limit.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
self.exec(command)
|
|
refRMSE = sum(self.get_channel_rmse(inputFile, decompFile))
|
|
|
|
command += ["-4partitioncandidatelimit", "1"]
|
|
self.exec(command)
|
|
|
|
# RMSE should get worse (higher) if we reduce search space
|
|
# Don't check this here, as 4 partitions not used in any Small image
|
|
# even for -exhaustive, BUT command line option must be accepted and
|
|
# not error ...
|
|
# self.assertGreater(testRMSE, refRMSE)
|
|
|
|
@unittest.skipIf(os.cpu_count() == 1, "Cannot test on single core host")
|
|
def test_thread_count(self):
|
|
"""
|
|
Test codec thread count.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
|
|
start = time.time()
|
|
self.exec(command)
|
|
refTime = time.time() - start
|
|
|
|
command += ["-j", "1"]
|
|
start = time.time()
|
|
self.exec(command)
|
|
testTime = time.time() - start
|
|
|
|
# Test time should get slower with fewer threads
|
|
self.assertGreater(testTime, refTime)
|
|
|
|
def test_silent(self):
|
|
"""
|
|
Test silent
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
decompFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the basic image without any channel weights
|
|
command = [
|
|
self.binary, "-tl",
|
|
inputFile, decompFile, "4x4", "-medium"]
|
|
stdout = self.exec(command)
|
|
|
|
command += ["-silent"]
|
|
stdoutSilent = self.exec(command)
|
|
|
|
# Check that stdout is shorter in silent mode. Note that this doesn't
|
|
# check that it is as silent as it should be, just that silent is wired
|
|
# somewhere ...
|
|
self.assertLess(len(stdoutSilent), len(stdout))
|
|
|
|
def test_image_quality_stability(self):
|
|
"""
|
|
Test that a round-trip and a file-based round-trip give same result.
|
|
"""
|
|
inputFile = "./Test/Images/Small/LDR-RGBA/ldr-rgba-00.png"
|
|
p1DecFile = self.get_tmp_image_path("LDR", "decomp")
|
|
p2CompFile = self.get_tmp_image_path("LDR", "comp")
|
|
p2DecFile = self.get_tmp_image_path("LDR", "decomp")
|
|
|
|
# Compute the first image using a direct round-trip
|
|
command = [self.binary, "-tl", inputFile, p1DecFile, "4x4", "-medium"]
|
|
self.exec(command)
|
|
|
|
# Compute the first image using a file-based round-trip
|
|
command = [self.binary, "-cl", inputFile, p2CompFile, "4x4", "-medium",
|
|
"-decode_unorm8"]
|
|
self.exec(command)
|
|
command = [self.binary, "-dl", p2CompFile, p2DecFile]
|
|
self.exec(command)
|
|
|
|
# RMSE should be the same
|
|
p1RMSE = sum(self.get_channel_rmse(inputFile, p1DecFile))
|
|
p2RMSE = sum(self.get_channel_rmse(inputFile, p2DecFile))
|
|
self.assertEqual(p1RMSE, p2RMSE)
|
|
|
|
|
|
class CLINTest(CLITestBase):
|
|
"""
|
|
Command line interface negative tests.
|
|
|
|
These tests are designed to test that bad inputs to the command line are
|
|
handled cleanly and that errors are correctly thrown.
|
|
|
|
Note that many tests are mutations of a valid positive test command line,
|
|
to ensure that the base command line is valid before it is mutated many
|
|
of these tests include a *positive test* to ensure that the starting point
|
|
is actually a valid command line (otherwise we could be throwing an
|
|
arbitrary error).
|
|
"""
|
|
|
|
def exec(self, command, expectPass=False):
|
|
"""
|
|
Execute a negative test.
|
|
|
|
Test will automatically fail if:
|
|
|
|
* The subprocess return code is zero, unless ``expectPass==True``.
|
|
* The subprocess correctly returned non-zero, but without any error
|
|
message.
|
|
* The subprocess dies with any kind of signal.
|
|
|
|
Args:
|
|
command (list(str)): The command to execute.
|
|
expectPass (bool): ``True`` if this command is actually expected to
|
|
pass, which is used to validate commands before mutating them.
|
|
"""
|
|
try:
|
|
result = sp.run(command, stdout=sp.PIPE, stderr=sp.PIPE,
|
|
universal_newlines=True, check=True)
|
|
error = False
|
|
except sp.CalledProcessError as ex:
|
|
# Pop out of the CPE scope to handle the error, as this reduces
|
|
# test log verbosity on failure by avoiding nested exceptions
|
|
result = ex
|
|
error = True
|
|
|
|
rcode = result.returncode
|
|
|
|
# Emit debug logging if needed (negative rcode is a signal)
|
|
badResult = (error == expectPass) or (rcode < 0)
|
|
|
|
if ASTCENC_CLI_ALWAYS or (badResult and ASTCENC_CLI_ON_ERROR_NEG):
|
|
# Format for shell replay
|
|
print("\n" + " ".join(command))
|
|
# Format for script command list replay
|
|
print("\n" + ", ".join(("\"%s\"" % x for x in command)))
|
|
|
|
if ASTCENC_LOG_ALWAYS or (badResult and ASTCENC_LOG_ON_ERROR_NEG):
|
|
print(result.stdout)
|
|
|
|
# If we expected a pass, then rcode == 0
|
|
if expectPass:
|
|
self.assertEqual(rcode, 0, "Exec did not pass as expected")
|
|
self.assertNotIn("ERROR", result.stderr)
|
|
return
|
|
|
|
# If we got a negative that's always bad (signal of some kind)
|
|
if rcode < 0:
|
|
msg = "Exec died with signal %s" % signal.Signals(-rcode).name
|
|
self.assertGreaterEqual(rcode, 0, msg)
|
|
|
|
# Otherwise just assert that we got an error log, and some positive
|
|
# return code value was returned
|
|
self.assertIn("ERROR", result.stderr)
|
|
self.assertGreater(rcode, 0, "Exec did not fail as expected")
|
|
|
|
def exec_with_omit(self, command, startOmit):
|
|
"""
|
|
Execute a negative test with command line argument omission.
|
|
|
|
These tests aim to prove that the command fails if arguments are
|
|
missing. However the passed command MUST be a valid command which
|
|
passes if no argument are omitted (this is checked, to ensure that
|
|
the test case is a valid test).
|
|
|
|
Test will automatically fail if:
|
|
|
|
* A partial command doesn't fail.
|
|
* The full command doesn't pass.
|
|
"""
|
|
# Run the command, incrementally omitting arguments
|
|
commandLen = len(command)
|
|
for subLen in range(startOmit, commandLen + 1):
|
|
omit = len(command) - subLen
|
|
with self.subTest(omit=omit):
|
|
testCommand = command[:subLen]
|
|
expectPass = omit == 0
|
|
self.exec(testCommand, expectPass)
|
|
|
|
def test_cl_missing_args(self):
|
|
"""
|
|
Test -cl with missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast"]
|
|
|
|
self.exec_with_omit(command, 2)
|
|
|
|
def test_cl_missing_input(self):
|
|
"""
|
|
Test -cl with a missing input file.
|
|
"""
|
|
# Build a valid command with a missing input file
|
|
command = [
|
|
self.binary, "-cl",
|
|
"./Test/Data/missing.png",
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_cl_missing_input_array_slice(self):
|
|
"""
|
|
Test -cl with a missing input file in an array slice.
|
|
"""
|
|
# Build a valid command with a missing input file
|
|
command = [
|
|
self.binary, "-cl",
|
|
"./Test/Data/Tiles/ldr.png",
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"3x3x3", "-fast", "-zdim", "3"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_cl_unknown_input(self):
|
|
"""
|
|
Test -cl with an unknown input file extension.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-cl",
|
|
"./Test/Data/empty.unk",
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_cl_missing_output(self):
|
|
"""
|
|
Test -cl with a missing output directory.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
"./DoesNotExist/test.astc",
|
|
"4x4", "-fast"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_cl_unknown_output(self):
|
|
"""
|
|
Test -cl with an unknown output file extension.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
"./test.aastc",
|
|
"4x4", "-fast"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_cl_bad_block_size(self):
|
|
"""
|
|
Test -cl with an invalid block size.
|
|
"""
|
|
badSizes = [
|
|
"4x5", # Illegal 2D block size
|
|
"3x3x4", # Illegal 3D block size
|
|
"4x4x4x4", # Too many dimensions
|
|
"4x", # Incomplete 2D block size
|
|
"4x4x", # Incomplete 3D block size
|
|
"4x4x4x", # Over-long 3D block size
|
|
"4xe", # Illegal non-numeric character
|
|
"4x4e" # Additional non-numeric character
|
|
]
|
|
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast"]
|
|
|
|
# Test that the underlying command is valid
|
|
self.exec(command, True)
|
|
|
|
blockIndex = command.index("4x4")
|
|
for badSize in badSizes:
|
|
with self.subTest(blockSize=badSize):
|
|
command[blockIndex] = badSize
|
|
self.exec(command)
|
|
|
|
def test_cl_bad_preset(self):
|
|
"""
|
|
Test -cl with an invalid encoding preset.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fastt"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_cl_bad_argument(self):
|
|
"""
|
|
Test -cl with an unknown additional argument.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast", "-unknown"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_cl_2d_block_with_array(self):
|
|
"""
|
|
Test -cl with a 2D block size and 3D input data.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
|
|
# TODO: This fails late (i.e. the data is still loaded, and we fail
|
|
# at processing time when we see a 3D array). We could fail earlier at
|
|
# parse time, which might consolidate the error handling code.
|
|
command = [
|
|
self.binary, "-cl",
|
|
"./Test/Data/Tiles/ldr.png",
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast", "-zdim", "2"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_cl_array_missing_args(self):
|
|
"""
|
|
Test -cl with a 2D block size and 3D input data.
|
|
"""
|
|
# Build an otherwise valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
"./Test/Data/Tiles/ldr.png",
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4x4", "-fast", "-zdim", "2"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_tl_missing_args(self):
|
|
"""
|
|
Test -tl with missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-tl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "decomp"),
|
|
"4x4", "-fast"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 2)
|
|
|
|
def test_tl_missing_input(self):
|
|
"""
|
|
Test -tl with a missing input file.
|
|
"""
|
|
# Build a valid command with a missing input file
|
|
command = [
|
|
self.binary, "-tl",
|
|
"./Test/Data/missing.png",
|
|
self.get_tmp_image_path("LDR", "decomp"),
|
|
"4x4", "-fast"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_tl_unknown_input(self):
|
|
"""
|
|
Test -tl with an unknown input file extension.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-tl",
|
|
"./Test/Data/empty.unk",
|
|
self.get_tmp_image_path("LDR", "decomp"),
|
|
"4x4", "-fast"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_tl_missing_output(self):
|
|
"""
|
|
Test -tl with a missing output directory.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-tl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
"./DoesNotExist/test.png",
|
|
"4x4", "-fast"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_tl_bad_block_size(self):
|
|
"""
|
|
Test -tl with an invalid block size.
|
|
"""
|
|
badSizes = [
|
|
"4x5", # Illegal 2D block size
|
|
"3x3x4", # Illegal 3D block size
|
|
"4x4x4x4", # Too many dimensions
|
|
"4x", # Incomplete 2D block size
|
|
"4x4x", # Incomplete 3D block size
|
|
"4x4x4x", # Over-long 3D block size
|
|
"4xe", # Illegal non-numeric character
|
|
"4x4e" # Additional non-numeric character
|
|
]
|
|
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-tl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "decomp"),
|
|
"4x4", "-fast"]
|
|
|
|
# Test that the underlying command is valid
|
|
self.exec(command, True)
|
|
|
|
blockIndex = command.index("4x4")
|
|
for badSize in badSizes:
|
|
with self.subTest(blockSize=badSize):
|
|
command[blockIndex] = badSize
|
|
self.exec(command)
|
|
|
|
def test_tl_bad_preset(self):
|
|
"""
|
|
Test -tl with an invalid encoding preset.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-tl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "decomp"),
|
|
"4x4", "-fastt"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_tl_bad_argument(self):
|
|
"""
|
|
Test -tl with an unknown additional argument.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-tl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "decomp"),
|
|
"4x4", "-fast", "-unknown"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_dl_missing_args(self):
|
|
"""
|
|
Test -dl with missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-dl",
|
|
self.get_ref_image_path("LDR", "comp", "A"),
|
|
self.get_tmp_image_path("LDR", "decomp")]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 2)
|
|
|
|
def test_dl_missing_output(self):
|
|
"""
|
|
Test -dl with a missing output directory.
|
|
"""
|
|
# Build an otherwise valid command with the test flaw
|
|
command = [
|
|
self.binary, "-dl",
|
|
self.get_ref_image_path("LDR", "comp", "A"),
|
|
"./DoesNotExist/test.png"]
|
|
|
|
self.exec(command)
|
|
|
|
def test_cl_a_missing_args(self):
|
|
"""
|
|
Test -cl with -a and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-a", "2"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_cw_missing_args(self):
|
|
"""
|
|
Test -cl with -cw and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-cw", "0", "1", "2", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_2partitionindexlimit_missing_args(self):
|
|
"""
|
|
Test -cl with -2partitionindexlimit and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-2partitionindexlimit", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_3partitionindexlimit_missing_args(self):
|
|
"""
|
|
Test -cl with -3partitionindexlimit and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-3partitionindexlimit", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_4partitionindexlimit_missing_args(self):
|
|
"""
|
|
Test -cl with -4partitionindexlimit and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-4partitionindexlimit", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_2partitioncandidatelimit_missing_args(self):
|
|
"""
|
|
Test -cl with -2partitioncandidatelimit and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-2partitioncandidatelimit", "1"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_3partitioncandidatelimit_missing_args(self):
|
|
"""
|
|
Test -cl with -3partitioncandidatelimit and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-3partitioncandidatelimit", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
|
|
def test_cl_4partitioncandidatelimit_missing_args(self):
|
|
"""
|
|
Test -cl with -4partitioncandidatelimit and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-4partitioncandidatelimit", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_blockmodelimit_missing_args(self):
|
|
"""
|
|
Test -cl with -blockmodelimit and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-blockmodelimit", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_refinementlimit_missing_args(self):
|
|
"""
|
|
Test -cl with -refinementlimit and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-refinementlimit", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_dblimit_missing_args(self):
|
|
"""
|
|
Test -cl with -dblimit and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-dblimit", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_2partitionearlylimit_missing_args(self):
|
|
"""
|
|
Test -cl with -2partitionlimitfactor and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-2partitionlimitfactor", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_3partitionearlylimit_missing_args(self):
|
|
"""
|
|
Test -cl with -3partitionlimitfactor and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-3partitionlimitfactor", "3"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_2planeearlylimit_missing_args(self):
|
|
"""
|
|
Test -cl with -2planelimitcorrelation and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-2planelimitcorrelation", "0.66"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_esw_missing_args(self):
|
|
"""
|
|
Test -cl with -esw and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-esw", "rgb1"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_esw_invalid_swizzle(self):
|
|
"""
|
|
Test -cl with -esw and invalid swizzles.
|
|
"""
|
|
badSwizzles = [
|
|
"", # Short swizzles
|
|
"r",
|
|
"rr",
|
|
"rrr",
|
|
"rrrrr", # Long swizzles
|
|
]
|
|
|
|
# Create swizzles with all invalid printable ascii codes
|
|
good = ["r", "g", "b", "a", "0", "1"]
|
|
for channel in string.printable:
|
|
if channel not in good:
|
|
badSwizzles.append(channel * 4)
|
|
|
|
# Build a valid base command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-esw", "rgba"]
|
|
|
|
blockIndex = command.index("rgba")
|
|
for badSwizzle in badSwizzles:
|
|
with self.subTest(swizzle=badSwizzle):
|
|
command[blockIndex] = badSwizzle
|
|
self.exec(command)
|
|
|
|
def test_cl_ssw_missing_args(self):
|
|
"""
|
|
Test -cl with -ssw and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-ssw", "rgba"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
def test_cl_ssw_invalid_swizzle(self):
|
|
"""
|
|
Test -cl with -ssw and invalid swizzles.
|
|
"""
|
|
badSwizzles = [
|
|
"", # Short swizzles
|
|
"rrrrr", # Long swizzles
|
|
]
|
|
|
|
# Create swizzles with all invalid printable ascii codes
|
|
good = ["r", "g", "b", "a"]
|
|
for channel in string.printable:
|
|
if channel not in good:
|
|
badSwizzles.append(channel * 4)
|
|
|
|
# Build a valid base command
|
|
command = [
|
|
self.binary, "-cl",
|
|
self.get_ref_image_path("LDR", "input", "A"),
|
|
self.get_tmp_image_path("LDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-ssw", "rgba"]
|
|
|
|
blockIndex = command.index("rgba")
|
|
for badSwizzle in badSwizzles:
|
|
with self.subTest(swizzle=badSwizzle):
|
|
command[blockIndex] = badSwizzle
|
|
self.exec(command)
|
|
|
|
def test_dl_dsw_missing_args(self):
|
|
"""
|
|
Test -dl with -dsw and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-dl",
|
|
self.get_ref_image_path("LDR", "comp", "A"),
|
|
self.get_tmp_image_path("LDR", "decomp"),
|
|
"-dsw", "rgb1"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 5)
|
|
|
|
def test_dl_dsw_invalid_swizzle(self):
|
|
"""
|
|
Test -dl with -dsw and invalid swizzles.
|
|
"""
|
|
badSwizzles = [
|
|
"", # Short swizzles
|
|
"r",
|
|
"rr",
|
|
"rrr",
|
|
"rrrrr", # Long swizzles
|
|
]
|
|
|
|
# Create swizzles with all invalid printable ascii codes
|
|
good = ["r", "g", "b", "a", "z", "0", "1"]
|
|
for channel in string.printable:
|
|
if channel not in good:
|
|
badSwizzles.append(channel * 4)
|
|
|
|
# Build a valid base command
|
|
command = [
|
|
self.binary, "-dl",
|
|
self.get_ref_image_path("LDR", "comp", "A"),
|
|
self.get_tmp_image_path("LDR", "decomp"),
|
|
"-dsw", "rgba"]
|
|
|
|
blockIndex = command.index("rgba")
|
|
for badSwizzle in badSwizzles:
|
|
with self.subTest(swizzle=badSwizzle):
|
|
command[blockIndex] = badSwizzle
|
|
self.exec(command)
|
|
|
|
def test_ch_mpsnr_missing_args(self):
|
|
"""
|
|
Test -ch with -mpsnr and missing arguments.
|
|
"""
|
|
# Build a valid command
|
|
command = [
|
|
self.binary, "-ch",
|
|
self.get_ref_image_path("HDR", "input", "A"),
|
|
self.get_tmp_image_path("HDR", "comp"),
|
|
"4x4", "-fast",
|
|
"-mpsnr", "-5", "5"]
|
|
|
|
# Run the command, incrementally omitting arguments
|
|
self.exec_with_omit(command, 7)
|
|
|
|
|
|
def main():
|
|
"""
|
|
The main function.
|
|
|
|
Returns:
|
|
int: The process return code.
|
|
"""
|
|
global g_TestEncoder
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
coders = ["none", "neon", "sve_128", "sve_256", "sse2", "sse4.1", "avx2"]
|
|
parser.add_argument("--encoder", dest="encoder", default="avx2",
|
|
choices=coders, help="test encoder variant")
|
|
args = parser.parse_known_args()
|
|
|
|
# Set the encoder for this test run
|
|
g_TestEncoder = args[0].encoder
|
|
|
|
# Set the sys.argv to remaining args (leave sys.argv[0] alone)
|
|
sys.argv[1:] = args[1]
|
|
|
|
results = unittest.main(exit=False)
|
|
return 0 if results.result.wasSuccessful() else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|