[jstests] Doc, dynamic import
Refactors: template check and logging logic
This commit is contained in:
parent
a5e70225d0
commit
bddf48281c
@ -1,47 +1,63 @@
|
|||||||
from . import (
|
"""
|
||||||
array_access,
|
This package contains templates for `test_jsinterp` and `test_interp_parse` to create test methods.
|
||||||
assignments,
|
These modules will create a test method for each module in this package. A test method consist of one or more subtest.
|
||||||
basic,
|
Each subtest initializes an instance of the tested class and runs one or more assertion.
|
||||||
branch,
|
|
||||||
calc,
|
|
||||||
call,
|
|
||||||
comments,
|
|
||||||
debug,
|
|
||||||
do_loop,
|
|
||||||
empty_return,
|
|
||||||
for_empty,
|
|
||||||
for_in,
|
|
||||||
for_loop,
|
|
||||||
func_expr,
|
|
||||||
getfield,
|
|
||||||
label,
|
|
||||||
morespace,
|
|
||||||
object_literal,
|
|
||||||
operators,
|
|
||||||
parens,
|
|
||||||
precedence,
|
|
||||||
strange_chars,
|
|
||||||
switch,
|
|
||||||
try_statement,
|
|
||||||
unary,
|
|
||||||
unshift,
|
|
||||||
while_loop,
|
|
||||||
with_statement
|
|
||||||
)
|
|
||||||
|
|
||||||
|
Any module should have a `list` of `dict` named ``tests`` and optionally a `dict` named ``skip``.
|
||||||
|
|
||||||
modules = [array_access, assignments, basic, branch, calc, call, comments, debug, do_loop, empty_return, for_empty,
|
Each `dict` in ``tests`` may have the following keys:
|
||||||
for_in, for_loop, func_expr, getfield, label, morespace, object_literal, operators, parens, precedence,
|
|
||||||
strange_chars, switch, try_statement, unary, unshift, while_loop, with_statement]
|
code: If missing subtest is skipped, Otherwise it's value is used as code to initialize the tested class.
|
||||||
|
globals: Optional. Used only by `test_jsinterp`. If set used as argument `variables` initializing `JSInterperter`.
|
||||||
|
asserts: Used only by `test_jsinterp`. If this is missing subtest is skipped, Should be a list of `dict`, each used
|
||||||
|
as an assertion for the initialized `JSInterpreter`. Each `dict` may have the following keys:
|
||||||
|
value: If missing assertion is skipped. Otherwise it's value is used as expected value in
|
||||||
|
an `assertEqual` call.
|
||||||
|
call: Optional. If set used as arguments of a `call_function` call of the initialized `JSInterpreter`
|
||||||
|
and the actual value of the created `assertEqual` call will be the return value of it.
|
||||||
|
Otherwise the actual value will be the return value of the `run` call.
|
||||||
|
ast: Used only by `test_interp_parse`. If missing subtest is skipped, Otherwise it's value is used as
|
||||||
|
expected value in an `assertEqual` call. The actual value will be the return value of the `parse` call
|
||||||
|
converted to `list`. Both on expected anc actual value `traverse` is called first to flatten and handle `zip`
|
||||||
|
objects.
|
||||||
|
|
||||||
|
In the `dict` named ``skip`` is optional and may have the following keys:
|
||||||
|
interpret
|
||||||
|
parse
|
||||||
|
Both used as the argument of `skipTest` decorator of the created test method in `test_jsinterp`
|
||||||
|
and `test_jsinterp_parse` respectably. Unless they're value is `True`, that case the test method is skipped entirely,
|
||||||
|
or `False`, which is the default value.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
This is not a functional template, rather a skeleton:
|
||||||
|
|
||||||
|
skip = {'interpret': 'Test not yet implemented',
|
||||||
|
'parse': 'Test not yet implemented'}
|
||||||
|
|
||||||
|
tests = [
|
||||||
|
{
|
||||||
|
'code': '',
|
||||||
|
'globals': {},
|
||||||
|
'asserts': [{'value': 0, 'call': ('f',)}],
|
||||||
|
'ast': []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def gettestcases():
|
def gettestcases():
|
||||||
for module in modules:
|
import os
|
||||||
|
|
||||||
|
modules = [module[:-3] for module in os.listdir(os.path.dirname(__file__))
|
||||||
|
if module != '__init__.py' and module[-3:] == '.py']
|
||||||
|
me = __import__(__name__, globals(), locals(), modules)
|
||||||
|
|
||||||
|
for module_name in modules:
|
||||||
|
module = getattr(me, module_name)
|
||||||
if hasattr(module, 'tests'):
|
if hasattr(module, 'tests'):
|
||||||
case = {'name': module.__name__[len(__name__) + 1:], 'subtests': [], 'skip': {}}
|
case = {
|
||||||
for test in getattr(module, 'tests'):
|
'name': module.__name__[len(__name__) + 1:],
|
||||||
if 'code' in test:
|
'subtests': module.tests,
|
||||||
case['subtests'].append(test)
|
'skip': getattr(module, 'skip', {})
|
||||||
if hasattr(module, 'skip'):
|
}
|
||||||
case['skip'] = getattr(module, 'skip')
|
|
||||||
yield case
|
yield case
|
||||||
|
@ -44,7 +44,7 @@ tests = [
|
|||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
'code': 'function x(a) { return a.split(""); }',
|
'code': 'function x(a) { return a.split(""); }',
|
||||||
# built-in functions not yet implemented
|
# FIXME built-in functions not yet implemented
|
||||||
# 'asserts': [{'value': ["a", "b", "c"], 'call': ('x',"abc")}],
|
# 'asserts': [{'value': ["a", "b", "c"], 'call': ('x',"abc")}],
|
||||||
'ast': [
|
'ast': [
|
||||||
(Token.FUNC, 'x', ['a'], [
|
(Token.FUNC, 'x', ['a'], [
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
see: `jstests`
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# Allow direct execution
|
# Allow direct execution
|
||||||
@ -14,7 +18,7 @@ else:
|
|||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
from youtube_dl.jsinterp import JSInterpreter
|
from youtube_dl.jsinterp import JSInterpreter
|
||||||
from test.jstests import gettestcases
|
from .jstests import gettestcases
|
||||||
|
|
||||||
defs = gettestcases()
|
defs = gettestcases()
|
||||||
# set level to logging.DEBUG to see messages about missing assertions
|
# set level to logging.DEBUG to see messages about missing assertions
|
||||||
@ -29,15 +33,25 @@ class TestJSInterpreter(unittest.TestCase):
|
|||||||
def generator(test_case, name):
|
def generator(test_case, name):
|
||||||
def test_template(self):
|
def test_template(self):
|
||||||
for test in test_case['subtests']:
|
for test in test_case['subtests']:
|
||||||
jsi = JSInterpreter(test['code'], variables=test.get('globals'))
|
if 'code' not in test:
|
||||||
if 'asserts' in test:
|
log_reason = 'No code in subtest, skipping'
|
||||||
for a in test['asserts']:
|
elif 'asserts' not in test:
|
||||||
if 'call' in a:
|
log_reason = 'No assertion in subtest, skipping'
|
||||||
self.assertEqual(jsi.call_function(*a['call']), a['value'])
|
|
||||||
else:
|
|
||||||
self.assertEqual(jsi.run(), a['value'])
|
|
||||||
else:
|
else:
|
||||||
log.debug('No assertion for subtest, skipping')
|
log_reason = None
|
||||||
|
|
||||||
|
if log_reason is None:
|
||||||
|
jsi = JSInterpreter(test['code'], variables=test.get('globals'))
|
||||||
|
for a in test['asserts']:
|
||||||
|
if 'value' in a:
|
||||||
|
if 'call' in a:
|
||||||
|
self.assertEqual(jsi.call_function(*a['call']), a['value'])
|
||||||
|
else:
|
||||||
|
self.assertEqual(jsi.run(), a['value'])
|
||||||
|
else:
|
||||||
|
log.debug('No value in assertion, skipping')
|
||||||
|
else:
|
||||||
|
log.debug(log_reason)
|
||||||
|
|
||||||
log = logging.getLogger('TestJSInterpreter.%s' % name)
|
log = logging.getLogger('TestJSInterpreter.%s' % name)
|
||||||
return test_template
|
return test_template
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
see: `jstests`
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# Allow direct execution
|
# Allow direct execution
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import logging
|
|
||||||
import copy
|
import copy
|
||||||
|
import logging
|
||||||
|
|
||||||
if sys.version_info < (2, 7):
|
if sys.version_info < (2, 7):
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
@ -20,7 +24,7 @@ from .jstests import gettestcases
|
|||||||
|
|
||||||
def traverse(node, tree_types=(list, tuple)):
|
def traverse(node, tree_types=(list, tuple)):
|
||||||
if sys.version_info > (3,) and isinstance(node, zip):
|
if sys.version_info > (3,) and isinstance(node, zip):
|
||||||
node = list(copy.deepcopy(node))
|
node = list(copy.copy(node))
|
||||||
if isinstance(node, tree_types):
|
if isinstance(node, tree_types):
|
||||||
tree = []
|
tree = []
|
||||||
for value in node:
|
for value in node:
|
||||||
@ -42,13 +46,16 @@ class TestJSInterpreterParse(unittest.TestCase):
|
|||||||
|
|
||||||
def generator(test_case, name):
|
def generator(test_case, name):
|
||||||
def test_template(self):
|
def test_template(self):
|
||||||
for a in test_case['subtests']:
|
for test in test_case['subtests']:
|
||||||
jsp = Parser(a['code'])
|
if 'code' in test:
|
||||||
parsed = list(jsp.parse())
|
jsp = Parser(test['code'])
|
||||||
if 'ast' in a:
|
parsed = list(jsp.parse())
|
||||||
self.assertEqual(traverse(parsed), traverse(a['ast']))
|
if 'ast' in test:
|
||||||
|
self.assertEqual(traverse(parsed), traverse(test['ast']))
|
||||||
|
else:
|
||||||
|
log.debug('No AST for subtest, trying to parse only')
|
||||||
else:
|
else:
|
||||||
log.debug('No AST for subtest, trying to parse only')
|
log.debug('No code in subtest, skipping')
|
||||||
|
|
||||||
log = logging.getLogger('TestJSInterpreterParse.%s' % name)
|
log = logging.getLogger('TestJSInterpreterParse.%s' % name)
|
||||||
return test_template
|
return test_template
|
||||||
|
Loading…
x
Reference in New Issue
Block a user