Add unittest framework

Adding a unittest framework to the project to enhance debugging capabilities, document expected (and unexpected) behaviors, and to prevent regression failures.
pull/95/head
dhunt1342 2019-11-16 10:49:20 -05:00
rodzic 0b73e89d51
commit 2f9749bd60
4 zmienionych plików z 277 dodań i 0 usunięć

16
test/__init__.py 100644
Wyświetl plik

@ -0,0 +1,16 @@
# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019. Rune B. Broberg
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

Wyświetl plik

@ -0,0 +1,83 @@
# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019. Rune B. Broberg
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import sys
import unittest
# Import targets to be tested
from NanoVNASaver import RFTools
rft = RFTools.RFTools()
class TestCases(unittest.TestCase):
'''
def formatSweepFrequency(freq: int,
mindigits: int = 2,
appendHz: bool = True,
insertSpace: bool = False,
countDot: bool = True,
assumeInfinity: bool = True) -> str:
'''
def test_basicIntegerValues(self):
# simple well-formed integers with no trailing zeros. Most importantly
# even with default mindigits, there is no loss of accuracy in the
# result. Returned values are not truncated if result would lose
# meaningful data.
self.assertEqual(rft.formatSweepFrequency(1), '1Hz')
self.assertEqual(rft.formatSweepFrequency(12), '12Hz')
self.assertEqual(rft.formatSweepFrequency(123), '123Hz')
self.assertEqual(rft.formatSweepFrequency(1234), '1.234kHz')
self.assertEqual(rft.formatSweepFrequency(12345), '12.345kHz')
self.assertEqual(rft.formatSweepFrequency(123456), '123.456kHz')
self.assertEqual(rft.formatSweepFrequency(1234567), '1.234567MHz')
self.assertEqual(rft.formatSweepFrequency(12345678), '12.345678MHz')
self.assertEqual(rft.formatSweepFrequency(123456789), '123.456789MHz')
def test_defaultMinDigits(self):
# simple integers with trailing zeros.
# DEFAULT behavior retains 2 digits after the period, mindigits=2.
self.assertEqual(rft.formatSweepFrequency(1000), '1.00kHz')
self.assertEqual(rft.formatSweepFrequency(10000), '10.00kHz')
self.assertEqual(rft.formatSweepFrequency(100000), '100.00kHz')
self.assertEqual(rft.formatSweepFrequency(1000000), '1.00MHz')
def test_nonDefaultMinDigits(self):
# simple integers with trailing zeros. setting mindigit value to something
# other than default, where trailing zeros >= mindigits, the number of
# zeros shown is equal to mindigits value.
self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=0), '1MHz')
self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=1), '1.0MHz')
self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=3), '1.000MHz')
self.assertEqual(rft.formatSweepFrequency(10000000, mindigits=4), '10.0000MHz')
self.assertEqual(rft.formatSweepFrequency(100000000, mindigits=5), '100.00000MHz')
self.assertEqual(rft.formatSweepFrequency(1000000000, mindigits=6), '1.000000GHz')
# where trailing zeros < mindigits, only available zeros are shown, if the
# result includes no decimal places (i.e. Hz values).
self.assertEqual(rft.formatSweepFrequency(1, mindigits=4), '1Hz')
self.assertEqual(rft.formatSweepFrequency(10, mindigits=4), '10Hz')
self.assertEqual(rft.formatSweepFrequency(100, mindigits=4), '100Hz')
# but where a decimal exists, and mindigits > number of available zeroes,
# this results in extra zeroes being padded into result, even into sub-Hz
# resolution. This is not useful for this application.
# TODO: Consider post-processing result for maxdigits based on SI unit.
self.assertEqual(rft.formatSweepFrequency(1000, mindigits=5), '1.00000kHz')
self.assertEqual(rft.formatSweepFrequency(1000, mindigits=10), '1.0000000000kHz')
if __name__ == '__main__':
unittest.main(verbosity=2)

Wyświetl plik

@ -0,0 +1,141 @@
# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019. Rune B. Broberg
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import sys
import unittest
# Import targets to be tested
from NanoVNASaver import RFTools
rft = RFTools.RFTools()
class TestCases(unittest.TestCase):
def test_basicSIUnits(self):
# simple well-formed integers with correct SI units
self.assertEqual(rft.parseFrequency('123Hz'), 123)
self.assertEqual(rft.parseFrequency('123456Hz'), 123456)
self.assertEqual(rft.parseFrequency('123kHz'), 123000)
self.assertEqual(rft.parseFrequency('123456kHz'), 123456000)
self.assertEqual(rft.parseFrequency('123MHz'), 123000000)
self.assertEqual(rft.parseFrequency('123456MHz'), 123456000000)
self.assertEqual(rft.parseFrequency('123GHz'), 123000000000)
self.assertEqual(rft.parseFrequency('123456GHz'), 123456000000000)
def test_commonMistakeKHz_vs_kHz(self):
# some poorly formatted values that still work as expected
self.assertEqual(rft.parseFrequency('123kHz'), 123000)
self.assertEqual(rft.parseFrequency('123KHz'), 123000)
def test_illegalInputValues(self):
# poorly formatted inputs that are identified as illegal
self.assertEqual(rft.parseFrequency('Junk'), -1)
self.assertEqual(rft.parseFrequency('Garbage'), -1)
self.assertEqual(rft.parseFrequency('123.Junk'), -1)
def test_missingDigitsAfterPeriod(self):
# some poorly formatted values that still work as expected
self.assertEqual(rft.parseFrequency('123.'), 123)
self.assertEqual(rft.parseFrequency('123.Hz'), 123)
self.assertEqual(rft.parseFrequency('123.kHz'), 123000)
self.assertEqual(rft.parseFrequency('123.MHz'), 123000000)
self.assertEqual(rft.parseFrequency('123.GHz'), 123000000000)
def test_unusualSIUnits(self):
#######################################################################
# Original behavior: unusual SI values that were legal, but inappropriate
# for this application provided unexpected outputs. This behavior was
# based on the full set of SI prefixes previously defined in SITools (below).
#PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m",
# "", "k", "M", "G", "T", "P", "E", "Z", "Y")
# New Behavior: After the reduction of legal SI values defined in SITools
# (below), these now return a -1 failure code.
# PREFIXES = ("", "k", "M", "G")
#######################################################################
'''
self.assertEqual(rft.parseFrequency('123EHz'), 123000000000000000000)
self.assertEqual(rft.parseFrequency('123PHz'), 123000000000000000)
self.assertEqual(rft.parseFrequency('123THz'), 123000000000000)
self.assertEqual(rft.parseFrequency('123YHz'), 122999999999999998473273344)
self.assertEqual(rft.parseFrequency('123ZHz'), 123000000000000002097152)
self.assertEqual(rft.parseFrequency('123aHz'), 0)
self.assertEqual(rft.parseFrequency('123fHz'), 0)
self.assertEqual(rft.parseFrequency('123mHz'), 0)
self.assertEqual(rft.parseFrequency('123nHz'), 0)
self.assertEqual(rft.parseFrequency('123pHz'), 0)
self.assertEqual(rft.parseFrequency('123yHz'), 0)
self.assertEqual(rft.parseFrequency('123zHz'), 0)
'''
self.assertEqual(rft.parseFrequency('123EHz'), -1)
self.assertEqual(rft.parseFrequency('123PHz'), -1)
self.assertEqual(rft.parseFrequency('123THz'), -1)
self.assertEqual(rft.parseFrequency('123YHz'), -1)
self.assertEqual(rft.parseFrequency('123ZHz'), -1)
self.assertEqual(rft.parseFrequency('123aHz'), -1)
self.assertEqual(rft.parseFrequency('123fHz'), -1)
self.assertEqual(rft.parseFrequency('123mHz'), -1)
self.assertEqual(rft.parseFrequency('123nHz'), -1)
self.assertEqual(rft.parseFrequency('123pHz'), -1)
self.assertEqual(rft.parseFrequency('123yHz'), -1)
self.assertEqual(rft.parseFrequency('123zHz'), -1)
def test_partialHzText(self):
#######################################################################
# Previous behavior for accidentally missing the H in Hz, resulted in
# detection of 'z' SI unit (zepto = 10^-21), which then rounded to 0.
# After reduction of legal SI values in SITools, this now returns a -1
# failure code.
#######################################################################
'''
self.assertEqual(rft.parseFrequency('123z'), 0)
self.assertEqual(rft.parseFrequency('123.z'), 0)
self.assertEqual(rft.parseFrequency('1.23z'), 0)
'''
self.assertEqual(rft.parseFrequency('123z'), -1)
self.assertEqual(rft.parseFrequency('123.z'), -1)
self.assertEqual(rft.parseFrequency('1.23z'), -1)
def test_basicExponentialNotation(self):
# check basic exponential notation
self.assertEqual(rft.parseFrequency('123e3'), 123000)
self.assertEqual(rft.parseFrequency('123e6'), 123000000)
self.assertEqual(rft.parseFrequency('123e9'), 123000000000)
self.assertEqual(rft.parseFrequency('123e4'), 1230000)
self.assertEqual(rft.parseFrequency('123e12'), 123000000000000)
self.assertEqual(rft.parseFrequency('123e18'), 123000000000000000000)
def test_negativeExponentialNotation(self):
# negative exponential values resulting in N < 0, return 0
self.assertEqual(rft.parseFrequency('123e-3'), 0)
self.assertEqual(rft.parseFrequency('1234e-4'), 0)
self.assertEqual(rft.parseFrequency('12345e-5'), 0)
self.assertEqual(rft.parseFrequency('12345678e-8'), 0)
# negative exponential values resulting in N > 0, return N
self.assertEqual(rft.parseFrequency('100000e-5'), 1)
self.assertEqual(rft.parseFrequency('100000e-4'), 10)
self.assertEqual(rft.parseFrequency('100000e-3'), 100)
self.assertEqual(rft.parseFrequency('100000e-2'), 1000)
self.assertEqual(rft.parseFrequency('100000e-1'), 10000)
def test_multiplePeriods(self):
# multiple periods are properly detected as bad
self.assertEqual(rft.parseFrequency('123..Hz'), -1)
self.assertEqual(rft.parseFrequency('123...Hz'), -1)
self.assertEqual(rft.parseFrequency('123....Hz'), -1)
self.assertEqual(rft.parseFrequency('1.23.Hz'), -1)
if __name__ == '__main__':
unittest.main(verbosity=2)

37
test_master.py 100644
Wyświetl plik

@ -0,0 +1,37 @@
# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019. Rune B. Broberg
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import sys
import unittest
###############################################################################
#
# Executing this file initiates the discovery and execution of all unittests
# in the "test" directory, with filenames starting with "test" that have a
# TestCases(unittest.TestCase) Class, which has class functions starting with
# "test_". This provides a simple test framework that is easily expandable by
# simply adding new "test_xxx.py" files into the test directory.
#
###############################################################################
if __name__ == '__main__':
loader = unittest.TestLoader()
tests = loader.discover('.')
testRunner = unittest.runner.TextTestRunner(
failfast=False,
verbosity=2)
testRunner.run(tests)