]> granicus.if.org Git - esp-idf/commitdiff
CI: modify unit test parser to parse unit test cases from build files
authorantti <antti@espressif.com>
Thu, 2 Mar 2017 09:51:25 +0000 (17:51 +0800)
committerHe Yin Ling <heyinling@espressif.com>
Thu, 6 Apr 2017 09:40:55 +0000 (17:40 +0800)
Previously test cases were parsed from test files

tools/unit-test-app/CreateSectionTable.py [new file with mode: 0644]
tools/unit-test-app/UnitTestParser.py

diff --git a/tools/unit-test-app/CreateSectionTable.py b/tools/unit-test-app/CreateSectionTable.py
new file mode 100644 (file)
index 0000000..38285b9
--- /dev/null
@@ -0,0 +1,145 @@
+# This file is used to process section data generated by `objdump -s`
+import re
+
+
+class SectionTable(object):
+
+    class Section(object):
+        SECTION_START_PATTERN = re.compile("Contents of section (.+?):")
+        DATA_PATTERN = re.compile("([0-9a-f]{4,8})")
+
+        def __init__(self, name, start_address, data):
+            self.name = name
+            self.start_address = start_address
+            self.data = data
+
+        def __contains__(self, item):
+            if (item["region"] == self.name or item["region"] == "any") \
+                    and (self.start_address <= item["address"] < (self.start_address + len(self.data))):
+                return True
+            else:
+                return False
+
+        def __getitem__(self, item):
+            if isinstance(item, int):
+                return self.data[item - self.start_address]
+            elif isinstance(item, slice):
+                start = item.start if item.start is None else item.start - self.start_address
+                stop = item.stop if item.stop is None else item.stop - self.start_address
+                return self.data[start:stop]
+            return self.data[item]
+
+        def __str__(self):
+            return "%s [%08x - %08x]" % (self.name, self.start_address, self.start_address+len(self.data))
+
+        __repr__ = __str__
+
+        @classmethod
+        def parse_raw_data(cls, raw_data):
+            name = ""
+            data = ""
+            start_address = 0
+            # first find start line
+            for i, line in enumerate(raw_data):
+                if line.find("Contents of section ") != -1:  # do strcmp first to speed up
+                    match = cls.SECTION_START_PATTERN.search(line)
+                    if match is not None:
+                        name = match.group(1)
+                        raw_data = raw_data[i+1:]
+                        break
+            else:
+                # do some error handling
+                raw_data = [""]  # add a dummy first data line
+                pass
+
+            def process_data_line(line_to_process):
+                # first remove the ascii part
+                hex_part = line_to_process.split("  ")[0]
+                # process rest part
+                data_list = cls.DATA_PATTERN.findall(hex_part)
+                try:
+                    _address = int(data_list[0], base=16)
+                except IndexError:
+                    _address = -1
+
+                def hex_to_str(hex_data):
+                    if len(hex_data) % 2 == 1:
+                        hex_data = "0" + hex_data  # append zero at the beginning
+                    _length = len(hex_data)
+                    return "".join([chr(int(hex_data[_i:_i+2], base=16))
+                                    for _i in range(0, _length, 2)])
+                    pass
+
+                return _address, "".join([hex_to_str(x) for x in data_list[1:]])
+
+            # handle first line:
+            address, _data = process_data_line(raw_data[0])
+            if address != -1:
+                start_address = address
+                data += _data
+                raw_data = raw_data[1:]
+                for i, line in enumerate(raw_data):
+                    address, _data = process_data_line(line)
+                    if address == -1:
+                        raw_data = raw_data[i:]
+                        break
+                    else:
+                        data += _data
+            else:
+                # do error handling
+                raw_data = []
+                pass
+            return cls(name, start_address, data) if start_address != -1 else None,\
+                None if len(raw_data) == 0 else raw_data
+            pass
+
+    def __init__(self, file_name):
+        with open(file_name, "rb") as f:
+            raw_data = f.readlines()
+        self.table = []
+        while raw_data:
+            section, raw_data = self.Section.parse_raw_data(raw_data)
+            self.table.append(section)
+
+    def get_unsigned_int(self, region, address, size=4, endian="LE"):
+        if address % 4 != 0 or size % 4 != 0:
+            print "warning: try to access without 4 bytes aligned"
+        key = {"address": address, "region": region}
+        for section in self.table:
+            if key in section:
+                tmp = section[address:address+size]
+                value = 0
+                for i in range(size):
+                    if endian == "LE":
+                        value += ord(tmp[i]) << (i*8)
+                    elif endian == "BE":
+                        value += ord(tmp[i]) << ((size - i - 1) * 8)
+                    else:
+                        print "only support LE or BE for parameter endian"
+                        assert False
+                break
+        else:
+            value = None
+        return value
+
+    def get_string(self, region, address):
+        value = None
+        key = {"address": address, "region": region}
+        for section in self.table:
+            if key in section:
+                value = section[address:]
+                for i, c in enumerate(value):
+                    if c == '\0':
+                        value = value[:i]
+                        break
+                break
+        return value
+    pass
+
+
+def main():
+    pass
+
+
+if __name__ == '__main__':
+    main()
index d3fa1efb0259ff86a517a81db7baa3ff070b21e0..8096f88391fbc6aa5a0f615e528c46cd9c2c8ddd 100644 (file)
@@ -1,9 +1,9 @@
 import yaml
 import os
-import os.path
 import re
 import sys
 import shutil
+import CreateSectionTable
 
 
 MODULE_MAP = yaml.load(open("ModuleDefinition.yml", "r"))
@@ -39,40 +39,28 @@ IDF_PATH = os.getcwd()
 
 class Parser(object):
     @classmethod
-    def parse_test_folders(cls):
-        test_folder_paths = list()
-        os.chdir(os.path.join(IDF_PATH, "components"))
-        component_dirs = [d for d in os.listdir(".") if os.path.isdir(d)]
-        for dir in component_dirs:
-            os.chdir(dir)
-            if "test" in os.listdir("."):
-                test_folder_paths.append(os.path.join(os.getcwd(), "test"))
-            os.chdir("..")
-        Parser.parse_test_files(test_folder_paths)
-
-    @classmethod
-    def parse_test_files(cls, test_folder_paths):
-        for path in test_folder_paths:
-            os.chdir(path)
-            for file_path in os.listdir("."):
-                if file_path[-2:] == ".c":
-                    Parser.read_test_file(os.path.join(os.getcwd(), file_path), len(test_cases)+1)
+    def parse_test_addresses(cls):
+        table = CreateSectionTable.SectionTable(os.path.join(IDF_PATH, "tools", "unit-test-app", "build", "tmp"))
+        file_index = 1
+        test_index = 1
+        with open(os.path.join(IDF_PATH, "tools", "unit-test-app", "build", "tests"), "r") as file:
+            for line in file:
+                line = line.split()
+                test = int(line[0],16)
+                section = line[3]
+                name_addr = table.get_unsigned_int(section, test, 4)
+                desc_addr = table.get_unsigned_int(section, test + 4, 4)
+                name = table.get_string("any", name_addr)
+                desc = table.get_string("any", desc_addr)
+                Parser.parse_test_cases(file_index, test_index, "%s, %s" % (name, desc))
+                file_index += 1
+                test_index += 1
         os.chdir(os.path.join("..", ".."))
         Parser.dump_test_cases(test_cases)
 
-    @classmethod
-    def read_test_file(cls, test_file_path, file_index):
-        test_index = 0
-        with open(test_file_path, "r") as file:
-            for line in file:
-                if re.match("TEST_CASE", line):
-                    test_index += 1
-                    tags = re.split(r"[\[\]\"]", line)
-                    Parser.parse_test_cases(file_index, test_index, tags)
-
-
     @classmethod
     def parse_test_cases(cls, file_index, test_index, tags):
+        tags = re.split(r"[\[\]\"]", tags)
         ci_ready = "Yes"
         test_env = "UT_T1_1"
         for tag in tags:
@@ -80,7 +68,7 @@ class Parser(object):
                 ci_ready = "No"
             if re.match("test_env=", tag):
                 test_env = tag[9:]
-        module_name = tags[4]
+        module_name = tags[1]
         try:
             MODULE_MAP[module_name]
         except KeyError:
@@ -91,14 +79,14 @@ class Parser(object):
         test_case = dict(TEST_CASE_PATTERN)
         test_case.update({"module": MODULE_MAP[module_name]['module'], 
                           "CI ready": ci_ready,
-                          "cmd set": ["IDFUnitTest/UnitTest", [tags[1]]],
+                          "cmd set": ["IDFUnitTest/UnitTest", [tags[0][:-2]]],
                           "ID": id,
                           "test point 2": module_name,
-                          "steps": tags[1],
-                          "comment": tags[1],
+                          "steps": tags[0][:-2],
+                          "comment": tags[0][:-2],
                           "test environment": test_env,
                           "sub module": MODULE_MAP[module_name]['sub module'],
-                          "summary": tags[1]})
+                          "summary": tags[0][:-2]})
         if test_case["CI ready"] == "Yes":
             if test_ids.has_key(test_env):
                 test_ids[test_env].append(id)
@@ -160,7 +148,10 @@ class Parser(object):
 
 
 def main():
-    Parser.parse_test_folders()
+    os.chdir(os.path.join(IDF_PATH, "tools", "unit-test-app", "build"))
+    os.system('xtensa-esp32-elf-objdump -t unit-test-app.elf | grep test_desc > tests')
+    os.system('xtensa-esp32-elf-objdump -s unit-test-app.elf > tmp')
+    Parser.parse_test_addresses()
     Parser.parse_gitlab_ci()
     Parser.dump_ci_config()
     Parser.copy_module_def_file()