| #!/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()) |