mirror of
https://github.com/drewcassidy/quicktex.git
synced 2024-09-13 06:37:34 +00:00
Migrate tests to pytest
This commit is contained in:
parent
daae86cf50
commit
920059bea1
@ -1,208 +1,206 @@
|
|||||||
import unittest
|
import math
|
||||||
from parameterized import parameterized, parameterized_class
|
|
||||||
from quicktex.s3tc.bc1 import BC1Block, BC1Texture, BC1Encoder, BC1Decoder
|
import pytest
|
||||||
from .images import BC1Blocks
|
|
||||||
from PIL import Image, ImageChops
|
from PIL import Image, ImageChops
|
||||||
|
|
||||||
in_endpoints = ((253, 254, 255), (65, 70, 67)) # has some small changes that should encode the same
|
from quicktex.s3tc.bc1 import BC1Block, BC1Texture, BC1Encoder, BC1Decoder
|
||||||
|
from .images import BC1Blocks
|
||||||
|
|
||||||
|
in_endpoints = ((253, 254, 255), (65, 70, 67)) # has some small changes that should encode the same in 5:6:5
|
||||||
out_endpoints = ((255, 255, 255, 255), (66, 69, 66, 255))
|
out_endpoints = ((255, 255, 255, 255), (66, 69, 66, 255))
|
||||||
selectors = [[0, 2, 3, 1]] * 4
|
selectors = [[0, 2, 3, 1]] * 4
|
||||||
block_bytes = b'\xff\xff\x28\x42\x78\x78\x78\x78'
|
block_bytes = b'\xff\xff\x28\x42\x78\x78\x78\x78'
|
||||||
|
|
||||||
|
|
||||||
class TestBC1Block(unittest.TestCase):
|
class TestBC1Block:
|
||||||
"""Tests for the BC1Block class"""
|
"""Tests for the BC1Block class"""
|
||||||
|
|
||||||
def test_size(self):
|
def test_size(self):
|
||||||
"""Test the size and dimensions of BC1Block"""
|
"""Test the size and dimensions of BC1Block"""
|
||||||
self.assertEqual(BC1Block.nbytes, 8, 'incorrect block size')
|
assert BC1Block.nbytes == 8
|
||||||
self.assertEqual(BC1Block.width, 4, 'incorrect block width')
|
assert BC1Block.width == 4
|
||||||
self.assertEqual(BC1Block.height, 4, 'incorrect block width')
|
assert BC1Block.height == 4
|
||||||
self.assertEqual(BC1Block.size, (4, 4), 'incorrect block dimensions')
|
assert BC1Block.size == (4, 4)
|
||||||
|
|
||||||
def test_buffer(self):
|
def test_buffer(self):
|
||||||
"""Test the buffer protocol of BC1Block"""
|
"""Test the buffer protocol of BC1Block"""
|
||||||
block = BC1Block()
|
block = BC1Block()
|
||||||
mv = memoryview(block)
|
mv = memoryview(block)
|
||||||
|
|
||||||
self.assertFalse(mv.readonly, 'buffer is readonly')
|
|
||||||
self.assertTrue(mv.c_contiguous, 'buffer is not contiguous')
|
|
||||||
self.assertEqual(mv.ndim, 1, 'buffer is multidimensional')
|
|
||||||
self.assertEqual(mv.nbytes, BC1Block.nbytes, 'buffer is the wrong size')
|
|
||||||
self.assertEqual(mv.format, 'B', 'buffer has the wrong format')
|
|
||||||
|
|
||||||
mv[:] = block_bytes
|
mv[:] = block_bytes
|
||||||
self.assertEqual(mv.tobytes(), block_bytes, 'incorrect buffer data')
|
|
||||||
|
assert not mv.readonly
|
||||||
|
assert mv.c_contiguous
|
||||||
|
assert mv.ndim == 1
|
||||||
|
assert mv.nbytes == 8
|
||||||
|
assert mv.format == 'B'
|
||||||
|
assert mv.tobytes() == block_bytes
|
||||||
|
assert mv.tobytes() == block.tobytes()
|
||||||
|
|
||||||
def test_constructor(self):
|
def test_constructor(self):
|
||||||
"""Test constructing a block out of endpoints and selectors"""
|
"""Test constructing a block out of endpoints and selectors"""
|
||||||
block = BC1Block(*in_endpoints, selectors)
|
block = BC1Block(*in_endpoints, selectors)
|
||||||
self.assertEqual(block.tobytes(), block_bytes, 'incorrect block bytes')
|
assert block.tobytes() == block_bytes
|
||||||
self.assertEqual(block.selectors, selectors, 'incorrect selectors')
|
assert block.selectors == selectors
|
||||||
self.assertEqual(block.endpoints, out_endpoints, 'incorrect endpoints')
|
assert block.endpoints == out_endpoints
|
||||||
self.assertFalse(block.is_3color, 'incorrect color mode')
|
assert not block.is_3color
|
||||||
|
|
||||||
def test_frombytes(self):
|
def test_frombytes(self):
|
||||||
"""Test constructing a block out of raw data"""
|
"""Test constructing a block out of raw data"""
|
||||||
block = BC1Block.frombytes(block_bytes)
|
block = BC1Block.frombytes(block_bytes)
|
||||||
self.assertEqual(block.tobytes(), block_bytes, 'incorrect block bytes')
|
assert block.tobytes() == block_bytes
|
||||||
self.assertEqual(block.selectors, selectors, 'incorrect selectors')
|
assert block.selectors == selectors
|
||||||
self.assertEqual(block.endpoints, out_endpoints, 'incorrect endpoints')
|
assert block.endpoints == out_endpoints
|
||||||
self.assertFalse(block.is_3color, 'incorrect color mode')
|
assert not block.is_3color
|
||||||
|
|
||||||
def test_eq(self):
|
def test_eq(self):
|
||||||
"""Test equality between two identical blocks"""
|
"""Test equality between two identical blocks"""
|
||||||
block1 = BC1Block.frombytes(block_bytes)
|
block1 = BC1Block.frombytes(block_bytes)
|
||||||
block2 = BC1Block.frombytes(block_bytes)
|
block2 = BC1Block.frombytes(block_bytes)
|
||||||
self.assertEqual(block1, block2, 'identical blocks not equal')
|
assert block1 == block2
|
||||||
|
|
||||||
|
|
||||||
@parameterized_class(
|
# noinspection PyMethodMayBeStatic
|
||||||
("name", "w", "h", "wb", "hb"), [("8x8", 8, 8, 2, 2), ("9x9", 9, 9, 3, 3), ("7x7", 7, 7, 2, 2), ("7x9", 7, 9, 2, 3)]
|
@pytest.mark.parametrize('w', [7, 8, 9])
|
||||||
)
|
@pytest.mark.parametrize('h', [7, 8, 9])
|
||||||
class TestBC1Texture(unittest.TestCase):
|
class TestBC1Texture:
|
||||||
def setUp(self):
|
def test_dimensions(self, w, h):
|
||||||
self.tex = BC1Texture(self.w, self.h)
|
"""Test dimensions of BC1Texture in pixels, blocks, and bytes"""
|
||||||
self.nbytes = self.wb * self.hb * BC1Block.nbytes
|
tex = BC1Texture(w, h)
|
||||||
|
wb = math.ceil(w / 4)
|
||||||
|
hb = math.ceil(h / 4)
|
||||||
|
|
||||||
def test_size(self):
|
assert tex.nbytes == BC1Block.nbytes * wb * hb # block width x block height
|
||||||
"""Test size of BC1Texture in bytes"""
|
assert len(tex.tobytes()) == tex.nbytes
|
||||||
self.assertEqual(self.tex.nbytes, self.nbytes, 'incorrect texture size')
|
|
||||||
self.assertEqual(len(self.tex.tobytes()), self.nbytes, 'incorrect texture size from tobytes')
|
|
||||||
|
|
||||||
def test_dimensions(self):
|
assert tex.width == w
|
||||||
"""Test dimensions of BC1Texture in pixels"""
|
assert tex.height == h
|
||||||
self.assertEqual(self.tex.width, self.w, 'incorrect texture width')
|
assert tex.size == (w, h)
|
||||||
self.assertEqual(self.tex.height, self.h, 'incorrect texture height')
|
|
||||||
self.assertEqual(self.tex.size, (self.w, self.h), 'incorrect texture dimensions')
|
|
||||||
|
|
||||||
def test_dimensions_blocks(self):
|
assert tex.width_blocks == wb
|
||||||
"""Test dimensions of BC1Texture in blocks"""
|
assert tex.height_blocks == hb
|
||||||
self.assertEqual(self.tex.width_blocks, self.wb, 'incorrect texture width_blocks')
|
assert tex.size_blocks == (wb, hb)
|
||||||
self.assertEqual(self.tex.height_blocks, self.hb, 'incorrect texture width_blocks')
|
|
||||||
self.assertEqual(self.tex.size_blocks, (self.wb, self.hb), 'incorrect texture dimensions_blocks')
|
|
||||||
|
|
||||||
def test_blocks(self):
|
def test_blocks(self, w, h):
|
||||||
"""Test getting and setting blocks to BC1Texture"""
|
"""Test getting and setting blocks to BC1Texture"""
|
||||||
blocks = [[BC1Block.frombytes(bytes([x, y] + [0] * 6)) for x in range(self.wb)] for y in range(self.hb)]
|
tex = BC1Texture(w, h)
|
||||||
for x in range(self.wb):
|
|
||||||
for y in range(self.hb):
|
|
||||||
self.tex[x, y] = blocks[y][x]
|
|
||||||
|
|
||||||
b = self.tex.tobytes()
|
# generate garbage blocks with the x and y index in the first 2 bytes
|
||||||
for x in range(self.wb):
|
blocks = [
|
||||||
for y in range(self.hb):
|
[BC1Block.frombytes(bytes([x, y] + [0] * 6)) for x in range(tex.width_blocks)]
|
||||||
index = (x + (y * self.wb)) * BC1Block.nbytes
|
for y in range(tex.height_blocks)
|
||||||
tb = self.tex[x, y]
|
]
|
||||||
|
# assign those blocks to the texture
|
||||||
|
for x in range(tex.width_blocks):
|
||||||
|
for y in range(tex.height_blocks):
|
||||||
|
tex[x, y] = blocks[y][x]
|
||||||
|
|
||||||
|
# get the blocks and analyze
|
||||||
|
b = tex.tobytes()
|
||||||
|
for x in range(tex.width_blocks):
|
||||||
|
for y in range(tex.height_blocks):
|
||||||
|
index = (x + (y * tex.width_blocks)) * BC1Block.nbytes
|
||||||
|
tb = tex[x, y]
|
||||||
fb = BC1Block.frombytes(b[index : index + BC1Block.nbytes])
|
fb = BC1Block.frombytes(b[index : index + BC1Block.nbytes])
|
||||||
self.assertEqual(tb, blocks[y][x], 'incorrect block read from texture')
|
assert tb == blocks[y][x]
|
||||||
self.assertEqual(fb, blocks[y][x], 'incorrect block read from texture bytes')
|
assert fb == blocks[y][x]
|
||||||
|
|
||||||
self.assertEqual(self.tex[-1, -1], self.tex[self.wb - 1, self.hb - 1], 'incorrect negative subscripting')
|
def text_subscript(self, w, h):
|
||||||
|
"""Test BC1Texture subscripting for blocks"""
|
||||||
|
tex = BC1Texture(w, h)
|
||||||
|
|
||||||
with self.assertRaises(IndexError):
|
# ensure negative wraparound works
|
||||||
_ = self.tex[self.wb, self.hb]
|
assert tex[-1, -1] == tex[tex.width_blocks - 1, tex.height_blocks - 1]
|
||||||
with self.assertRaises(IndexError):
|
|
||||||
_ = self.tex[-1 - self.wb, -1 - self.hb]
|
|
||||||
|
|
||||||
def test_buffer(self):
|
with pytest.raises(IndexError):
|
||||||
|
_ = tex[tex.width_blocks, tex.height_blocks]
|
||||||
|
with pytest.raises(IndexError):
|
||||||
|
_ = tex[-1 - tex.width_blocks, -1 - tex.height_blocks]
|
||||||
|
|
||||||
|
def test_buffer(self, w, h):
|
||||||
"""Test the buffer protocol of BC1Texture"""
|
"""Test the buffer protocol of BC1Texture"""
|
||||||
mv = memoryview(self.tex)
|
tex = BC1Texture(w, h)
|
||||||
|
mv = memoryview(tex)
|
||||||
|
|
||||||
self.assertFalse(mv.readonly, 'buffer is readonly')
|
data = block_bytes * tex.width_blocks * tex.height_blocks
|
||||||
self.assertTrue(mv.c_contiguous, 'buffer is not contiguous')
|
|
||||||
self.assertEqual(mv.nbytes, self.nbytes, 'buffer is the wrong size')
|
|
||||||
self.assertEqual(mv.format, 'B', 'buffer has the wrong format')
|
|
||||||
|
|
||||||
data = block_bytes * self.wb * self.hb
|
|
||||||
mv[:] = data
|
mv[:] = data
|
||||||
self.assertEqual(mv.tobytes(), data, 'incorrect buffer data')
|
|
||||||
|
assert not mv.readonly
|
||||||
|
assert mv.c_contiguous
|
||||||
|
assert mv.nbytes == tex.nbytes
|
||||||
|
assert mv.format == 'B'
|
||||||
|
assert mv.tobytes() == data
|
||||||
|
|
||||||
|
|
||||||
@parameterized_class(
|
@pytest.mark.parametrize(
|
||||||
("name", "color_mode"),
|
'color_mode',
|
||||||
[
|
[BC1Encoder.ColorMode.FourColor, BC1Encoder.ColorMode.ThreeColor, BC1Encoder.ColorMode.ThreeColorBlack],
|
||||||
("4Color", BC1Encoder.ColorMode.FourColor),
|
|
||||||
("3Color", BC1Encoder.ColorMode.ThreeColor),
|
|
||||||
("3Color_Black", BC1Encoder.ColorMode.ThreeColorBlack),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
class TestBC1Encoder(unittest.TestCase):
|
class TestBC1Encoder:
|
||||||
"""Test BC1Encoder"""
|
"""Test BC1Encoder"""
|
||||||
|
|
||||||
@classmethod
|
def test_block_4color(self, color_mode):
|
||||||
def setUpClass(cls):
|
|
||||||
cls.bc1_encoder = BC1Encoder(5, cls.color_mode)
|
|
||||||
|
|
||||||
def test_block_4color(self):
|
|
||||||
"""Test encoder output with 4 color greyscale test block"""
|
"""Test encoder output with 4 color greyscale test block"""
|
||||||
out_tex = self.bc1_encoder.encode(BC1Blocks.greyscale.texture)
|
encoder = BC1Encoder(color_mode=color_mode)
|
||||||
|
out_tex = encoder.encode(BC1Blocks.greyscale.texture)
|
||||||
self.assertEqual(out_tex.size_blocks, (1, 1), 'encoded texture has multiple blocks')
|
|
||||||
|
|
||||||
out_block = out_tex[0, 0]
|
out_block = out_tex[0, 0]
|
||||||
|
|
||||||
self.assertFalse(out_block.is_3color, 'returned 3color mode for greyscale test block')
|
assert out_tex.size_blocks == (1, 1)
|
||||||
self.assertEqual(out_block, BC1Blocks.greyscale.block, 'encoded block is incorrect')
|
|
||||||
|
|
||||||
def test_block_3color(self):
|
assert not out_block.is_3color
|
||||||
|
assert out_block == BC1Blocks.greyscale.block
|
||||||
|
|
||||||
|
def test_block_3color(self, color_mode):
|
||||||
"""Test encoder output with 3 color test block"""
|
"""Test encoder output with 3 color test block"""
|
||||||
out_tex = self.bc1_encoder.encode(BC1Blocks.three_color.texture)
|
encoder = BC1Encoder(color_mode=color_mode)
|
||||||
|
out_tex = encoder.encode(BC1Blocks.three_color.texture)
|
||||||
self.assertEqual(out_tex.size_blocks, (1, 1), 'encoded texture has multiple blocks')
|
|
||||||
|
|
||||||
out_block = out_tex[0, 0]
|
out_block = out_tex[0, 0]
|
||||||
|
|
||||||
if self.color_mode != BC1Encoder.ColorMode.FourColor:
|
assert out_tex.size_blocks == (1, 1)
|
||||||
|
|
||||||
|
if encoder.color_mode != BC1Encoder.ColorMode.FourColor:
|
||||||
# we only care about the selectors if we are in 3 color mode
|
# we only care about the selectors if we are in 3 color mode
|
||||||
self.assertTrue(out_block.is_3color, 'returned 4-color block for 3 color test block')
|
assert out_block.is_3color
|
||||||
self.assertEqual(out_block, BC1Blocks.three_color.block, 'encoded block is incorrect')
|
assert out_block == BC1Blocks.three_color.block
|
||||||
else:
|
else:
|
||||||
self.assertFalse(out_block.is_3color, 'returned 3-color block in 4-color mode')
|
assert not out_block.is_3color
|
||||||
|
|
||||||
def test_block_3color_black(self):
|
def test_block_3color_black(self, color_mode):
|
||||||
"""Test encoder output with 3 color test block with black pixels"""
|
"""Test encoder output with 3 color test block with black pixels"""
|
||||||
out_tex = self.bc1_encoder.encode(BC1Blocks.three_color_black.texture)
|
encoder = BC1Encoder(color_mode=color_mode)
|
||||||
|
out_tex = encoder.encode(BC1Blocks.three_color_black.texture)
|
||||||
self.assertEqual(out_tex.size_blocks, (1, 1), 'encoded texture has multiple blocks')
|
|
||||||
|
|
||||||
out_block = out_tex[0, 0]
|
out_block = out_tex[0, 0]
|
||||||
|
|
||||||
|
assert out_tex.size_blocks == (1, 1)
|
||||||
|
|
||||||
has_black = 3 in [j for row in out_block.selectors for j in row]
|
has_black = 3 in [j for row in out_block.selectors for j in row]
|
||||||
|
|
||||||
if self.color_mode == BC1Encoder.ColorMode.ThreeColorBlack:
|
if color_mode == BC1Encoder.ColorMode.ThreeColorBlack:
|
||||||
# we only care about the selectors if we are in 3 color black mode
|
# we only care about the selectors if we are in 3 color black mode
|
||||||
self.assertTrue(out_block.is_3color, 'returned 4-color block for 3 color test block with black')
|
assert out_block.is_3color
|
||||||
self.assertTrue(has_black, 'block does not have black pixels as expected')
|
assert has_black
|
||||||
self.assertEqual(out_block, BC1Blocks.three_color_black.block, "encoded block is incorrect")
|
assert out_block == BC1Blocks.three_color_black.block
|
||||||
elif self.color_mode == BC1Encoder.ColorMode.ThreeColor:
|
elif color_mode == BC1Encoder.ColorMode.ThreeColor:
|
||||||
self.assertFalse(has_black and out_block.is_3color, 'returned 3color block with black pixels')
|
assert not (has_black and out_block.is_3color)
|
||||||
else:
|
else:
|
||||||
self.assertFalse(out_block.is_3color, 'returned 3-color block in 4-color mode')
|
assert not out_block.is_3color
|
||||||
|
|
||||||
|
|
||||||
class TestBC1Decoder(unittest.TestCase):
|
@pytest.mark.parametrize('texture', [BC1Blocks.greyscale, BC1Blocks.three_color, BC1Blocks.three_color_black])
|
||||||
|
class TestBC1Decoder:
|
||||||
"""Test BC1Decoder"""
|
"""Test BC1Decoder"""
|
||||||
|
|
||||||
@classmethod
|
def test_block(self, texture):
|
||||||
def setUpClass(cls):
|
|
||||||
cls.bc1_decoder = BC1Decoder()
|
|
||||||
|
|
||||||
@parameterized.expand(
|
|
||||||
[
|
|
||||||
("4color", BC1Blocks.greyscale.block, BC1Blocks.greyscale.image),
|
|
||||||
("3color", BC1Blocks.three_color.block, BC1Blocks.three_color.image),
|
|
||||||
("3color_black", BC1Blocks.three_color_black.block, BC1Blocks.three_color_black.image),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
def test_block(self, _, block, image):
|
|
||||||
"""Test decoder output for a single block"""
|
"""Test decoder output for a single block"""
|
||||||
|
block = texture.block
|
||||||
|
image = texture.image
|
||||||
|
decoder = BC1Decoder()
|
||||||
in_tex = BC1Texture(4, 4)
|
in_tex = BC1Texture(4, 4)
|
||||||
in_tex[0, 0] = block
|
in_tex[0, 0] = block
|
||||||
out_tex = self.bc1_decoder.decode(in_tex)
|
out_tex = decoder.decode(in_tex)
|
||||||
|
|
||||||
self.assertEqual(out_tex.size, (4, 4), 'decoded texture has incorrect dimensions')
|
assert out_tex.size == (4, 4)
|
||||||
|
|
||||||
out_img = Image.frombytes('RGBA', (4, 4), out_tex.tobytes())
|
out_img = Image.frombytes('RGBA', (4, 4), out_tex.tobytes())
|
||||||
img_diff = ImageChops.difference(out_img, image).convert('L')
|
img_diff = ImageChops.difference(out_img, image).convert('L')
|
||||||
img_hist = img_diff.histogram()
|
img_hist = img_diff.histogram()
|
||||||
self.assertEqual(16, img_hist[0], 'decoded block is incorrect')
|
assert img_hist[0] == 16
|
||||||
|
@ -1,178 +1,179 @@
|
|||||||
import unittest
|
import math
|
||||||
from parameterized import parameterized, parameterized_class
|
|
||||||
from quicktex.s3tc.bc4 import BC4Block, BC4Texture, BC4Encoder, BC4Decoder
|
import pytest
|
||||||
from .images import BC4Blocks
|
|
||||||
from PIL import Image, ImageChops
|
from PIL import Image, ImageChops
|
||||||
|
|
||||||
|
from quicktex.s3tc.bc4 import BC4Block, BC4Texture, BC4Encoder, BC4Decoder
|
||||||
|
from .images import BC4Blocks
|
||||||
|
|
||||||
class TestBC4Block(unittest.TestCase):
|
block_bytes = b'\xF0\x10\x88\x86\x68\xAC\xCF\xFA'
|
||||||
|
selectors = [[0, 1, 2, 3]] * 2 + [[4, 5, 6, 7]] * 2
|
||||||
|
endpoints = (240, 16)
|
||||||
|
|
||||||
|
|
||||||
|
class TestBC4Block:
|
||||||
"""Tests for the BC1Block class"""
|
"""Tests for the BC1Block class"""
|
||||||
|
|
||||||
block_bytes = b'\xF0\x10\x88\x86\x68\xAC\xCF\xFA'
|
|
||||||
selectors = [[0, 1, 2, 3]] * 2 + [[4, 5, 6, 7]] * 2
|
|
||||||
endpoints = (240, 16)
|
|
||||||
|
|
||||||
def test_size(self):
|
def test_size(self):
|
||||||
"""Test the size and dimensions of BC4Block"""
|
"""Test the size and dimensions of BC4Block"""
|
||||||
self.assertEqual(BC4Block.nbytes, 8, 'incorrect block size')
|
assert BC4Block.nbytes == 8
|
||||||
self.assertEqual(BC4Block.width, 4, 'incorrect block width')
|
assert BC4Block.width == 4
|
||||||
self.assertEqual(BC4Block.height, 4, 'incorrect block width')
|
assert BC4Block.height == 4
|
||||||
self.assertEqual(BC4Block.size, (4, 4), 'incorrect block dimensions')
|
assert BC4Block.size == (4, 4)
|
||||||
|
|
||||||
def test_buffer(self):
|
def test_buffer(self):
|
||||||
"""Test the buffer protocol of BC4Block"""
|
"""Test the buffer protocol of BC4Block"""
|
||||||
block = BC4Block()
|
block = BC4Block()
|
||||||
mv = memoryview(block)
|
mv = memoryview(block)
|
||||||
|
mv[:] = block_bytes
|
||||||
|
|
||||||
self.assertFalse(mv.readonly, 'buffer is readonly')
|
assert not mv.readonly
|
||||||
self.assertTrue(mv.c_contiguous, 'buffer is not contiguous')
|
assert mv.c_contiguous
|
||||||
self.assertEqual(mv.ndim, 1, 'buffer is multidimensional')
|
assert mv.ndim == 1
|
||||||
self.assertEqual(mv.nbytes, BC4Block.nbytes, 'buffer is the wrong size')
|
assert mv.nbytes == 8
|
||||||
self.assertEqual(mv.format, 'B', 'buffer has the wrong format')
|
assert mv.format == 'B'
|
||||||
|
assert mv.tobytes() == block_bytes
|
||||||
mv[:] = self.block_bytes
|
assert mv.tobytes() == block.tobytes()
|
||||||
self.assertEqual(mv.tobytes(), self.block_bytes, 'incorrect buffer data')
|
|
||||||
|
|
||||||
def test_constructor(self):
|
def test_constructor(self):
|
||||||
"""Test constructing a block out of endpoints and selectors"""
|
"""Test constructing a block out of endpoints and selectors"""
|
||||||
block = BC4Block(*self.endpoints, self.selectors)
|
block = BC4Block(*endpoints, selectors)
|
||||||
self.assertEqual(block.tobytes(), self.block_bytes, 'incorrect block bytes')
|
assert block.tobytes() == block_bytes
|
||||||
self.assertEqual(block.selectors, self.selectors, 'incorrect selectors')
|
assert block.selectors == selectors
|
||||||
self.assertEqual(block.endpoints, self.endpoints, 'incorrect endpoints')
|
assert block.endpoints == endpoints
|
||||||
|
|
||||||
def test_frombytes(self):
|
def test_frombytes(self):
|
||||||
"""Test constructing a block out of raw data"""
|
"""Test constructing a block out of raw data"""
|
||||||
block = BC4Block.frombytes(self.block_bytes)
|
block = BC4Block.frombytes(block_bytes)
|
||||||
self.assertEqual(block.tobytes(), self.block_bytes, 'incorrect block bytes')
|
assert block.tobytes() == block_bytes
|
||||||
self.assertEqual(block.selectors, self.selectors, 'incorrect selectors')
|
assert block.selectors == selectors
|
||||||
self.assertEqual(block.endpoints, self.endpoints, 'incorrect endpoints')
|
assert block.endpoints == endpoints
|
||||||
|
|
||||||
def test_eq(self):
|
def test_eq(self):
|
||||||
"""Test equality between two identical blocks"""
|
"""Test equality between two identical blocks"""
|
||||||
block1 = BC4Block.frombytes(self.block_bytes)
|
block1 = BC4Block.frombytes(block_bytes)
|
||||||
block2 = BC4Block.frombytes(self.block_bytes)
|
block2 = BC4Block.frombytes(block_bytes)
|
||||||
self.assertEqual(block1, block2, 'identical blocks not equal')
|
assert block1 == block2
|
||||||
|
|
||||||
def test_values_6(self):
|
def test_values_6(self):
|
||||||
"""Test values of a 6-value block"""
|
"""Test values of a 6-value block"""
|
||||||
block = BC4Block(8, 248, [[0] * 4] * 4)
|
block = BC4Block(8, 248, [[0] * 4] * 4)
|
||||||
|
|
||||||
self.assertEqual(block.values, [8, 248, 56, 104, 152, 200, 0, 255], 'incorrect values')
|
assert block.values == [8, 248, 56, 104, 152, 200, 0, 255]
|
||||||
self.assertTrue(block.is_6value, 'incorrect is_6value')
|
assert block.is_6value
|
||||||
|
|
||||||
def test_values_8(self):
|
def test_values_8(self):
|
||||||
"""Test values of an 8-value block"""
|
"""Test values of an 8-value block"""
|
||||||
block = BC4Block(240, 16, [[0] * 4] * 4)
|
block = BC4Block(240, 16, [[0] * 4] * 4)
|
||||||
|
|
||||||
self.assertEqual(block.values, [240, 16, 208, 176, 144, 112, 80, 48], 'incorrect values')
|
assert block.values == [240, 16, 208, 176, 144, 112, 80, 48]
|
||||||
self.assertFalse(block.is_6value, 'incorrect is_6value')
|
assert not block.is_6value
|
||||||
|
|
||||||
|
|
||||||
@parameterized_class(
|
# noinspection PyMethodMayBeStatic
|
||||||
("name", "w", "h", "wb", "hb"), [("8x8", 8, 8, 2, 2), ("9x9", 9, 9, 3, 3), ("7x7", 7, 7, 2, 2), ("7x9", 7, 9, 2, 3)]
|
@pytest.mark.parametrize('w', [7, 8, 9])
|
||||||
)
|
@pytest.mark.parametrize('h', [7, 8, 9])
|
||||||
class TestBC4Texture(unittest.TestCase):
|
class TestBC4Texture:
|
||||||
def setUp(self):
|
def test_dimensions(self, w, h):
|
||||||
self.tex = BC4Texture(self.w, self.h)
|
"""Test dimensions of BC4Texture in pixels, blocks, and bytes"""
|
||||||
self.nbytes = self.wb * self.hb * BC4Block.nbytes
|
tex = BC4Texture(w, h)
|
||||||
|
wb = math.ceil(w / 4)
|
||||||
|
hb = math.ceil(h / 4)
|
||||||
|
|
||||||
def test_size(self):
|
assert tex.nbytes == BC4Block.nbytes * wb * hb # block width x block height
|
||||||
"""Test size of BC4Texture in bytes"""
|
assert len(tex.tobytes()) == tex.nbytes
|
||||||
self.assertEqual(self.tex.nbytes, self.nbytes, 'incorrect texture size')
|
|
||||||
self.assertEqual(len(self.tex.tobytes()), self.nbytes, 'incorrect texture size from tobytes')
|
|
||||||
|
|
||||||
def test_dimensions(self):
|
assert tex.width == w
|
||||||
"""Test dimensions of BC4Texture in pixels"""
|
assert tex.height == h
|
||||||
self.assertEqual(self.tex.width, self.w, 'incorrect texture width')
|
assert tex.size == (w, h)
|
||||||
self.assertEqual(self.tex.height, self.h, 'incorrect texture height')
|
|
||||||
self.assertEqual(self.tex.size, (self.w, self.h), 'incorrect texture dimensions')
|
|
||||||
|
|
||||||
def test_dimensions_blocks(self):
|
assert tex.width_blocks == wb
|
||||||
"""Test dimensions of BC4Texture in blocks"""
|
assert tex.height_blocks == hb
|
||||||
self.assertEqual(self.tex.width_blocks, self.wb, 'incorrect texture width_blocks')
|
assert tex.size_blocks == (wb, hb)
|
||||||
self.assertEqual(self.tex.height_blocks, self.hb, 'incorrect texture width_blocks')
|
|
||||||
self.assertEqual(self.tex.size_blocks, (self.wb, self.hb), 'incorrect texture dimensions_blocks')
|
|
||||||
|
|
||||||
def test_blocks(self):
|
def test_blocks(self, w, h):
|
||||||
"""Test getting and setting blocks to BC4Texture"""
|
"""Test getting and setting blocks to BC4Texture"""
|
||||||
blocks = [[BC4Block.frombytes(bytes([x, y] + [0] * 6)) for x in range(self.wb)] for y in range(self.hb)]
|
tex = BC4Texture(w, h)
|
||||||
for x in range(self.wb):
|
|
||||||
for y in range(self.hb):
|
|
||||||
self.tex[x, y] = blocks[y][x]
|
|
||||||
|
|
||||||
b = self.tex.tobytes()
|
# generate garbage blocks with the x and y index in the first 2 bytes
|
||||||
for x in range(self.wb):
|
blocks = [
|
||||||
for y in range(self.hb):
|
[BC4Block.frombytes(bytes([x, y] + [0] * 6)) for x in range(tex.width_blocks)]
|
||||||
index = (x + (y * self.wb)) * BC4Block.nbytes
|
for y in range(tex.height_blocks)
|
||||||
tb = self.tex[x, y]
|
]
|
||||||
|
# assign those blocks to the texture
|
||||||
|
for x in range(tex.width_blocks):
|
||||||
|
for y in range(tex.height_blocks):
|
||||||
|
tex[x, y] = blocks[y][x]
|
||||||
|
|
||||||
|
# get the blocks and analyze
|
||||||
|
b = tex.tobytes()
|
||||||
|
for x in range(tex.width_blocks):
|
||||||
|
for y in range(tex.height_blocks):
|
||||||
|
index = (x + (y * tex.width_blocks)) * BC4Block.nbytes
|
||||||
|
tb = tex[x, y]
|
||||||
fb = BC4Block.frombytes(b[index : index + BC4Block.nbytes])
|
fb = BC4Block.frombytes(b[index : index + BC4Block.nbytes])
|
||||||
self.assertEqual(tb, blocks[y][x], 'incorrect block read from texture')
|
assert tb == blocks[y][x]
|
||||||
self.assertEqual(fb, blocks[y][x], 'incorrect block read from texture bytes')
|
assert fb == blocks[y][x]
|
||||||
|
|
||||||
self.assertEqual(self.tex[-1, -1], self.tex[self.wb - 1, self.hb - 1], 'incorrect negative subscripting')
|
def text_subscript(self, w, h):
|
||||||
|
"""Test BC4Texture subscripting for blocks"""
|
||||||
|
tex = BC4Texture(w, h)
|
||||||
|
|
||||||
with self.assertRaises(IndexError):
|
# ensure negative wraparound works
|
||||||
_ = self.tex[self.wb, self.hb]
|
assert tex[-1, -1] == tex[tex.width_blocks - 1, tex.height_blocks - 1]
|
||||||
with self.assertRaises(IndexError):
|
|
||||||
_ = self.tex[-1 - self.wb, -1 - self.hb]
|
|
||||||
|
|
||||||
def test_buffer(self):
|
with pytest.raises(IndexError):
|
||||||
"""Test the buffer protocol of BC4Texture"""
|
_ = tex[tex.width_blocks, tex.height_blocks]
|
||||||
mv = memoryview(self.tex)
|
with pytest.raises(IndexError):
|
||||||
|
_ = tex[-1 - tex.width_blocks, -1 - tex.height_blocks]
|
||||||
|
|
||||||
self.assertFalse(mv.readonly, 'buffer is readonly')
|
def test_buffer(self, w, h):
|
||||||
self.assertTrue(mv.c_contiguous, 'buffer is not contiguous')
|
"""Test the buffer protocol of BC1Texture"""
|
||||||
self.assertEqual(mv.nbytes, self.nbytes, 'buffer is the wrong size')
|
tex = BC4Texture(w, h)
|
||||||
self.assertEqual(mv.format, 'B', 'buffer has the wrong format')
|
mv = memoryview(tex)
|
||||||
|
|
||||||
data = b'\xF0\x10\x88\x86\x68\xAC\xCF\xFA' * self.wb * self.hb
|
data = block_bytes * tex.width_blocks * tex.height_blocks
|
||||||
mv[:] = data
|
mv[:] = data
|
||||||
self.assertEqual(mv.tobytes(), data, 'incorrect buffer data')
|
|
||||||
|
assert not mv.readonly
|
||||||
|
assert mv.c_contiguous
|
||||||
|
assert mv.nbytes == tex.nbytes
|
||||||
|
assert mv.format == 'B'
|
||||||
|
assert mv.tobytes() == data
|
||||||
|
|
||||||
|
|
||||||
class TestBC4Encoder(unittest.TestCase):
|
class TestBC4Encoder:
|
||||||
"""Test BC4Encoder"""
|
"""Test BC4Encoder"""
|
||||||
|
|
||||||
# 6-value blocks are not yet supported by the encoder so we only run one test
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.bc4_encoder = BC4Encoder(0)
|
|
||||||
|
|
||||||
def test_block(self):
|
def test_block(self):
|
||||||
"""Test encoder output with 8 value test block"""
|
"""Test encoder output with 8 value test block"""
|
||||||
out_tex = self.bc4_encoder.encode(BC4Blocks.eight_value.texture)
|
encoder = BC4Encoder(0)
|
||||||
|
out_tex = encoder.encode(BC4Blocks.eight_value.texture)
|
||||||
self.assertEqual(out_tex.size_blocks, (1, 1), 'encoded texture has multiple blocks')
|
|
||||||
|
|
||||||
out_block = out_tex[0, 0]
|
out_block = out_tex[0, 0]
|
||||||
|
|
||||||
self.assertFalse(out_block.is_6value, 'returned 6value mode')
|
assert out_tex.size_blocks == (1, 1)
|
||||||
self.assertEqual(out_block, BC4Blocks.eight_value.block, 'encoded block is incorrect')
|
|
||||||
|
assert not out_block.is_6value
|
||||||
|
assert out_block == BC4Blocks.eight_value.block
|
||||||
|
|
||||||
|
|
||||||
class TestBC4Decoder(unittest.TestCase):
|
@pytest.mark.parametrize('texture', [BC4Blocks.eight_value, BC4Blocks.six_value])
|
||||||
|
class TestBC4Decoder:
|
||||||
"""Test BC4Decoder"""
|
"""Test BC4Decoder"""
|
||||||
|
|
||||||
@classmethod
|
def test_block(self, texture):
|
||||||
def setUpClass(cls):
|
|
||||||
cls.bc4_decoder = BC4Decoder(0)
|
|
||||||
|
|
||||||
@parameterized.expand(
|
|
||||||
[
|
|
||||||
("8value", BC4Blocks.eight_value.block, BC4Blocks.eight_value.image),
|
|
||||||
("6value", BC4Blocks.six_value.block, BC4Blocks.six_value.image),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
def test_block(self, _, block, image):
|
|
||||||
"""Test decoder output for a single block"""
|
"""Test decoder output for a single block"""
|
||||||
|
block = texture.block
|
||||||
|
image = texture.image
|
||||||
|
decoder = BC4Decoder(0)
|
||||||
in_tex = BC4Texture(4, 4)
|
in_tex = BC4Texture(4, 4)
|
||||||
in_tex[0, 0] = block
|
in_tex[0, 0] = block
|
||||||
out_tex = self.bc4_decoder.decode(in_tex)
|
out_tex = decoder.decode(in_tex)
|
||||||
|
|
||||||
self.assertEqual(out_tex.size, (4, 4), 'decoded texture has incorrect dimensions')
|
assert out_tex.size == (4, 4)
|
||||||
|
|
||||||
out_img = Image.frombytes('RGBA', (4, 4), out_tex.tobytes())
|
out_img = Image.frombytes('RGBA', (4, 4), out_tex.tobytes())
|
||||||
img_diff = ImageChops.difference(out_img, image).convert('L')
|
img_diff = ImageChops.difference(out_img, image).convert('L')
|
||||||
img_hist = img_diff.histogram()
|
img_hist = img_diff.histogram()
|
||||||
|
|
||||||
self.assertEqual(16, img_hist[0], 'decoded block is incorrect')
|
assert img_hist[0] == 16
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
"""Test if everything is installed correctly"""
|
"""Test if everything is installed correctly"""
|
||||||
|
|
||||||
import unittest
|
|
||||||
import os.path
|
|
||||||
import quicktex
|
import quicktex
|
||||||
|
|
||||||
tests_path = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
|
|
||||||
|
class TestInstall:
|
||||||
class TestInstall(unittest.TestCase):
|
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
"""Test if the extension module version matches what setuptools returns"""
|
"""Test if the extension module version matches what setuptools returns"""
|
||||||
try:
|
try:
|
||||||
|
@ -1,58 +1,60 @@
|
|||||||
import unittest
|
|
||||||
import os.path
|
import os.path
|
||||||
from .images import image_path
|
|
||||||
from quicktex import RawTexture
|
import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
from quicktex import RawTexture
|
||||||
|
from .images import image_path
|
||||||
|
|
||||||
class TestRawTexture(unittest.TestCase):
|
|
||||||
|
class TestRawTexture:
|
||||||
boilerplate = Image.open(os.path.join(image_path, 'Boilerplate.png'))
|
boilerplate = Image.open(os.path.join(image_path, 'Boilerplate.png'))
|
||||||
boilerplate_bytes = boilerplate.tobytes('raw', 'RGBX')
|
boilerplate_bytes = boilerplate.tobytes('raw', 'RGBX')
|
||||||
width, height = boilerplate.size
|
width, height = boilerplate.size
|
||||||
size = width * height * 4
|
nbytes = width * height * 4
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.tex = RawTexture(self.width, self.height)
|
|
||||||
|
|
||||||
def test_size(self):
|
def test_size(self):
|
||||||
"""Test byte size and image dimensions"""
|
"""Test byte size and image dimensions"""
|
||||||
self.assertEqual(self.tex.nbytes, self.size, "incorrect texture byte size")
|
tex = RawTexture(self.width, self.height)
|
||||||
self.assertEqual(self.tex.width, self.width, "incorrect texture width")
|
assert tex.nbytes == self.nbytes
|
||||||
self.assertEqual(self.tex.height, self.height, "incorrect texture height")
|
assert tex.width == self.width
|
||||||
self.assertEqual(self.tex.size, (self.width, self.height), "incorrect texture dimensions")
|
assert tex.height == self.height
|
||||||
|
assert tex.size == (self.width, self.height)
|
||||||
|
|
||||||
def test_pixels(self):
|
def test_pixels(self):
|
||||||
"""Test getting and setting pixel values"""
|
"""Test getting and setting pixel values"""
|
||||||
|
tex = RawTexture(self.width, self.height)
|
||||||
color1 = (69, 13, 12, 0) # totally random color
|
color1 = (69, 13, 12, 0) # totally random color
|
||||||
color2 = (19, 142, 93, 44)
|
color2 = (19, 142, 93, 44)
|
||||||
|
|
||||||
self.tex[0, 0] = color1
|
tex[0, 0] = color1
|
||||||
self.tex[-1, -1] = color2
|
tex[-1, -1] = color2
|
||||||
data = self.tex.tobytes()
|
data = tex.tobytes()
|
||||||
|
|
||||||
self.assertEqual(self.tex[0, 0], color1)
|
assert tex[0, 0] == color1
|
||||||
self.assertEqual(self.tex[-1, -1], color2)
|
assert tex[-1, -1] == color2
|
||||||
self.assertEqual(tuple(data[0:4]), color1)
|
assert tuple(data[0:4]) == color1
|
||||||
self.assertEqual(tuple(data[-4:]), color2)
|
assert tuple(data[-4:]) == color2
|
||||||
|
|
||||||
with self.assertRaises(IndexError):
|
with pytest.raises(IndexError):
|
||||||
thing = self.tex[self.width, self.height]
|
_ = tex[self.width, self.height]
|
||||||
with self.assertRaises(IndexError):
|
with pytest.raises(IndexError):
|
||||||
thing = self.tex[-1 - self.width, -1 - self.height]
|
_ = tex[-1 - self.width, -1 - self.height]
|
||||||
|
|
||||||
def test_buffer(self):
|
def test_buffer(self):
|
||||||
"""Test the Buffer protocol implementation for RawTexture"""
|
"""Test the Buffer protocol implementation for RawTexture"""
|
||||||
mv = memoryview(self.tex)
|
tex = RawTexture(self.width, self.height)
|
||||||
|
mv = memoryview(tex)
|
||||||
self.assertFalse(mv.readonly, 'buffer is readonly')
|
|
||||||
self.assertTrue(mv.c_contiguous, 'buffer is not contiguous')
|
|
||||||
self.assertEqual(mv.nbytes, self.size, 'buffer is the wrong size')
|
|
||||||
self.assertEqual(mv.format, 'B', 'buffer has the wrong format')
|
|
||||||
|
|
||||||
mv[:] = self.boilerplate_bytes
|
mv[:] = self.boilerplate_bytes
|
||||||
self.assertEqual(mv.tobytes(), self.boilerplate_bytes, 'incorrect buffer data')
|
|
||||||
|
assert not mv.readonly
|
||||||
|
assert mv.c_contiguous
|
||||||
|
assert mv.nbytes == self.nbytes
|
||||||
|
assert mv.format == 'B'
|
||||||
|
assert mv.tobytes() == self.boilerplate_bytes
|
||||||
|
assert mv.tobytes() == tex.tobytes()
|
||||||
|
|
||||||
def test_frombytes(self):
|
def test_frombytes(self):
|
||||||
"""Test the frombytes factory function"""
|
"""Test the frombytes factory function"""
|
||||||
bytetex = RawTexture.frombytes(self.boilerplate_bytes, *self.boilerplate.size)
|
bytetex = RawTexture.frombytes(self.boilerplate_bytes, *self.boilerplate.size)
|
||||||
self.assertEqual(self.boilerplate_bytes, bytetex.tobytes(), 'Incorrect bytes after writing to buffer')
|
assert self.boilerplate_bytes == bytetex.tobytes()
|
||||||
|
Loading…
Reference in New Issue
Block a user