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

View File

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

View File

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

View File

@ -22,30 +22,94 @@
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
if TYPE_CHECKING:
pass
def inc(x: int) -> int:
"""Testing inc."""
return x + 1
# A smattering of enum value types...
@unique
class EnumTest(Enum):
"""Testing..."""
FIRST = 0
SECOND = 1
def test_answer() -> None:
"""Testing answer."""
fooval: List[int] = [3, 4]
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
class CompoundTest(entity.CompoundValue):
"""Testing..."""
isubval = entity.Field('i', entity.IntValue(default=34532))
def test_answer2() -> None:
"""Testing answer."""
assert inc(3) == 4
class CompoundTest2(CompoundTest):
"""Testing..."""
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
self._filename = filename
self.modulename = f'temp{_nextfilenum}'
_nextfilenum += 1
# Types we *want* for lines
self.linetypes_wanted: Dict[int, str] = {}
@ -71,26 +73,32 @@ class StaticTestFile:
# (so that we can recycle our mypy cache).
if _tempdir is None:
_tempdir = tempfile.TemporaryDirectory()
# print(f"Created temp dir at {_tempdir.name}")
# Copy our file into the temp dir with a unique name, find all
# instances of static_type_equals(), and run mypy type checks
# in those places to get static types.
tempfilepath = os.path.join(_tempdir.name, f'temp{_nextfilenum}.py')
_nextfilenum += 1
tempfilepath = os.path.join(_tempdir.name, self.modulename + '.py')
with open(tempfilepath, 'w') as outfile:
outfile.write(self.filter_file_contents(fdata))
results = subprocess.run([
PYTHON_BIN, '-m', 'mypy', '--no-error-summary', '--config-file',
'.mypy.ini', '--cache-dir', _tempdir.name, tempfilepath
],
capture_output=True,
check=False)
# HMM; it seems we get an errored return code due to reveal_type()s.
# So I guess we just have to ignore other errors, which is unforunate.
# (though if the run fails, we'll probably error when attempting to
# look up a revealed type that we don't have anyway)
results = subprocess.run(
[
PYTHON_BIN, '-m', 'mypy', '--no-error-summary',
'--config-file', '.mypy.ini', '--cache-dir',
os.path.join(_tempdir.name, '.mypy_cache'), tempfilepath
],
capture_output=True,
check=False,
)
# 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()
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:
finfo = line.split(' ')[0]
fparts = finfo.split(':')
@ -172,17 +180,22 @@ def static_type_equals(value: Any, statictype: Type) -> bool:
if filename not in _statictestfiles:
_statictestfiles[filename] = StaticTestFile(filename)
testfile = _statictestfiles[filename]
wanttype = _statictestfiles[filename].linetypes_wanted[linenumber]
mypytype = _statictestfiles[filename].linetypes_mypy[linenumber]
wanttype = testfile.linetypes_wanted[linenumber]
mypytype = testfile.linetypes_mypy[linenumber]
# Do some filtering of Mypy types to simple python ones.
# (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.list', 'List')
mypytype = mypytype.replace('typing.Sequence', 'Sequence')
# temp3.FooClass -> FooClass
mypytype = mypytype.replace(testfile.modulename + '.', '')
if wanttype != mypytype:
print(f'Mypy type "{mypytype}" does not match '
f'the desired type "{wanttype}" on line {linenumber}.')