import pytest
import numpy as np
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from alice_zip.generators import (
ProceduralGenerator,
PerlinNoiseGenerator,
FourierGenerator,
SineWaveGenerator,
PolynomialGenerator,
get_generator,
decompress_from_params,
decompress_from_lzma,
GeneratorType,
GeneratorParameters,
DataDomain,
CompressionEngine,
CompressionResult,
)
class TestPerlinNoiseGenerator:
def setup_method(self):
self.generator = PerlinNoiseGenerator()
def test_generate_2d_basic(self):
params = GeneratorParameters(
generator_type=GeneratorType.PERLIN_NOISE,
seed=42,
parameters={'scale': 10.0, 'octaves': 4},
output_shape=(64, 64)
)
result = self.generator.generate(params)
assert result.shape == (64, 64)
assert result.dtype == np.float32
assert 0.0 <= result.min() <= result.max() <= 1.0
def test_generate_reproducibility(self):
params = GeneratorParameters(
generator_type=GeneratorType.PERLIN_NOISE,
seed=12345,
parameters={'scale': 5.0, 'octaves': 3},
output_shape=(32, 32)
)
result1 = self.generator.generate(params)
result2 = self.generator.generate(params)
np.testing.assert_array_equal(result1, result2)
def test_generate_different_seeds(self):
params1 = GeneratorParameters(
generator_type=GeneratorType.PERLIN_NOISE,
seed=1,
parameters={'scale': 5.0},
output_shape=(32, 32)
)
params2 = GeneratorParameters(
generator_type=GeneratorType.PERLIN_NOISE,
seed=2,
parameters={'scale': 5.0},
output_shape=(32, 32)
)
result1 = self.generator.generate(params1)
result2 = self.generator.generate(params2)
assert not np.array_equal(result1, result2)
def test_generate_different_scales(self):
params_small = GeneratorParameters(
generator_type=GeneratorType.PERLIN_NOISE,
seed=42,
parameters={'scale': 5.0, 'octaves': 4},
output_shape=(64, 64)
)
params_large = GeneratorParameters(
generator_type=GeneratorType.PERLIN_NOISE,
seed=42,
parameters={'scale': 20.0, 'octaves': 4},
output_shape=(64, 64)
)
result_small = self.generator.generate(params_small)
result_large = self.generator.generate(params_large)
assert not np.array_equal(result_small, result_large)
def test_generate_with_persistence(self):
params_low = GeneratorParameters(
generator_type=GeneratorType.PERLIN_NOISE,
seed=42,
parameters={'scale': 10.0, 'octaves': 4, 'persistence': 0.3},
output_shape=(32, 32)
)
params_high = GeneratorParameters(
generator_type=GeneratorType.PERLIN_NOISE,
seed=42,
parameters={'scale': 10.0, 'octaves': 4, 'persistence': 0.7},
output_shape=(32, 32)
)
result_low = self.generator.generate(params_low)
result_high = self.generator.generate(params_high)
assert not np.array_equal(result_low, result_high)
class TestFourierGenerator:
def setup_method(self):
self.generator = FourierGenerator()
def test_generate_single_frequency(self):
params = GeneratorParameters(
generator_type=GeneratorType.FOURIER,
seed=0,
parameters={
'coefficients': [(10, 1.0, 0.0)], 'dc_offset': 0.0
},
output_shape=(1000,)
)
result = self.generator.generate(params)
assert result.shape == (1000,)
assert result.dtype == np.float32
assert -1.5 <= result.min() <= result.max() <= 1.5
def test_generate_multiple_frequencies(self):
params = GeneratorParameters(
generator_type=GeneratorType.FOURIER,
seed=0,
parameters={
'coefficients': [
(5, 1.0, 0.0),
(10, 0.5, np.pi/4),
(15, 0.25, np.pi/2)
],
'dc_offset': 0.5
},
output_shape=(1000,)
)
result = self.generator.generate(params)
assert result.shape == (1000,)
assert np.mean(result) > 0.3
def test_generate_dc_only(self):
params = GeneratorParameters(
generator_type=GeneratorType.FOURIER,
seed=0,
parameters={
'coefficients': [],
'dc_offset': 5.0
},
output_shape=(100,)
)
result = self.generator.generate(params)
np.testing.assert_array_almost_equal(result, np.full(100, 5.0), decimal=5)
class TestSineWaveGenerator:
def setup_method(self):
self.generator = SineWaveGenerator()
def test_generate_basic_sine(self):
params = GeneratorParameters(
generator_type=GeneratorType.FOURIER, seed=0,
parameters={
'frequency': 10.0,
'amplitude': 1.0,
'phase': 0.0,
'dc_offset': 0.0,
'is_simple_sine': True
},
output_shape=(1000,)
)
result = self.generator.generate(params)
assert result.shape == (1000,)
assert result.dtype == np.float32
np.testing.assert_almost_equal(np.max(np.abs(result)), 1.0, decimal=2)
def test_generate_with_phase(self):
params_no_phase = GeneratorParameters(
generator_type=GeneratorType.FOURIER,
seed=0,
parameters={
'frequency': 5.0,
'amplitude': 1.0,
'phase': 0.0,
'dc_offset': 0.0
},
output_shape=(1000,)
)
params_with_phase = GeneratorParameters(
generator_type=GeneratorType.FOURIER,
seed=0,
parameters={
'frequency': 5.0,
'amplitude': 1.0,
'phase': np.pi/2, 'dc_offset': 0.0
},
output_shape=(1000,)
)
result_no_phase = self.generator.generate(params_no_phase)
result_with_phase = self.generator.generate(params_with_phase)
assert not np.allclose(result_no_phase, result_with_phase)
def test_generate_with_dc_offset(self):
params = GeneratorParameters(
generator_type=GeneratorType.FOURIER,
seed=0,
parameters={
'frequency': 10.0,
'amplitude': 1.0,
'phase': 0.0,
'dc_offset': 5.0
},
output_shape=(1000,)
)
result = self.generator.generate(params)
np.testing.assert_almost_equal(np.mean(result), 5.0, decimal=1)
class TestPolynomialGenerator:
def setup_method(self):
self.generator = PolynomialGenerator()
def test_generate_linear(self):
params = GeneratorParameters(
generator_type=GeneratorType.POLYNOMIAL,
seed=0,
parameters={'coefficients': [2.0, 1.0]}, output_shape=(100,)
)
result = self.generator.generate(params)
assert result.shape == (100,)
assert result[0] < result[-1]
def test_generate_quadratic(self):
params = GeneratorParameters(
generator_type=GeneratorType.POLYNOMIAL,
seed=0,
parameters={'coefficients': [1.0, 0.0, 0.0]}, output_shape=(100,)
)
result = self.generator.generate(params)
assert result.shape == (100,)
assert result.min() >= -0.01
def test_generate_constant(self):
params = GeneratorParameters(
generator_type=GeneratorType.POLYNOMIAL,
seed=0,
parameters={'coefficients': [5.0]}, output_shape=(50,)
)
result = self.generator.generate(params)
np.testing.assert_array_almost_equal(result, np.full(50, 5.0), decimal=5)
def test_generate_cubic(self):
params = GeneratorParameters(
generator_type=GeneratorType.POLYNOMIAL,
seed=0,
parameters={'coefficients': [1.0, -2.0, 1.0, 0.0]}, output_shape=(100,)
)
result = self.generator.generate(params)
assert result.shape == (100,)
assert result.dtype == np.float32
class TestGetGenerator:
def test_get_perlin_generator(self):
gen = get_generator(GeneratorType.PERLIN_NOISE)
assert isinstance(gen, PerlinNoiseGenerator)
def test_get_fourier_generator(self):
gen = get_generator(GeneratorType.FOURIER)
assert isinstance(gen, FourierGenerator)
def test_get_polynomial_generator(self):
gen = get_generator(GeneratorType.POLYNOMIAL)
assert isinstance(gen, PolynomialGenerator)
def test_get_unknown_generator(self):
with pytest.raises(ValueError, match="Unknown generator type"):
get_generator(GeneratorType.L_SYSTEM)
class TestDecompressFromParams:
def test_decompress_simple_sine(self):
params = GeneratorParameters(
generator_type=GeneratorType.FOURIER,
seed=0,
parameters={
'frequency': 20.0,
'amplitude': 2.0,
'phase': 0.0,
'dc_offset': 1.0,
'is_simple_sine': True
},
output_shape=(500,)
)
result = decompress_from_params(params)
assert result.shape == (500,)
assert isinstance(result, np.ndarray)
def test_decompress_polynomial(self):
params = GeneratorParameters(
generator_type=GeneratorType.POLYNOMIAL,
seed=0,
parameters={'coefficients': [1.0, -2.0, 1.0]}, output_shape=(200,)
)
result = decompress_from_params(params)
assert result.shape == (200,)
def test_decompress_fourier(self):
params = GeneratorParameters(
generator_type=GeneratorType.FOURIER,
seed=0,
parameters={
'coefficients': [(5, 1.0, 0.0), (10, 0.5, 0.0)],
'dc_offset': 0.0
},
output_shape=(1000,)
)
result = decompress_from_params(params)
assert result.shape == (1000,)
def test_decompress_perlin(self):
params = GeneratorParameters(
generator_type=GeneratorType.PERLIN_NOISE,
seed=42,
parameters={'scale': 10.0, 'octaves': 4},
output_shape=(64, 64)
)
result = decompress_from_params(params)
assert result.shape == (64, 64)
class TestDecompressFromLzma:
def test_decompress_lzma_float32(self):
import lzma
original = np.array([1.0, 2.0, 3.0, 4.0, 5.0], dtype=np.float32)
compressed = lzma.compress(original.tobytes())
result = decompress_from_lzma(compressed, (5,), dtype='float32')
np.testing.assert_array_equal(result, original)
def test_decompress_lzma_2d(self):
import lzma
original = np.arange(100, dtype=np.float32).reshape(10, 10)
compressed = lzma.compress(original.tobytes())
result = decompress_from_lzma(compressed, (10, 10), dtype='float32')
np.testing.assert_array_equal(result, original)
class TestGeneratorParameters:
def test_creation(self):
params = GeneratorParameters(
generator_type=GeneratorType.PERLIN_NOISE,
seed=42,
parameters={'scale': 10.0},
output_shape=(64, 64)
)
assert params.generator_type == GeneratorType.PERLIN_NOISE
assert params.seed == 42
assert params.parameters == {'scale': 10.0}
assert params.output_shape == (64, 64)
assert params.dtype == "float32"
def test_custom_dtype(self):
params = GeneratorParameters(
generator_type=GeneratorType.POLYNOMIAL,
seed=0,
parameters={'coefficients': [1.0]},
output_shape=(100,),
dtype="float64"
)
assert params.dtype == "float64"
class TestDataDomain:
def test_domain_values(self):
assert DataDomain.NUMERICAL.value == "numerical_data"
assert DataDomain.TEXTURE_PATTERN.value == "texture_pattern"
assert DataDomain.NATURAL_IMAGE.value == "natural_image"
assert DataDomain.AUDIO_MUSIC.value == "audio_music"
assert DataDomain.TEXT.value == "text"
assert DataDomain.GENERIC.value == "generic"
class TestCompressionEngine:
def test_engine_values(self):
assert CompressionEngine.PROCEDURAL.value == "procedural_generation"
assert CompressionEngine.NEURAL_CODEC.value == "neural_codec"
assert CompressionEngine.FUNCTION_FITTER.value == "function_fitter"
assert CompressionEngine.FALLBACK_LZMA.value == "fallback_lzma"
class TestCompressionResult:
def test_creation(self):
result = CompressionResult(
success=True,
engine_used=CompressionEngine.PROCEDURAL,
original_size=1000,
compressed_size=100,
compression_ratio=10.0
)
assert result.success is True
assert result.engine_used == CompressionEngine.PROCEDURAL
assert result.original_size == 1000
assert result.compressed_size == 100
assert result.compression_ratio == 10.0
def test_has_residual(self):
result_no_residual = CompressionResult(
success=True,
engine_used=CompressionEngine.PROCEDURAL,
original_size=1000,
compressed_size=100,
compression_ratio=10.0,
residual_data=None
)
result_with_residual = CompressionResult(
success=True,
engine_used=CompressionEngine.PROCEDURAL,
original_size=1000,
compressed_size=100,
compression_ratio=10.0,
residual_data=b'some residual'
)
assert result_no_residual.has_residual is False
assert result_with_residual.has_residual is True
def test_total_compressed_size(self):
result = CompressionResult(
success=True,
engine_used=CompressionEngine.PROCEDURAL,
original_size=1000,
compressed_size=100,
compression_ratio=10.0,
residual_data=b'1234567890' )
assert result.total_compressed_size == 110
def test_effective_ratio(self):
result = CompressionResult(
success=True,
engine_used=CompressionEngine.PROCEDURAL,
original_size=1000,
compressed_size=100,
compression_ratio=10.0,
residual_data=b'1234567890' )
assert abs(result.effective_ratio - (1000 / 110)) < 0.01
class TestGeneratorType:
def test_generator_types(self):
assert GeneratorType.PERLIN_NOISE.value == "perlin_noise"
assert GeneratorType.SIMPLEX_NOISE.value == "simplex_noise"
assert GeneratorType.FRACTAL.value == "fractal"
assert GeneratorType.L_SYSTEM.value == "l_system"
assert GeneratorType.CELLULAR_AUTOMATA.value == "cellular_automata"
assert GeneratorType.WAVE_FUNCTION.value == "wave_function"
assert GeneratorType.FOURIER.value == "fourier"
assert GeneratorType.POLYNOMIAL.value == "polynomial"
assert GeneratorType.SPLINE.value == "spline"
if __name__ == '__main__':
pytest.main([__file__, '-v'])