3
3
#
4
4
# Copyright (c) 2018 Intel Corporation
5
5
# SPDX-License-Identifier: Apache-2.0
6
-
7
6
import os
8
7
import contextlib
9
8
import string
34
33
import yaml
35
34
import json
36
35
from multiprocessing import Lock , Process , Value
36
+ from typing import List
37
37
38
38
try :
39
39
# Use the C LibYAML parser if available, rather than the Python parser.
@@ -1576,6 +1576,41 @@ class DisablePyTestCollectionMixin(object):
1576
1576
__test__ = False
1577
1577
1578
1578
1579
+ class ScanPathResult :
1580
+ """Result of the TestCase.scan_path function call.
1581
+
1582
+ Attributes:
1583
+ matches A list of test cases
1584
+ warnings A string containing one or more
1585
+ warnings to display
1586
+ has_registered_test_suites Whether or not the path contained any
1587
+ calls to the ztest_register_test_suite
1588
+ macro.
1589
+ has_run_registered_test_suites Whether or not the path contained at
1590
+ least one call to
1591
+ ztest_run_registered_test_suites.
1592
+ """
1593
+ def __init__ (self ,
1594
+ matches : List [str ] = None ,
1595
+ warnings : str = None ,
1596
+ has_registered_test_suites : bool = False ,
1597
+ has_run_registered_test_suites : bool = False ):
1598
+ self .matches = matches
1599
+ self .warnings = warnings
1600
+ self .has_registered_test_suites = has_registered_test_suites
1601
+ self .has_run_registered_test_suites = has_run_registered_test_suites
1602
+
1603
+ def __eq__ (self , other ):
1604
+ if not isinstance (other , ScanPathResult ):
1605
+ return False
1606
+ return (sorted (self .matches ) == sorted (other .matches ) and
1607
+ self .warnings == other .warnings and
1608
+ (self .has_registered_test_suites ==
1609
+ other .has_registered_test_suites ) and
1610
+ (self .has_run_registered_test_suites ==
1611
+ other .has_run_registered_test_suites ))
1612
+
1613
+
1579
1614
class TestCase (DisablePyTestCollectionMixin ):
1580
1615
"""Class representing a test application
1581
1616
"""
@@ -1662,29 +1697,42 @@ def scan_file(inf_name):
1662
1697
# line--as we only search starting the end of this match
1663
1698
br"^\s*ztest_test_suite\(\s*(?P<suite_name>[a-zA-Z0-9_]+)\s*," ,
1664
1699
re .MULTILINE )
1700
+ registered_suite_regex = re .compile (
1701
+ br"^\s*ztest_register_test_suite"
1702
+ br"\(\s*(?P<suite_name>[a-zA-Z0-9_]+)\s*," ,
1703
+ re .MULTILINE )
1665
1704
stc_regex = re .compile (
1666
- br"^\s*" # empy space at the beginning is ok
1705
+ br""" ^\s* # empy space at the beginning is ok
1667
1706
# catch the case where it is declared in the same sentence, e.g:
1668
1707
#
1669
1708
# ztest_test_suite(mutex_complex, ztest_user_unit_test(TESTNAME));
1670
- br"(?:ztest_test_suite\([a-zA-Z0-9_]+,\s*)?"
1709
+ # ztest_register_test_suite(n, p, ztest_user_unit_test(TESTNAME),
1710
+ (?:ztest_
1711
+ (?:test_suite\(|register_test_suite\([a-zA-Z0-9_]+\s*,\s*)
1712
+ [a-zA-Z0-9_]+\s*,\s*
1713
+ )?
1671
1714
# Catch ztest[_user]_unit_test-[_setup_teardown](TESTNAME)
1672
- br" ztest_(?:1cpu_)?(?:user_)?unit_test(?:_setup_teardown)?"
1715
+ ztest_(?:1cpu_)?(?:user_)?unit_test(?:_setup_teardown)?
1673
1716
# Consume the argument that becomes the extra testcse
1674
- br"\(\s*"
1675
- br"(?P<stc_name>[a-zA-Z0-9_]+)"
1717
+ \(\s*(?P<stc_name>[a-zA-Z0-9_]+)
1676
1718
# _setup_teardown() variant has two extra arguments that we ignore
1677
- br" (?:\s*,\s*[a-zA-Z0-9_]+\s*,\s*[a-zA-Z0-9_]+)?"
1678
- br" \s*\)" ,
1719
+ (?:\s*,\s*[a-zA-Z0-9_]+\s*,\s*[a-zA-Z0-9_]+)?
1720
+ \s*\)"" " ,
1679
1721
# We don't check how it finishes; we don't care
1680
- re .MULTILINE )
1722
+ re .MULTILINE | re . VERBOSE )
1681
1723
suite_run_regex = re .compile (
1682
1724
br"^\s*ztest_run_test_suite\((?P<suite_name>[a-zA-Z0-9_]+)\)" ,
1683
1725
re .MULTILINE )
1726
+ registered_suite_run_regex = re .compile (
1727
+ br"^\s*ztest_run_registered_test_suites\("
1728
+ br"(\*+|&)?(?P<state_identifier>[a-zA-Z0-9_]+)\)" ,
1729
+ re .MULTILINE )
1684
1730
achtung_regex = re .compile (
1685
1731
br"(#ifdef|#endif)" ,
1686
1732
re .MULTILINE )
1687
1733
warnings = None
1734
+ has_registered_test_suites = False
1735
+ has_run_registered_test_suites = False
1688
1736
1689
1737
with open (inf_name ) as inf :
1690
1738
if os .name == 'nt' :
@@ -1695,52 +1743,94 @@ def scan_file(inf_name):
1695
1743
1696
1744
with contextlib .closing (mmap .mmap (** mmap_args )) as main_c :
1697
1745
suite_regex_match = suite_regex .search (main_c )
1698
- if not suite_regex_match :
1746
+ registered_suite_regex_match = registered_suite_regex .search (
1747
+ main_c )
1748
+
1749
+ if registered_suite_regex_match :
1750
+ has_registered_test_suites = True
1751
+ if registered_suite_run_regex .search (main_c ):
1752
+ has_run_registered_test_suites = True
1753
+
1754
+ if not suite_regex_match and not has_registered_test_suites :
1699
1755
# can't find ztest_test_suite, maybe a client, because
1700
1756
# it includes ztest.h
1701
- return None , None
1757
+ return ScanPathResult (
1758
+ matches = None ,
1759
+ warnings = None ,
1760
+ has_registered_test_suites = has_registered_test_suites ,
1761
+ has_run_registered_test_suites = has_run_registered_test_suites )
1702
1762
1703
1763
suite_run_match = suite_run_regex .search (main_c )
1704
- if not suite_run_match :
1764
+ if suite_regex_match and not suite_run_match :
1705
1765
raise ValueError ("can't find ztest_run_test_suite" )
1706
1766
1767
+ if suite_regex_match :
1768
+ search_start = suite_regex_match .end ()
1769
+ else :
1770
+ search_start = registered_suite_regex_match .end ()
1771
+
1772
+ if suite_run_match :
1773
+ search_end = suite_run_match .start ()
1774
+ else :
1775
+ search_end = re .compile (br"\);" , re .MULTILINE ) \
1776
+ .search (main_c , search_start ) \
1777
+ .end ()
1707
1778
achtung_matches = re .findall (
1708
1779
achtung_regex ,
1709
- main_c [suite_regex_match . end (): suite_run_match . start () ])
1780
+ main_c [search_start : search_end ])
1710
1781
if achtung_matches :
1711
1782
warnings = "found invalid %s in ztest_test_suite()" \
1712
1783
% ", " .join (sorted ({match .decode () for match in achtung_matches },reverse = True ))
1713
1784
_matches = re .findall (
1714
1785
stc_regex ,
1715
- main_c [suite_regex_match . end (): suite_run_match . start () ])
1786
+ main_c [search_start : search_end ])
1716
1787
for match in _matches :
1717
1788
if not match .decode ().startswith ("test_" ):
1718
1789
warnings = "Found a test that does not start with test_"
1719
1790
matches = [match .decode ().replace ("test_" , "" , 1 ) for match in _matches ]
1720
- return matches , warnings
1791
+ return ScanPathResult (
1792
+ matches = matches ,
1793
+ warnings = warnings ,
1794
+ has_registered_test_suites = has_registered_test_suites ,
1795
+ has_run_registered_test_suites = has_run_registered_test_suites )
1721
1796
1722
1797
def scan_path (self , path ):
1723
1798
subcases = []
1799
+ has_registered_test_suites = False
1800
+ has_run_registered_test_suites = False
1724
1801
for filename in glob .glob (os .path .join (path , "src" , "*.c*" )):
1725
1802
try :
1726
- _subcases , warnings = self .scan_file (filename )
1727
- if warnings :
1728
- logger .error ("%s: %s" % (filename , warnings ))
1729
- raise TwisterRuntimeError ("%s: %s" % (filename , warnings ))
1730
- if _subcases :
1731
- subcases += _subcases
1803
+ result : ScanPathResult = self .scan_file (filename )
1804
+ if result .warnings :
1805
+ logger .error ("%s: %s" % (filename , result .warnings ))
1806
+ raise TwisterRuntimeError (
1807
+ "%s: %s" % (filename , result .warnings ))
1808
+ if result .matches :
1809
+ subcases += result .matches
1810
+ if result .has_registered_test_suites :
1811
+ has_registered_test_suites = True
1812
+ if result .has_run_registered_test_suites :
1813
+ has_run_registered_test_suites = True
1732
1814
except ValueError as e :
1733
1815
logger .error ("%s: can't find: %s" % (filename , e ))
1734
1816
1735
1817
for filename in glob .glob (os .path .join (path , "*.c" )):
1736
1818
try :
1737
- _subcases , warnings = self .scan_file (filename )
1738
- if warnings :
1739
- logger .error ("%s: %s" % (filename , warnings ))
1740
- if _subcases :
1741
- subcases += _subcases
1819
+ result : ScanPathResult = self .scan_file (filename )
1820
+ if result . warnings :
1821
+ logger .error ("%s: %s" % (filename , result . warnings ))
1822
+ if result . matches :
1823
+ subcases += result . matches
1742
1824
except ValueError as e :
1743
1825
logger .error ("%s: can't find: %s" % (filename , e ))
1826
+
1827
+ if has_registered_test_suites and not has_run_registered_test_suites :
1828
+ warning = \
1829
+ "Found call to 'ztest_register_test_suite()' but no " \
1830
+ "call to 'ztest_run_registered_test_suites()'"
1831
+ logger .error (warning )
1832
+ raise TwisterRuntimeError (warning )
1833
+
1744
1834
return subcases
1745
1835
1746
1836
def parse_subcases (self , test_path ):
0 commit comments