Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
avocado
提交
5ae77d8b
A
avocado
项目概览
openeuler
/
avocado
通知
0
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
A
avocado
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
5ae77d8b
编写于
7月 19, 2018
作者:
A
Amador Pahim
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'clebergnu-instrumented_loader_to_safeloader'
Signed-off-by:
N
Amador Pahim
<
amador@pahim.org
>
上级
ea7732d7
a308549e
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
296 addition
and
285 deletion
+296
-285
avocado/core/loader.py
avocado/core/loader.py
+1
-200
avocado/core/safeloader.py
avocado/core/safeloader.py
+202
-0
selftests/unit/test_loader.py
selftests/unit/test_loader.py
+0
-81
selftests/unit/test_safeloader.py
selftests/unit/test_safeloader.py
+93
-4
未找到文件。
avocado/core/loader.py
浏览文件 @
5ae77d8b
...
...
@@ -17,8 +17,6 @@
Test loader module.
"""
import
ast
import
collections
import
imp
import
inspect
import
os
...
...
@@ -634,203 +632,6 @@ class FileLoader(TestLoader):
subtests_filter
))
return
tests
def
_find_avocado_tests
(
self
,
path
,
class_name
=
None
):
"""
Attempts to find Avocado instrumented tests from Python source files
:param path: path to a Python source code file
:type path: str
:param class_name: the specific class to be found
:type path: str
:returns: tuple where first item is dict with class name and additional
info such as method names and tags; the second item is
set of class names which look like avocado tests but are
force-disabled.
:rtype: tuple
"""
# If only the Test class was imported from the avocado namespace
test_import
=
False
# The name used, in case of 'from avocado import Test as AvocadoTest'
test_import_name
=
None
# If the "avocado" module itself was imported
mod_import
=
False
# The name used, in case of 'import avocado as avocadolib'
mod_import_name
=
None
# The resulting test classes
result
=
collections
.
OrderedDict
()
disabled
=
set
()
if
os
.
path
.
isdir
(
path
):
path
=
os
.
path
.
join
(
path
,
"__init__.py"
)
with
open
(
path
)
as
source_file
:
mod
=
ast
.
parse
(
source_file
.
read
(),
path
)
for
statement
in
mod
.
body
:
# Looking for a 'from avocado import Test'
if
(
isinstance
(
statement
,
ast
.
ImportFrom
)
and
statement
.
module
==
'avocado'
):
for
name
in
statement
.
names
:
if
name
.
name
==
'Test'
:
test_import
=
True
if
name
.
asname
is
not
None
:
test_import_name
=
name
.
asname
else
:
test_import_name
=
name
.
name
break
# Looking for a 'import avocado'
elif
isinstance
(
statement
,
ast
.
Import
):
for
name
in
statement
.
names
:
if
name
.
name
==
'avocado'
:
mod_import
=
True
if
name
.
asname
is
not
None
:
mod_import_name
=
name
.
nasname
else
:
mod_import_name
=
name
.
name
# Looking for a 'class Anything(anything):'
elif
isinstance
(
statement
,
ast
.
ClassDef
):
# class_name will exist only under recursion. In that
# case, we will only process the class if it has the
# expected class_name.
if
class_name
is
not
None
and
class_name
!=
statement
.
name
:
continue
docstring
=
ast
.
get_docstring
(
statement
)
# Looking for a class that has in the docstring either
# ":avocado: enable" or ":avocado: disable
has_disable
=
safeloader
.
check_docstring_directive
(
docstring
,
'disable'
)
if
(
has_disable
and
class_name
is
None
):
disabled
.
add
(
statement
.
name
)
continue
cl_tags
=
safeloader
.
get_docstring_directives_tags
(
docstring
)
has_enable
=
safeloader
.
check_docstring_directive
(
docstring
,
'enable'
)
if
(
has_enable
and
class_name
is
None
):
info
=
self
.
_get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
continue
# Looking for the 'recursive' docstring or a 'class_name'
# (meaning we are under recursion)
has_recurse
=
safeloader
.
check_docstring_directive
(
docstring
,
'recursive'
)
if
(
has_recurse
or
class_name
is
not
None
):
info
=
self
.
_get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
# Getting the list of parents of the current class
parents
=
statement
.
bases
# Searching the parents in the same module
for
parent
in
parents
[:]:
# Looking for a 'class FooTest(module.Parent)'
if
isinstance
(
parent
,
ast
.
Attribute
):
parent_class
=
parent
.
attr
# Looking for a 'class FooTest(Parent)'
else
:
parent_class
=
parent
.
id
res
,
dis
=
self
.
_find_avocado_tests
(
path
,
parent_class
)
if
res
:
parents
.
remove
(
parent
)
for
cls
in
res
:
info
.
extend
(
res
[
cls
])
disabled
.
update
(
dis
)
# If there are parents left to be discovered, they
# might be in a different module.
for
parent
in
parents
:
if
isinstance
(
parent
,
ast
.
Attribute
):
# Looking for a 'class FooTest(module.Parent)'
parent_module
=
parent
.
value
.
id
parent_class
=
parent
.
attr
else
:
# Looking for a 'class FooTest(Parent)'
parent_module
=
None
parent_class
=
parent
.
id
for
node
in
mod
.
body
:
reference
=
None
# Looking for 'from parent import class'
if
isinstance
(
node
,
ast
.
ImportFrom
):
reference
=
parent_class
# Looking for 'import parent'
elif
isinstance
(
node
,
ast
.
Import
):
reference
=
parent_module
if
reference
is
None
:
continue
for
artifact
in
node
.
names
:
# Looking for a class alias
# ('from parent import class as alias')
if
artifact
.
asname
is
not
None
:
parent_class
=
reference
=
artifact
.
name
# If the parent class or the parent module
# is found in the imports, discover the
# parent module path and find the parent
# class there
if
artifact
.
name
==
reference
:
modules_paths
=
[
os
.
path
.
dirname
(
path
)]
modules_paths
.
extend
(
sys
.
path
)
if
parent_module
is
None
:
parent_module
=
node
.
module
_
,
ppath
,
_
=
imp
.
find_module
(
parent_module
,
modules_paths
)
res
,
dis
=
self
.
_find_avocado_tests
(
ppath
,
parent_class
)
if
res
:
for
cls
in
res
:
info
.
extend
(
res
[
cls
])
disabled
.
update
(
dis
)
continue
if
test_import
:
base_ids
=
[
base
.
id
for
base
in
statement
.
bases
if
hasattr
(
base
,
'id'
)]
# Looking for a 'class FooTest(Test):'
if
test_import_name
in
base_ids
:
info
=
self
.
_get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
continue
# Looking for a 'class FooTest(avocado.Test):'
if
mod_import
:
for
base
in
statement
.
bases
:
module
=
base
.
value
.
id
klass
=
base
.
attr
if
module
==
mod_import_name
and
klass
==
'Test'
:
info
=
self
.
_get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
continue
return
result
,
disabled
@
staticmethod
def
_get_methods_info
(
statement_body
,
class_tags
):
methods_info
=
[]
for
st
in
statement_body
:
if
(
isinstance
(
st
,
ast
.
FunctionDef
)
and
st
.
name
.
startswith
(
'test'
)):
docstring
=
ast
.
get_docstring
(
st
)
mt_tags
=
safeloader
.
get_docstring_directives_tags
(
docstring
)
mt_tags
.
update
(
class_tags
)
methods
=
[
method
for
method
,
_
in
methods_info
]
if
st
.
name
not
in
methods
:
methods_info
.
append
((
st
.
name
,
mt_tags
))
return
methods_info
def
_find_python_unittests
(
self
,
test_path
,
disabled
,
subtests_filter
):
result
=
[]
class_methods
=
safeloader
.
find_class_and_methods
(
test_path
,
...
...
@@ -856,7 +657,7 @@ class FileLoader(TestLoader):
test_name
=
test_path
try
:
# Avocado tests
avocado_tests
,
disabled
=
s
elf
.
_
find_avocado_tests
(
test_path
)
avocado_tests
,
disabled
=
s
afeloader
.
find_avocado_tests
(
test_path
)
if
avocado_tests
:
test_factories
=
[]
for
test_class
,
info
in
avocado_tests
.
items
():
...
...
avocado/core/safeloader.py
浏览文件 @
5ae77d8b
...
...
@@ -17,7 +17,11 @@ Safe (AST based) test loader module utilities
"""
import
ast
import
collections
import
imp
import
os
import
re
import
sys
from
..utils
import
data_structures
...
...
@@ -154,3 +158,201 @@ def find_class_and_methods(path, method_pattern=None, base_class=None):
methods
=
data_structures
.
ordered_list_unique
(
methods
)
result
[
statement
.
name
]
=
methods
return
result
def
get_methods_info
(
statement_body
,
class_tags
):
"""
Returns information on an Avocado instrumented test method
"""
methods_info
=
[]
for
st
in
statement_body
:
if
(
isinstance
(
st
,
ast
.
FunctionDef
)
and
st
.
name
.
startswith
(
'test'
)):
docstring
=
ast
.
get_docstring
(
st
)
mt_tags
=
get_docstring_directives_tags
(
docstring
)
mt_tags
.
update
(
class_tags
)
methods
=
[
method
for
method
,
_
in
methods_info
]
if
st
.
name
not
in
methods
:
methods_info
.
append
((
st
.
name
,
mt_tags
))
return
methods_info
def
find_avocado_tests
(
path
,
class_name
=
None
):
"""
Attempts to find Avocado instrumented tests from Python source files
:param path: path to a Python source code file
:type path: str
:param class_name: the specific class to be found
:type path: str
:returns: tuple where first item is dict with class name and additional
info such as method names and tags; the second item is
set of class names which look like avocado tests but are
force-disabled.
:rtype: tuple
"""
# If only the Test class was imported from the avocado namespace
test_import
=
False
# The name used, in case of 'from avocado import Test as AvocadoTest'
test_import_name
=
None
# If the "avocado" module itself was imported
mod_import
=
False
# The name used, in case of 'import avocado as avocadolib'
mod_import_name
=
None
# The resulting test classes
result
=
collections
.
OrderedDict
()
disabled
=
set
()
if
os
.
path
.
isdir
(
path
):
path
=
os
.
path
.
join
(
path
,
"__init__.py"
)
with
open
(
path
)
as
source_file
:
mod
=
ast
.
parse
(
source_file
.
read
(),
path
)
for
statement
in
mod
.
body
:
# Looking for a 'from avocado import Test'
if
(
isinstance
(
statement
,
ast
.
ImportFrom
)
and
statement
.
module
==
'avocado'
):
for
name
in
statement
.
names
:
if
name
.
name
==
'Test'
:
test_import
=
True
if
name
.
asname
is
not
None
:
test_import_name
=
name
.
asname
else
:
test_import_name
=
name
.
name
break
# Looking for a 'import avocado'
elif
isinstance
(
statement
,
ast
.
Import
):
for
name
in
statement
.
names
:
if
name
.
name
==
'avocado'
:
mod_import
=
True
if
name
.
asname
is
not
None
:
mod_import_name
=
name
.
nasname
else
:
mod_import_name
=
name
.
name
# Looking for a 'class Anything(anything):'
elif
isinstance
(
statement
,
ast
.
ClassDef
):
# class_name will exist only under recursion. In that
# case, we will only process the class if it has the
# expected class_name.
if
class_name
is
not
None
and
class_name
!=
statement
.
name
:
continue
docstring
=
ast
.
get_docstring
(
statement
)
# Looking for a class that has in the docstring either
# ":avocado: enable" or ":avocado: disable
has_disable
=
check_docstring_directive
(
docstring
,
'disable'
)
if
(
has_disable
and
class_name
is
None
):
disabled
.
add
(
statement
.
name
)
continue
cl_tags
=
get_docstring_directives_tags
(
docstring
)
has_enable
=
check_docstring_directive
(
docstring
,
'enable'
)
if
(
has_enable
and
class_name
is
None
):
info
=
get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
continue
# Looking for the 'recursive' docstring or a 'class_name'
# (meaning we are under recursion)
has_recurse
=
check_docstring_directive
(
docstring
,
'recursive'
)
if
(
has_recurse
or
class_name
is
not
None
):
info
=
get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
# Getting the list of parents of the current class
parents
=
statement
.
bases
# Searching the parents in the same module
for
parent
in
parents
[:]:
# Looking for a 'class FooTest(module.Parent)'
if
isinstance
(
parent
,
ast
.
Attribute
):
parent_class
=
parent
.
attr
# Looking for a 'class FooTest(Parent)'
else
:
parent_class
=
parent
.
id
res
,
dis
=
find_avocado_tests
(
path
,
parent_class
)
if
res
:
parents
.
remove
(
parent
)
for
cls
in
res
:
info
.
extend
(
res
[
cls
])
disabled
.
update
(
dis
)
# If there are parents left to be discovered, they
# might be in a different module.
for
parent
in
parents
:
if
isinstance
(
parent
,
ast
.
Attribute
):
# Looking for a 'class FooTest(module.Parent)'
parent_module
=
parent
.
value
.
id
parent_class
=
parent
.
attr
else
:
# Looking for a 'class FooTest(Parent)'
parent_module
=
None
parent_class
=
parent
.
id
for
node
in
mod
.
body
:
reference
=
None
# Looking for 'from parent import class'
if
isinstance
(
node
,
ast
.
ImportFrom
):
reference
=
parent_class
# Looking for 'import parent'
elif
isinstance
(
node
,
ast
.
Import
):
reference
=
parent_module
if
reference
is
None
:
continue
for
artifact
in
node
.
names
:
# Looking for a class alias
# ('from parent import class as alias')
if
artifact
.
asname
is
not
None
:
parent_class
=
reference
=
artifact
.
name
# If the parent class or the parent module
# is found in the imports, discover the
# parent module path and find the parent
# class there
if
artifact
.
name
==
reference
:
modules_paths
=
[
os
.
path
.
dirname
(
path
)]
modules_paths
.
extend
(
sys
.
path
)
if
parent_module
is
None
:
parent_module
=
node
.
module
_
,
ppath
,
_
=
imp
.
find_module
(
parent_module
,
modules_paths
)
res
,
dis
=
find_avocado_tests
(
ppath
,
parent_class
)
if
res
:
for
cls
in
res
:
info
.
extend
(
res
[
cls
])
disabled
.
update
(
dis
)
continue
if
test_import
:
base_ids
=
[
base
.
id
for
base
in
statement
.
bases
if
hasattr
(
base
,
'id'
)]
# Looking for a 'class FooTest(Test):'
if
test_import_name
in
base_ids
:
info
=
get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
continue
# Looking for a 'class FooTest(avocado.Test):'
if
mod_import
:
for
base
in
statement
.
bases
:
module
=
base
.
value
.
id
klass
=
base
.
attr
if
module
==
mod_import_name
and
klass
==
'Test'
:
info
=
get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
continue
return
result
,
disabled
selftests/unit/test_loader.py
浏览文件 @
5ae77d8b
import
os
import
shutil
import
stat
import
sys
import
multiprocessing
import
tempfile
import
unittest
...
...
@@ -187,57 +186,6 @@ class Second(avocado.Test):
pass
"""
KEEP_METHODS_ORDER
=
'''
from avocado import Test
class MyClass(Test):
def test2(self):
pass
def testA(self):
pass
def test1(self):
pass
def testZZZ(self):
pass
def test(self):
pass
'''
RECURSIVE_DISCOVERY_TEST1
=
"""
from avocado import Test
class BaseClass(Test):
def test_basic(self):
pass
class FirstChild(BaseClass):
def test_first_child(self):
pass
class SecondChild(FirstChild):
'''
:avocado: disable
'''
def test_second_child(self):
pass
"""
RECURSIVE_DISCOVERY_TEST2
=
"""
from avocado import Test
from recursive_discovery_test1 import SecondChild
class ThirdChild(Test, SecondChild):
'''
:avocado: recursive
'''
def test_third_child(self):
pass
"""
PYTHON_UNITTEST
=
"""#!/usr/bin/env python
from unittest import TestCase
...
...
@@ -515,35 +463,6 @@ class LoaderTest(unittest.TestCase):
'does,not,exist'
])
self
.
assertEqual
(
len
(
filtered
),
0
)
def
test_methods_order
(
self
):
avocado_keep_methods_order
=
script
.
TemporaryScript
(
'keepmethodsorder.py'
,
KEEP_METHODS_ORDER
)
avocado_keep_methods_order
.
save
()
expected_order
=
[
'test2'
,
'testA'
,
'test1'
,
'testZZZ'
,
'test'
]
tests
=
self
.
loader
.
_find_avocado_tests
(
avocado_keep_methods_order
.
path
)[
0
]
methods
=
[
method
[
0
]
for
method
in
tests
[
'MyClass'
]]
self
.
assertEqual
(
expected_order
,
methods
)
avocado_keep_methods_order
.
remove
()
def
test_recursive_discovery
(
self
):
avocado_recursive_discovery_test1
=
script
.
TemporaryScript
(
'recursive_discovery_test1.py'
,
RECURSIVE_DISCOVERY_TEST1
)
avocado_recursive_discovery_test1
.
save
()
avocado_recursive_discovery_test2
=
script
.
TemporaryScript
(
'recursive_discovery_test2.py'
,
RECURSIVE_DISCOVERY_TEST2
)
avocado_recursive_discovery_test2
.
save
()
sys
.
path
.
append
(
os
.
path
.
dirname
(
avocado_recursive_discovery_test1
.
path
))
tests
=
self
.
loader
.
_find_avocado_tests
(
avocado_recursive_discovery_test2
.
path
)[
0
]
expected
=
{
'ThirdChild'
:
[(
'test_third_child'
,
set
([])),
(
'test_second_child'
,
set
([])),
(
'test_first_child'
,
set
([])),
(
'test_basic'
,
set
([]))]}
self
.
assertEqual
(
expected
,
tests
)
def
test_python_unittest
(
self
):
disabled_test
=
script
.
TemporaryScript
(
"disabled.py"
,
AVOCADO_TEST_OK_DISABLED
,
...
...
selftests/unit/test_safeloader.py
浏览文件 @
5ae77d8b
import
ast
import
sys
import
os
import
re
import
unittest
...
...
@@ -6,6 +8,58 @@ from avocado.core import safeloader
from
avocado.utils
import
script
KEEP_METHODS_ORDER
=
'''
from avocado import Test
class MyClass(Test):
def test2(self):
pass
def testA(self):
pass
def test1(self):
pass
def testZZZ(self):
pass
def test(self):
pass
'''
RECURSIVE_DISCOVERY_TEST1
=
"""
from avocado import Test
class BaseClass(Test):
def test_basic(self):
pass
class FirstChild(BaseClass):
def test_first_child(self):
pass
class SecondChild(FirstChild):
'''
:avocado: disable
'''
def test_second_child(self):
pass
"""
RECURSIVE_DISCOVERY_TEST2
=
"""
from avocado import Test
from recursive_discovery_test1 import SecondChild
class ThirdChild(Test, SecondChild):
'''
:avocado: recursive
'''
def test_third_child(self):
pass
"""
def
get_this_file
():
this_file
=
__file__
if
this_file
.
endswith
(
'.py'
):
...
...
@@ -165,7 +219,9 @@ class FindClassAndMethods(UnlimitedDiff):
'FindClassAndMethods'
:
[
'test_self'
,
'test_with_pattern'
,
'test_with_base_class'
,
'test_with_pattern_and_base_class'
],
'test_with_pattern_and_base_class'
,
'test_methods_order'
,
'test_recursive_discovery'
],
'UnlimitedDiff'
:
[
'setUp'
]
}
found
=
safeloader
.
find_class_and_methods
(
get_this_file
())
...
...
@@ -187,7 +243,9 @@ class FindClassAndMethods(UnlimitedDiff):
'FindClassAndMethods'
:
[
'test_self'
,
'test_with_pattern'
,
'test_with_base_class'
,
'test_with_pattern_and_base_class'
],
'test_with_pattern_and_base_class'
,
'test_methods_order'
,
'test_recursive_discovery'
],
'UnlimitedDiff'
:
[]
}
found
=
safeloader
.
find_class_and_methods
(
get_this_file
(),
...
...
@@ -199,7 +257,9 @@ class FindClassAndMethods(UnlimitedDiff):
'FindClassAndMethods'
:
[
'test_self'
,
'test_with_pattern'
,
'test_with_base_class'
,
'test_with_pattern_and_base_class'
],
'test_with_pattern_and_base_class'
,
'test_methods_order'
,
'test_recursive_discovery'
],
}
found
=
safeloader
.
find_class_and_methods
(
get_this_file
(),
base_class
=
'UnlimitedDiff'
)
...
...
@@ -209,13 +269,42 @@ class FindClassAndMethods(UnlimitedDiff):
reference
=
{
'FindClassAndMethods'
:
[
'test_with_pattern'
,
'test_with_base_class'
,
'test_with_pattern_and_base_class'
]
,
'test_with_pattern_and_base_class'
]
}
found
=
safeloader
.
find_class_and_methods
(
get_this_file
(),
re
.
compile
(
r
'test_with.*'
),
'UnlimitedDiff'
)
self
.
assertEqual
(
reference
,
found
)
def
test_methods_order
(
self
):
avocado_keep_methods_order
=
script
.
TemporaryScript
(
'keepmethodsorder.py'
,
KEEP_METHODS_ORDER
)
avocado_keep_methods_order
.
save
()
expected_order
=
[
'test2'
,
'testA'
,
'test1'
,
'testZZZ'
,
'test'
]
tests
=
safeloader
.
find_avocado_tests
(
avocado_keep_methods_order
.
path
)[
0
]
methods
=
[
method
[
0
]
for
method
in
tests
[
'MyClass'
]]
self
.
assertEqual
(
expected_order
,
methods
)
avocado_keep_methods_order
.
remove
()
def
test_recursive_discovery
(
self
):
avocado_recursive_discovery_test1
=
script
.
TemporaryScript
(
'recursive_discovery_test1.py'
,
RECURSIVE_DISCOVERY_TEST1
)
avocado_recursive_discovery_test1
.
save
()
avocado_recursive_discovery_test2
=
script
.
TemporaryScript
(
'recursive_discovery_test2.py'
,
RECURSIVE_DISCOVERY_TEST2
)
avocado_recursive_discovery_test2
.
save
()
sys
.
path
.
append
(
os
.
path
.
dirname
(
avocado_recursive_discovery_test1
.
path
))
tests
=
safeloader
.
find_avocado_tests
(
avocado_recursive_discovery_test2
.
path
)[
0
]
expected
=
{
'ThirdChild'
:
[(
'test_third_child'
,
set
([])),
(
'test_second_child'
,
set
([])),
(
'test_first_child'
,
set
([])),
(
'test_basic'
,
set
([]))]}
self
.
assertEqual
(
expected
,
tests
)
if
__name__
==
'__main__'
:
unittest
.
main
()
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录