Skip to content

Commit

Permalink
[Add] check bad req (#57578)
Browse files Browse the repository at this point in the history
  • Loading branch information
megemini authored Sep 22, 2023
1 parent 7a9669b commit 89c2952
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 17 deletions.
47 changes: 39 additions & 8 deletions tools/sampcd_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ class BadStatement:
msg: str = ''

def check(self, docstring: str) -> bool:
"""Return `True` for bad statement detected."""
raise NotImplementedError


Expand Down Expand Up @@ -276,6 +277,36 @@ def check(self, docstring):
return False


class DeprecatedRequired(BadStatement):
msg = 'Please use `# doctest: +REQUIRES({})` instead of `# {} {}`.'

_pattern = re.compile(
r"""
\#
\s*
(?P<directive>require[sd]?\s*:)
(?P<env>.+)
""",
re.X,
)

def check(self, docstring):
for match_obj in self._pattern.finditer(docstring):
dep_directive = match_obj.group('directive').strip()
dep_env = match_obj.group('env').strip()

if dep_env:
env = 'env:' + ', env:'.join(
[e.strip().upper() for e in dep_env.split(',') if e.strip()]
)
self.msg = self.__class__.msg.format(
env, dep_directive, dep_env
)
return True

return False


class Xdoctester(DocTester):
"""A Xdoctest doctester."""

Expand All @@ -288,6 +319,7 @@ class Xdoctester(DocTester):
] = {
'fluid': (Fluid,),
'skip': (SkipNoReason,),
'require': (DeprecatedRequired,),
}

def __init__(
Expand Down Expand Up @@ -394,11 +426,12 @@ def prepare(self, test_capacity: set):

self._test_capacity = test_capacity

def _check_bad_statements(self, docstring: str) -> typing.Set[str]:
def _check_bad_statements(self, docstring: str) -> typing.Set[BadStatement]:
bad_results = set()
for name, statement_cls in self.bad_statements.items():
if statement_cls[0](*statement_cls[1:]).check(docstring):
bad_results.add(name)
for _, statement_cls in self.bad_statements.items():
bad_statement = statement_cls[0](*statement_cls[1:])
if bad_statement.check(docstring):
bad_results.add(bad_statement)

return bad_results

Expand All @@ -407,10 +440,8 @@ def run(self, api_name: str, docstring: str) -> typing.List[TestResult]:
# check bad statements
bad_results = self._check_bad_statements(docstring)
if bad_results:
for name in bad_results:
logger.warning(
"%s >>> %s", api_name, str(self.bad_statements[name][0].msg)
)
for bad_statement in bad_results:
logger.warning("%s >>> %s", api_name, bad_statement.msg)

return [
TestResult(
Expand Down
16 changes: 8 additions & 8 deletions tools/sampcd_processor_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def cls_map(mcs) -> typing.Dict[str, Result]:
return mcs.__cls_map


class Passed(Result, metaclass=MetaResult):
class RPassed(Result, metaclass=MetaResult):
name = 'passed'
is_fail = False

Expand All @@ -117,7 +117,7 @@ def msg(cls, count, env):
return f">>> {count} sample codes ran success in env: {env}"


class Skipped(Result, metaclass=MetaResult):
class RSkipped(Result, metaclass=MetaResult):
name = 'skipped'
is_fail = False
logger = logger.warning
Expand All @@ -127,7 +127,7 @@ def msg(cls, count, env):
return f">>> {count} sample codes skipped in env: {env}"


class Failed(Result, metaclass=MetaResult):
class RFailed(Result, metaclass=MetaResult):
name = 'failed'
is_fail = True
logger = logger.error
Expand All @@ -137,7 +137,7 @@ def msg(cls, count, env):
return f">>> {count} sample codes ran failed in env: {env}"


class NoCode(Result, metaclass=MetaResult):
class RNoCode(Result, metaclass=MetaResult):
name = 'nocode'
is_fail = True
logger = logger.error
Expand All @@ -147,7 +147,7 @@ def msg(cls, count, env):
return f">>> {count} apis don't have sample codes or could not run test in env: {env}"


class Timeout(Result, metaclass=MetaResult):
class RTimeout(Result, metaclass=MetaResult):
name = 'timeout'
is_fail = True
logger = logger.error
Expand All @@ -157,7 +157,7 @@ def msg(cls, count, env):
return f">>> {count} sample codes ran timeout or error in env: {env}"


class BadStatement(Result, metaclass=MetaResult):
class RBadStatement(Result, metaclass=MetaResult):
name = 'badstatement'
is_fail = True
logger = logger.error
Expand Down Expand Up @@ -199,8 +199,8 @@ def __init__(self, **kwargs) -> None:

if self.__unique_state is None:
logger.warning('Default result will be set to FAILED!')
setattr(self, Failed.name, True)
self.__unique_state = Failed
setattr(self, RFailed.name, True)
self.__unique_state = RFailed

@property
def state(self) -> Result:
Expand Down
147 changes: 146 additions & 1 deletion tools/test_sampcd_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2320,6 +2320,16 @@ def test_bad_statements(self):
>>> # import paddle.fluid
>>> import os
""",
'oneline_skip': """
this is docstring...
Examples:
.. code-block:: python
>>> import os # doctest: +SKIP
>>> import sys
""",
}

_clear_environ()
Expand All @@ -2329,7 +2339,7 @@ def test_bad_statements(self):
doctester.prepare(test_capacity)

test_results = get_test_results(doctester, docstrings_to_test)
self.assertEqual(len(test_results), 10)
self.assertEqual(len(test_results), 11)

(
tr_0,
Expand All @@ -2342,6 +2352,7 @@ def test_bad_statements(self):
tr_7,
tr_8,
tr_9,
tr_10,
) = test_results

self.assertIn('bad_fluid', tr_0.name)
Expand Down Expand Up @@ -2385,6 +2396,140 @@ def test_bad_statements(self):
self.assertFalse(tr_9.badstatement)
self.assertTrue(tr_9.passed)

self.assertIn('oneline_skip', tr_10.name)
self.assertTrue(tr_10.badstatement)
self.assertFalse(tr_10.passed)

def test_bad_statements_req(self):
docstrings_to_test = {
'bad_required': """
this is docstring...
Examples:
.. code-block:: python
>>> import sys
>>> # required: GPU
>>> import os
""",
'bad_requires': """
this is docstring...
Examples:
.. code-block:: python
>>> import sys
>>> # requires: GPU
>>> import os
""",
'bad_require': """
this is docstring...
Examples:
.. code-block:: python
>>> import sys
>>> # require : GPU
>>> import os
""",
'bad_require_2': """
this is docstring...
Examples:
.. code-block:: python
>>> import sys
>>> # require: GPU, xpu
>>> import os
""",
'bad_req': """
this is docstring...
Examples:
.. code-block:: python
>>> import sys
>>> #require:gpu
>>> import os
""",
'ignore_req': """
this is docstring...
Examples:
.. code-block:: python
>>> import sys
>>> #require:
>>> import os
""",
'ignore_req_bad_req': """
this is docstring...
Examples:
.. code-block:: python
>>> import sys
>>> #require: xpu
>>> import os
>>> #require:
>>> import os
""",
}

_clear_environ()

test_capacity = {'cpu'}
doctester = Xdoctester()
doctester.prepare(test_capacity)

test_results = get_test_results(doctester, docstrings_to_test)
self.assertEqual(len(test_results), 7)

(
tr_0,
tr_1,
tr_2,
tr_3,
tr_4,
tr_5,
tr_6,
) = test_results

self.assertIn('bad_required', tr_0.name)
self.assertTrue(tr_0.badstatement)
self.assertFalse(tr_0.passed)

self.assertIn('bad_requires', tr_1.name)
self.assertTrue(tr_1.badstatement)
self.assertFalse(tr_1.passed)

self.assertIn('bad_require', tr_2.name)
self.assertTrue(tr_1.badstatement)
self.assertFalse(tr_1.passed)

self.assertIn('bad_require_2', tr_3.name)
self.assertTrue(tr_3.badstatement)
self.assertFalse(tr_3.passed)

self.assertIn('bad_req', tr_4.name)
self.assertTrue(tr_4.badstatement)
self.assertFalse(tr_4.passed)

self.assertIn('ignore_req', tr_5.name)
self.assertFalse(tr_5.badstatement)
self.assertTrue(tr_5.passed)

self.assertIn('ignore_req_bad_req', tr_6.name)
self.assertTrue(tr_6.badstatement)
self.assertFalse(tr_6.passed)


if __name__ == '__main__':
unittest.main()

0 comments on commit 89c2952

Please # to comment.