Merge pull request #1 from jpf/master

test suite
This commit is contained in:
András Veres-Szentkirályi 2013-06-19 01:20:57 -07:00
commit f14d0a8e66
13 changed files with 17825 additions and 3 deletions

3
requirements.txt Normal file
View file

@ -0,0 +1,3 @@
PIL==1.1.7
mock==1.0.1
nose==1.3.0

10
sstv.py
View file

@ -26,6 +26,7 @@ class SSTV(object):
BITS_TO_STRUCT = {8: 'b', 16: 'h'} BITS_TO_STRUCT = {8: 'b', 16: 'h'}
def write_wav(self, filename): def write_wav(self, filename):
"""writes the whole image to a Microsoft WAV file"""
fmt = '<' + self.BITS_TO_STRUCT[self.bits] fmt = '<' + self.BITS_TO_STRUCT[self.bits]
data = ''.join(struct.pack(fmt, b) for b in self.gen_samples()) data = ''.join(struct.pack(fmt, b) for b in self.gen_samples())
with closing(wave.open(filename, 'wb')) as wav: with closing(wave.open(filename, 'wb')) as wav:
@ -35,7 +36,8 @@ class SSTV(object):
wav.writeframes(data) wav.writeframes(data)
def gen_samples(self): def gen_samples(self):
"""generates bits from gen_values""" """generates discrete samples from gen_values(), performing quantization according to the bits per sample value given during construction
"""
max_value = 2 ** self.bits max_value = 2 ** self.bits
alias = 1 / max_value alias = 1 / max_value
amp = max_value / 2 amp = max_value / 2
@ -46,7 +48,8 @@ class SSTV(object):
yield max(min(highest, sample), lowest) yield max(min(highest, sample), lowest)
def gen_values(self): def gen_values(self):
"""generates -1 .. +1 values from freq_bits""" """generates samples between -1 and +1 from gen_freq_bits(), performing sampling according to the samples per second value given during construction
"""
spms = self.samples_per_sec / 1000 spms = self.samples_per_sec / 1000
param = 0 param = 0
samples = 0 samples = 0
@ -61,7 +64,8 @@ class SSTV(object):
samples -= tx samples -= tx
def gen_freq_bits(self): def gen_freq_bits(self):
"""generates (freq, msec) tuples from image""" """generates tuples (freq, msec) that describe a sine wave segment with frequency in Hz and duration in ms
"""
yield FREQ_VIS_START, MSEC_VIS_START yield FREQ_VIS_START, MSEC_VIS_START
yield FREQ_SYNC, MSEC_VIS_SYNC yield FREQ_SYNC, MSEC_VIS_SYNC
yield FREQ_VIS_START, MSEC_VIS_START yield FREQ_VIS_START, MSEC_VIS_START

BIN
tests/assets/320x256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

45
tests/test_color.py Normal file
View file

@ -0,0 +1,45 @@
import unittest
from itertools import islice
import pickle
from PIL import Image
import color
class TestMartinM1(unittest.TestCase):
def setUp(self):
self.image = Image.new('RGB', (320, 256))
self.s = color.MartinM1(self.image, 48000, 16)
lena = Image.open('tests/assets/320x256.png')
self.lena = color.MartinM1(lena, 48000, 16)
def test_gen_freq_bits(self):
expected = pickle.load(open("tests/assets/MartinM1_freq_bits.p"))
actual = list(islice(self.s.gen_freq_bits(), 0, 1000))
self.assertEqual(expected, actual)
def test_gen_freq_bits_lena(self):
expected = pickle.load(open("tests/assets/MartinM1_freq_bits_lena.p"))
actual = list(islice(self.lena.gen_freq_bits(), 0, 1000))
self.assertEqual(expected, actual)
def test_encode_line(self):
zeroth = list(self.s.encode_line(0))
first = list(self.s.encode_line(1))
tenth = list(self.s.encode_line(10))
eleventh = list(self.s.encode_line(11))
self.assertEqual(zeroth, first)
self.assertEqual(tenth, eleventh)
self.assertEqual(zeroth, eleventh)
def test_encode_line_lena(self):
self.maxDiff = None
line_numbers = [1, 10, 100]
for line in line_numbers:
file = open("tests/assets/MartinM1_encode_line_lena%d.p" % line)
expected = pickle.load(file)
actual = list(self.lena.encode_line(line))
self.assertEqual(expected, actual)

78
tests/test_sstv.py Normal file
View file

@ -0,0 +1,78 @@
import unittest
from itertools import islice
import pickle
import mock
from mock import MagicMock
from StringIO import StringIO
import hashlib
import sstv
from sstv import SSTV
class TestSSTV(unittest.TestCase):
def setUp(self):
self.s = SSTV(False, 48000, 16)
self.s.VIS_CODE = 0x00
self.s.SYNC = 7
def test_horizontal_sync(self):
horizontal_sync = self.s.horizontal_sync()
expected = (1200, self.s.SYNC)
actual = horizontal_sync.next()
self.assertEqual(expected, actual)
def test_gen_freq_bits(self):
gen_freq_bits = self.s.gen_freq_bits()
expected = [(1900, 300),
(1200, 10),
(1900, 300),
(1200, 30),
(1300, 30),
(1300, 30),
(1300, 30),
(1300, 30),
(1300, 30),
(1300, 30),
(1300, 30),
(1300, 30),
(1200, 30)]
actual = list(islice(gen_freq_bits, 0, 1000))
self.assertEqual(expected, actual)
# FIXME: Instead of using a test fixture, 'expected' should be synthesized?
def test_gen_values(self):
gen_values = self.s.gen_values()
expected = pickle.load(open("tests/assets/SSTV_gen_values.p"))
actual = list(islice(gen_values, 0, 1000))
self.assertEqual(expected, actual)
def test_gen_samples(self):
gen_values = self.s.gen_samples()
# gen_samples uses random to avoid quantization noise
# by using additive noise, so there's always a chance
# of running the code two consecutive times on the same machine
# and having different results.
# https://en.wikipedia.org/wiki/Quantization_%28signal_processing%29
sstv.random = MagicMock(return_value=0.4) # xkcd:221
expected = pickle.load(open("tests/assets/SSTV_gen_samples.p"))
actual = list(islice(gen_values, 0, 1000))
self.assertEqual(expected, actual)
def test_write_wav(self):
self.maxDiff = None
sio = StringIO()
sio.close = MagicMock() # ignore close() so we can .getvalue()
mock_open = MagicMock(return_value=sio)
with mock.patch('__builtin__.open', mock_open):
self.s.write_wav('unittest.wav')
expected = 'bf61c82e96aed1370d5c1753d87729db'
data = sio.getvalue()
actual = hashlib.md5(data).hexdigest()
self.assertEqual(expected, actual)
def test_init(self):
self.assertEqual(self.s.image, False)
self.assertEqual(self.s.samples_per_sec, 48000)
self.assertEqual(self.s.bits, 16)