Add unit tests

This commit is contained in:
Elara 2024-11-12 23:39:13 -08:00
parent fbd62512d1
commit ab9a7bb34e
4 changed files with 440 additions and 0 deletions

0
test/__init__.py Normal file
View File

137
test/test_interp.py Normal file
View File

@ -0,0 +1,137 @@
from hisscl import interp
import unittest
import io
class TestBasic(unittest.TestCase):
def test_assignment(self):
cfg = interp.Interp(io.StringIO("x = 26.04"), "TestBasic.test_assignment").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], 26.04)
def test_block(self):
cfg = interp.Interp(io.StringIO("x { y = 26.04 }"), "TestBasic.test_block").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], [{'y': 26.04}])
def test_block_labels(self):
cfg = interp.Interp(io.StringIO("x y { z = 26.04 }"), "TestBasic.test_block_labels").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], [{'z': 26.04}])
self.assertEqual(cfg['x'][0].labels, ['y'])
class TestExpressions(unittest.TestCase):
def test_add(self):
cfg = interp.Interp(io.StringIO("x = 123 + 333"), "TestExpressions.test_add").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], 456)
def test_sub(self):
cfg = interp.Interp(io.StringIO("x = 456 - 333"), "TestExpressions.test_sub").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], 123)
def test_mul(self):
cfg = interp.Interp(io.StringIO("x = 128 * 2"), "TestExpressions.test_mul").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], 256)
def test_div(self):
cfg = interp.Interp(io.StringIO("x = 256 / 2"), "TestExpressions.test_div").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], 128)
def test_mod(self):
cfg = interp.Interp(io.StringIO("x = 256 % 3"), "TestExpressions.test_mod").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], 1)
def test_eq(self):
cfg = interp.Interp(io.StringIO("x = 123 == 456"), "TestExpressions.test_eq").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], False)
def test_ne(self):
cfg = interp.Interp(io.StringIO("x = 123 != 456"), "TestExpressions.test_ne").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], True)
def test_lt(self):
cfg = interp.Interp(io.StringIO("x = 123 < 456"), "TestExpressions.test_lt").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], True)
def test_gt(self):
cfg = interp.Interp(io.StringIO("x = 123 > 456"), "TestExpressions.test_gt").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], False)
def test_le(self):
cfg = interp.Interp(io.StringIO("x = 123 <= 123"), "TestExpressions.test_le").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], True)
def test_ge(self):
cfg = interp.Interp(io.StringIO("x = 1234 >= 123"), "TestExpressions.test_ge").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], True)
def test_or(self):
cfg = interp.Interp(io.StringIO("x = true || false"), "TestExpressions.test_or").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], True)
def test_and(self):
cfg = interp.Interp(io.StringIO("x = true && false"), "TestExpressions.test_and").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], False)
def test_not(self):
cfg = interp.Interp(io.StringIO("x = !true"), "TestExpressions.test_not").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], False)
def test_neg(self):
cfg = interp.Interp(io.StringIO("x = -1"), "TestExpressions.test_neg").run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], -1)
class TestRefs(unittest.TestCase):
def test_var(self):
i = interp.Interp(io.StringIO("x = 123 + y"), "TestRefs.test_var")
i['y'] = 333
cfg = i.run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], 456)
def test_index(self):
i = interp.Interp(io.StringIO("x = y[1]"), "TestRefs.test_index")
i['y'] = [123, 456, 789]
cfg = i.run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], 456)
def test_func(self):
def y(a, b):
return a + b
i = interp.Interp(io.StringIO("x = y(123, 333)"), "TestRefs.test_func")
i['y'] = y
cfg = i.run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], 456)
def test_func_expansion(self):
def y(a, b):
return a + b
i = interp.Interp(io.StringIO("x = y(z...)"), "TestRefs.test_func_expansion")
i['y'] = y
i['z'] = (123, 333)
cfg = i.run()
self.assertIn('x', cfg)
self.assertEqual(cfg['x'], 456)
def test_call_uncallable(self):
i = interp.Interp(io.StringIO("x = y(123, 333)"), "TestRefs.test_call_uncallable")
i['y'] = 0
with self.assertRaises(ValueError) as ctx:
cfg = i.run()
self.assertIn('cannot call non-callable object', str(ctx.exception))

86
test/test_lexer.py Normal file
View File

@ -0,0 +1,86 @@
from hisscl import lexer
import unittest
import io
class TestComments(unittest.TestCase):
def test_double_slash(self):
tok, pos, lit = lexer.Lexer(io.StringIO('// this should be ignored'), 'TestComments.test_double_slash').scan()
self.assertEqual(tok, lexer.Token.EOF)
self.assertEqual(lit, '')
def test_pound(self):
tok, pos, lit = lexer.Lexer(io.StringIO('# this should be ignored'), 'TestComments.test_pound').scan()
self.assertEqual(tok, lexer.Token.EOF)
self.assertEqual(lit, '')
def test_multiline(self):
tok, pos, lit = lexer.Lexer(io.StringIO('/* this\nshould\nbe\nignored */'), 'TestComments.test_multiline').scan()
self.assertEqual(tok, lexer.Token.EOF)
self.assertEqual(lit, '')
class TestOperators(unittest.TestCase):
def test_lt(self):
tok, pos, lit = lexer.Lexer(io.StringIO('<'), 'TestOperators.test_lt').scan()
self.assertEqual(tok, lexer.Token.OPERATOR)
self.assertEqual(lit, '<')
def test_div(self):
tok, pos, lit = lexer.Lexer(io.StringIO('/'), 'TestOperators.test_div').scan()
self.assertEqual(tok, lexer.Token.OPERATOR)
self.assertEqual(lit, '/')
def test_multichar(self):
tok, pos, lit = lexer.Lexer(io.StringIO('<='), 'TestOperators.test_multichar').scan()
self.assertEqual(tok, lexer.Token.OPERATOR)
self.assertEqual(lit, '<=')
def test_ellipsis(self):
tok, pos, lit = lexer.Lexer(io.StringIO('...'), 'TestOperators.test_ellipsis').scan()
self.assertEqual(tok, lexer.Token.ELLIPSIS)
self.assertEqual(lit, '...')
def test_invalid_ellipsis(self):
with self.assertRaises(lexer.ExpectedError) as ctx:
lexer.Lexer(io.StringIO('..'), 'TestOperators.test_invalid_ellipsis').scan()
self.assertIn("expected .", str(ctx.exception))
self.assertIn("got EOF", str(ctx.exception))
class TestHeredoc(unittest.TestCase):
def test_heredoc(self):
tok, pos, lit = lexer.Lexer(io.StringIO('<<EOT\nthis\nis\na\nmultiline\nstring\nEOT'), 'TestHeredoc.test_heredoc').scan()
self.assertEqual(tok, lexer.Token.HEREDOC)
self.assertEqual(lit, '\nthis\nis\na\nmultiline\nstring\n')
class TestNumber(unittest.TestCase):
def test_integer(self):
tok, pos, lit = lexer.Lexer(io.StringIO('1234'), 'TestNumber.test_integer').scan()
self.assertEqual(tok, lexer.Token.INTEGER)
self.assertEqual(lit, "1234")
def test_float(self):
tok, pos, lit = lexer.Lexer(io.StringIO('1234.5678'), 'TestNumber.test_float').scan()
self.assertEqual(tok, lexer.Token.FLOAT)
self.assertEqual(lit, "1234.5678")
def test_invalid_float(self):
with self.assertRaises(lexer.ExpectedError) as ctx:
lexer.Lexer(io.StringIO('1.0.0'), 'TestNumber.test_invalid_float').scan()
self.assertIn("expected number", str(ctx.exception))
self.assertIn("got '.'", str(ctx.exception))
class TestString(unittest.TestCase):
def test_basic(self):
tok, pos, lit = lexer.Lexer(io.StringIO('""'), 'TestString.test_basic').scan()
self.assertEqual(tok, lexer.Token.STRING)
self.assertEqual(lit, '""')
def test_escape(self):
tok, pos, lit = lexer.Lexer(io.StringIO('"\\""'), 'TestString.test_escape').scan()
self.assertEqual(tok, lexer.Token.STRING)
self.assertEqual(lit, '"\\""')
def test_newline(self):
with self.assertRaises(lexer.ExpectedError) as ctx:
lexer.Lexer(io.StringIO('"\n"'), 'TestString.test_newline').scan()
self.assertIn("expected '\"'", str(ctx.exception))
self.assertIn("got '\\n'", str(ctx.exception))

217
test/test_parser.py Normal file
View File

@ -0,0 +1,217 @@
from hisscl import parser, ast
import unittest
import io
class TestLiterals(unittest.TestCase):
def test_integer(self):
val = parser.Parser(io.StringIO('1234'), 'TestLiterals.test_integer')._parse_value()
self.assertIsInstance(val, ast.Integer)
assert type(val) is ast.Integer
self.assertEqual(val.value, 1234)
def test_float(self):
val = parser.Parser(io.StringIO('1234.5678'), 'TestLiterals.test_float')._parse_value()
self.assertIsInstance(val, ast.Float)
assert type(val) is ast.Float
self.assertEqual(val.value, 1234.5678)
def test_string(self):
val = parser.Parser(io.StringIO('"test \\" \\u26a7"'), 'TestLiterals.test_string')._parse_value()
self.assertIsInstance(val, ast.String)
assert type(val) is ast.String
self.assertEqual(val.value, 'test " \u26a7')
def test_bool(self):
val = parser.Parser(io.StringIO('true'), 'TestLiterals.test_bool')._parse_value()
self.assertIsInstance(val, ast.Bool)
assert type(val) is ast.Bool
self.assertEqual(val.value, True)
def test_heredoc(self):
val = parser.Parser(io.StringIO('<<EOT\nthis\nis\na\nmultiline\nstring\nEOT'), 'TestLiterals.test_heredoc')._parse_value()
self.assertIsInstance(val, ast.String)
assert type(val) is ast.String
self.assertEqual(val.value, '\nthis\nis\na\nmultiline\nstring\n')
class TestCollections(unittest.TestCase):
def test_tuple(self):
val = parser.Parser(io.StringIO('[1, 2.0, "3", true]'), 'TestCollections.test_tuple')._parse_value()
self.assertIsInstance(val, ast.Tuple)
assert type(val) is ast.Tuple
self.assertEqual(val.items, [
ast.Integer(
pos = ast.Position(name="TestCollections.test_tuple", line=1, col=2),
value = 1,
),
ast.Float(
pos = ast.Position(name="TestCollections.test_tuple", line=1, col=5),
value = 2.0,
),
ast.String(
pos = ast.Position(name="TestCollections.test_tuple", line=1, col=10),
value = "3",
),
ast.Bool(
pos = ast.Position(name="TestCollections.test_tuple", line=1, col=15),
value = True,
),
])
def test_object(self):
val = parser.Parser(io.StringIO('{true: 2.0, "3": 4, 5.0: "6"}'), 'TestCollections.test_object')._parse_value()
self.assertIsInstance(val, ast.Object)
assert type(val) is ast.Object
self.assertEqual(val.items, [
(
ast.Bool(
pos = ast.Position(name="TestCollections.test_object", line=1, col=2),
value = True,
),
ast.Float(
pos = ast.Position(name="TestCollections.test_object", line=1, col=8),
value = 2.0,
),
),
(
ast.String(
pos = ast.Position(name="TestCollections.test_object", line=1, col=13),
value = "3",
),
ast.Integer(
pos = ast.Position(name="TestCollections.test_object", line=1, col=18),
value = 4,
),
),
(
ast.Float(
pos = ast.Position(name="TestCollections.test_object", line=1, col=21),
value = 5.0,
),
ast.String(
pos = ast.Position(name="TestCollections.test_object", line=1, col=26),
value = "6",
),
)
])
class TestExpressions(unittest.TestCase):
def test_bare_value(self):
val = parser.Parser(io.StringIO('1234'), 'TestExpressions.test_bare_value')._parse_expr()
self.assertIsInstance(val, ast.Integer)
assert type(val) is ast.Integer
self.assertEqual(val.value, 1234)
def test_binary(self):
val = parser.Parser(io.StringIO('1234 == 5678'), 'TestExpressions.test_binary')._parse_expr()
self.assertEqual(val, ast.BinaryExpression(
pos = ast.Position(name='TestExpressions.test_binary', line=1, col=1),
left = ast.Integer(
pos = ast.Position(name='TestExpressions.test_binary', line=1, col=1),
value = 1234,
),
op = ast.Operator(
pos = ast.Position(name='TestExpressions.test_binary', line=1, col=6),
value = '==',
),
right = ast.Integer(
pos = ast.Position(name='TestExpressions.test_binary', line=1, col=9),
value = 5678
),
))
def test_binary_nested(self):
val = parser.Parser(io.StringIO('(1234 - 5) == 5678'), 'TestExpressions.test_binary_nested')._parse_expr()
self.assertEqual(val, ast.BinaryExpression(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=2),
left = ast.BinaryExpression(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=2),
left = ast.Integer(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=2),
value = 1234,
),
op = ast.Operator(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=7),
value = '-',
),
right = ast.Integer(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=9),
value = 5,
),
),
op = ast.Operator(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=12),
value = '==',
),
right = ast.Integer(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=15),
value = 5678
),
))
def test_binary_multi(self):
val = parser.Parser(io.StringIO('1234 == 5678 - 4444'), 'TestExpressions.test_binary_nested')._parse_expr()
self.assertEqual(val, ast.BinaryExpression(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=1),
left = ast.Integer(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=1),
value = 1234,
),
op = ast.Operator(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=6),
value = '==',
),
right = ast.BinaryExpression(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=9),
left = ast.Integer(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=9),
value = 5678
),
op = ast.Operator(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=14),
value = '-',
),
right = ast.Integer(
pos = ast.Position(name='TestExpressions.test_binary_nested', line=1, col=16),
value = 4444,
),
),
))
def test_expansion(self):
val = parser.Parser(io.StringIO('x(y...)'), 'TestExpressions.test_expansion')._parse_expr()
self.assertEqual(val, ast.FunctionCall(
pos = ast.Position(name='TestExpressions.test_expansion', line=1, col=1),
name = 'x',
args = [
ast.Expansion(
pos = ast.Position(name='TestExpressions.test_expansion', line=1, col=3),
value = ast.VariableRef(
pos = ast.Position(name='TestExpressions.test_expansion', line=1, col=3),
name = 'y',
),
),
],
))
def test_index(self):
val = parser.Parser(io.StringIO('x[0]'), 'TestExpressions.test_index')._parse_expr()
self.assertIsInstance(val, ast.Index)
assert type(val) is ast.Index
self.assertEqual(val.value, ast.VariableRef(
pos = ast.Position(name='TestExpressions.test_index', line=1, col=1),
name = 'x',
))
self.assertEqual(val.index, ast.Integer(
pos = ast.Position(name='TestExpressions.test_index', line=1, col=3),
value = 0,
))
def test_unary(self):
val = parser.Parser(io.StringIO('!true'), 'TestExpressions.test_unary')._parse_value()
self.assertIsInstance(val, ast.UnaryExpression)
assert type(val) is ast.UnaryExpression
self.assertEqual(val.op.value, '!')
self.assertEqual(val.value, ast.Bool(
pos = ast.Position(name="TestExpressions.test_unary", line=1, col=2),
value = True,
))