added some entity tests

This commit is contained in:
Eric Froemling 2019-12-14 10:52:44 -08:00
parent fbd7ea71d3
commit 1420c54947
5 changed files with 121 additions and 31 deletions

View File

@ -187,6 +187,7 @@
<w>bumpmap</w> <w>bumpmap</w>
<w>buttondown</w> <w>buttondown</w>
<w>buttonwidget</w> <w>buttonwidget</w>
<w>bval</w>
<w>byteswap</w> <w>byteswap</w>
<w>cachable</w> <w>cachable</w>
<w>cachebasename</w> <w>cachebasename</w>
@ -281,6 +282,7 @@
<w>compat</w> <w>compat</w>
<w>compileall</w> <w>compileall</w>
<w>compilelocations</w> <w>compilelocations</w>
<w>compoundlist</w>
<w>configerror</w> <w>configerror</w>
<w>confighash</w> <w>confighash</w>
<w>configkey</w> <w>configkey</w>
@ -470,6 +472,7 @@
<w>entrytype</w> <w>entrytype</w>
<w>entrytypeselect</w> <w>entrytypeselect</w>
<w>enumtype</w> <w>enumtype</w>
<w>enumval</w>
<w>envhash</w> <w>envhash</w>
<w>epath</w> <w>epath</w>
<w>epicfail</w> <w>epicfail</w>
@ -613,6 +616,7 @@
<w>functxt</w> <w>functxt</w>
<w>funtionality</w> <w>funtionality</w>
<w>futimens</w> <w>futimens</w>
<w>fval</w>
<w>gameactivity</w> <w>gameactivity</w>
<w>gamebutton</w> <w>gamebutton</w>
<w>gameclass</w> <w>gameclass</w>
@ -794,10 +798,12 @@
<w>iserverget</w> <w>iserverget</w>
<w>iserverput</w> <w>iserverput</w>
<w>ispunch</w> <w>ispunch</w>
<w>isubval</w>
<w>isysroot</w> <w>isysroot</w>
<w>itms</w> <w>itms</w>
<w>itmsp</w> <w>itmsp</w>
<w>itunes</w> <w>itunes</w>
<w>ival</w>
<w>jackmorgan</w> <w>jackmorgan</w>
<w>janktastic</w> <w>janktastic</w>
<w>janky</w> <w>janky</w>
@ -1442,6 +1448,7 @@
<w>sitebuiltins</w> <w>sitebuiltins</w>
<w>skey</w> <w>skey</w>
<w>sline</w> <w>sline</w>
<w>slval</w>
<w>smlh</w> <w>smlh</w>
<w>smtpd</w> <w>smtpd</w>
<w>smtplib</w> <w>smtplib</w>
@ -1546,6 +1553,7 @@
<w>svalin</w> <w>svalin</w>
<w>sver</w> <w>sver</w>
<w>svne</w> <w>svne</w>
<w>svvv</w>
<w>swip</w> <w>swip</w>
<w>swipsound</w> <w>swipsound</w>
<w>symlinked</w> <w>symlinked</w>
@ -1576,6 +1584,7 @@
<w>tchar</w> <w>tchar</w>
<w>tcombine</w> <w>tcombine</w>
<w>tdelay</w> <w>tdelay</w>
<w>tdval</w>
<w>teambasesession</w> <w>teambasesession</w>
<w>teamgame</w> <w>teamgame</w>
<w>teamnamescolors</w> <w>teamnamescolors</w>

View File

@ -18,6 +18,7 @@
"astroid", "astroid",
"bs_mapdefs_bridgit", "bs_mapdefs_bridgit",
"pylint.lint", "pylint.lint",
"pytest",
"bs_mapdefs_zig_zag", "bs_mapdefs_zig_zag",
"bs_mapdefs_the_pad", "bs_mapdefs_the_pad",
"bs_mapdefs_step_right_up", "bs_mapdefs_step_right_up",

View File

@ -18,6 +18,9 @@ ignore_errors = True
[mypy-astroid.*] [mypy-astroid.*]
ignore_missing_imports = True ignore_missing_imports = True
[mypy-pytest.*]
ignore_missing_imports = True
[mypy-efrotools.pylintplugins] [mypy-efrotools.pylintplugins]
disallow_any_unimported = False disallow_any_unimported = False

View File

@ -22,30 +22,94 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, List, Sequence from typing import TYPE_CHECKING
from enum import Enum, unique
import pytest
from bafoundation import entity
from efrotools.statictest import static_type_equals from efrotools.statictest import static_type_equals
if TYPE_CHECKING: if TYPE_CHECKING:
pass pass
def inc(x: int) -> int: # A smattering of enum value types...
"""Testing inc.""" @unique
return x + 1 class EnumTest(Enum):
"""Testing..."""
FIRST = 0
SECOND = 1
def test_answer() -> None: class CompoundTest(entity.CompoundValue):
"""Testing answer.""" """Testing..."""
fooval: List[int] = [3, 4] isubval = entity.Field('i', entity.IntValue(default=34532))
assert static_type_equals(fooval[0], int)
assert static_type_equals(fooval, List[int])
somevar: Sequence[int] = []
assert static_type_equals(somevar, Sequence[int])
assert isinstance(fooval, list)
assert inc(3) == 4
def test_answer2() -> None: class CompoundTest2(CompoundTest):
"""Testing answer.""" """Testing..."""
assert inc(3) == 4 isubval2 = entity.Field('i2', entity.IntValue(default=3453))
class EntityTest(entity.Entity):
"""Testing..."""
ival = entity.Field('i', entity.IntValue(default=345))
sval = entity.Field('s', entity.StringValue(default='svvv'))
bval = entity.Field('b', entity.BoolValue(default=True))
fval = entity.Field('f', entity.FloatValue(default=1.0))
grp = entity.CompoundField('g', CompoundTest2())
grp2 = entity.CompoundField('g2', CompoundTest())
enumval = entity.Field('e', entity.EnumValue(EnumTest, default=None))
enumval2 = entity.Field(
'e2', entity.OptionalEnumValue(EnumTest, default=EnumTest.SECOND))
compoundlist = entity.CompoundListField('l', CompoundTest())
slval = entity.ListField('sl', entity.StringValue())
tval2 = entity.Field('t2', entity.DateTimeValue())
str_int_dict = entity.DictField('sd', str, entity.IntValue())
tdval = entity.CompoundDictField('td', str, CompoundTest())
fval2 = entity.Field('f2', entity.Float3Value())
class EntityTest2(entity.EntityMixin, CompoundTest2):
"""test."""
def test_entity_values() -> None:
"""Test various entity assigns for value and type correctness."""
ent = EntityTest()
# Simple int field.
with pytest.raises(TypeError):
# noinspection PyTypeHints
ent.ival = 'strval' # type: ignore
assert static_type_equals(ent.ival, int)
assert isinstance(ent.ival, int)
assert ent.ival == 345
ent.ival = 346
assert ent.ival == 346
# Simple str/int dict field.
assert 'foo' not in ent.str_int_dict
with pytest.raises(TypeError):
# noinspection PyTypeHints
ent.str_int_dict[0] = 123 # type: ignore
with pytest.raises(TypeError):
# noinspection PyTypeHints
ent.str_int_dict['foo'] = 'bar' # type: ignore
ent.str_int_dict['foo'] = 123
assert static_type_equals(ent.str_int_dict['foo'], int)
assert ent.str_int_dict['foo'] == 123
# Compound list field.
with pytest.raises(IndexError):
print(ent.compoundlist[0])
with pytest.raises(TypeError):
# noinspection PyTypeHints
ent.compoundlist[0] = 123 # type: ignore
assert len(ent.compoundlist) == 0
assert not ent.compoundlist
ent.compoundlist.append()
assert ent.compoundlist
assert len(ent.compoundlist) == 1
assert static_type_equals(ent.compoundlist[0], CompoundTest)

View File

@ -52,6 +52,8 @@ class StaticTestFile:
from efrotools import PYTHON_BIN from efrotools import PYTHON_BIN
self._filename = filename self._filename = filename
self.modulename = f'temp{_nextfilenum}'
_nextfilenum += 1
# Types we *want* for lines # Types we *want* for lines
self.linetypes_wanted: Dict[int, str] = {} self.linetypes_wanted: Dict[int, str] = {}
@ -71,26 +73,32 @@ class StaticTestFile:
# (so that we can recycle our mypy cache). # (so that we can recycle our mypy cache).
if _tempdir is None: if _tempdir is None:
_tempdir = tempfile.TemporaryDirectory() _tempdir = tempfile.TemporaryDirectory()
# print(f"Created temp dir at {_tempdir.name}")
# Copy our file into the temp dir with a unique name, find all # Copy our file into the temp dir with a unique name, find all
# instances of static_type_equals(), and run mypy type checks # instances of static_type_equals(), and run mypy type checks
# in those places to get static types. # in those places to get static types.
tempfilepath = os.path.join(_tempdir.name, f'temp{_nextfilenum}.py') tempfilepath = os.path.join(_tempdir.name, self.modulename + '.py')
_nextfilenum += 1
with open(tempfilepath, 'w') as outfile: with open(tempfilepath, 'w') as outfile:
outfile.write(self.filter_file_contents(fdata)) outfile.write(self.filter_file_contents(fdata))
results = subprocess.run([ results = subprocess.run(
PYTHON_BIN, '-m', 'mypy', '--no-error-summary', '--config-file', [
'.mypy.ini', '--cache-dir', _tempdir.name, tempfilepath PYTHON_BIN, '-m', 'mypy', '--no-error-summary',
], '--config-file', '.mypy.ini', '--cache-dir',
capture_output=True, os.path.join(_tempdir.name, '.mypy_cache'), tempfilepath
check=False) ],
# HMM; it seems we get an errored return code due to reveal_type()s. capture_output=True,
# So I guess we just have to ignore other errors, which is unforunate. check=False,
# (though if the run fails, we'll probably error when attempting to )
# look up a revealed type that we don't have anyway)
# HMM; it seems we always get an errored return code due to
# our use of reveal_type() so we can't look at that.
# However we can look for error notices in the output and fail there.
lines = results.stdout.decode().splitlines() lines = results.stdout.decode().splitlines()
for line in lines: for line in lines:
if ': error: ' in line:
print("Full mypy output:\n", results.stdout.decode())
raise RuntimeError('Errors detected in mypy output.')
if 'Revealed type is ' in line: if 'Revealed type is ' in line:
finfo = line.split(' ')[0] finfo = line.split(' ')[0]
fparts = finfo.split(':') fparts = finfo.split(':')
@ -172,17 +180,22 @@ def static_type_equals(value: Any, statictype: Type) -> bool:
if filename not in _statictestfiles: if filename not in _statictestfiles:
_statictestfiles[filename] = StaticTestFile(filename) _statictestfiles[filename] = StaticTestFile(filename)
testfile = _statictestfiles[filename]
wanttype = _statictestfiles[filename].linetypes_wanted[linenumber] wanttype = testfile.linetypes_wanted[linenumber]
mypytype = _statictestfiles[filename].linetypes_mypy[linenumber] mypytype = testfile.linetypes_mypy[linenumber]
# Do some filtering of Mypy types to simple python ones. # Do some filtering of Mypy types to simple python ones.
# (ie: 'builtins.list[builtins.int*]' -> int) # (ie: 'builtins.list[builtins.int*]' -> int)
mypytype = mypytype.replace('builtins.int*', 'int') mypytype = mypytype.replace('*', '')
mypytype = mypytype.replace('?', '')
mypytype = mypytype.replace('builtins.int', 'int') mypytype = mypytype.replace('builtins.int', 'int')
mypytype = mypytype.replace('builtins.list', 'List') mypytype = mypytype.replace('builtins.list', 'List')
mypytype = mypytype.replace('typing.Sequence', 'Sequence') mypytype = mypytype.replace('typing.Sequence', 'Sequence')
# temp3.FooClass -> FooClass
mypytype = mypytype.replace(testfile.modulename + '.', '')
if wanttype != mypytype: if wanttype != mypytype:
print(f'Mypy type "{mypytype}" does not match ' print(f'Mypy type "{mypytype}" does not match '
f'the desired type "{wanttype}" on line {linenumber}.') f'the desired type "{wanttype}" on line {linenumber}.')