• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# coding: utf-8
3
4"""
5Copyright (c) 2023 Huawei Device Co., Ltd.
6Licensed under the Apache License, Version 2.0 (the "License");
7you may not use this file except in compliance with the License.
8You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12Unless required by applicable law or agreed to in writing, software
13distributed under the License is distributed on an "AS IS" BASIS,
14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15See the License for the specific language governing permissions and
16limitations under the License.
17
18Description: output test results
19"""
20
21import copy
22import logging
23import os
24import time
25import zipfile
26
27import pandas
28
29import options
30
31
32incremetal_compile_tests = ["no_change",
33                            "add_oneline",
34                            "add_file",
35                            "add_nonexistent_file",
36                            "delete_file",
37                            "reverse_hap_mode",
38                            "change_module_name"
39                            ]
40
41other_tests = ["binary_consistency",
42               "break_continue_compile",
43               "compile_with_error",
44               "compile_with_exceed_length",
45               "ohos_test"
46               ]
47
48
49class TestResult:
50    def __init__(self):
51        self.passed = []
52        self.failed = []
53        self.time = 0.0
54
55
56def print_result(test_result, test_tasks):
57    logging.info("========================================")
58    logging.info("Test finished. The result is as following:")
59    logging.info("=====> Summary")
60    logging.info("Total test number: %s, took time: %.3f s",
61                 len(test_tasks), test_result.time)
62    logging.info("Passed test number: %s", len(test_result.passed))
63    logging.info("Failed test number: %s", len(test_result.failed))
64
65    logging.info("=====> Detail Information")
66    logging.info("-----")
67    idx = 1
68    for task in test_tasks:
69        logging.info("task index: %d", idx)
70        idx = idx + 1
71        logging.info("task name: %s", task.name)
72        logging.info("task type: %s", task.type)
73        # print full compile result
74        logging.info("--full compilation result:")
75        logging.info("debug: %s, abc_size(byte) %s, time(s) %s, error message: %s",
76                     task.full_compilation_info.debug_info.result,
77                     task.full_compilation_info.debug_info.abc_size,
78                     task.full_compilation_info.debug_info.time,
79                     task.full_compilation_info.debug_info.error_message)
80        logging.info("release: %s, abc_size(byte) %s, time(s) %s, error message: %s",
81                     task.full_compilation_info.release_info.result,
82                     task.full_compilation_info.release_info.abc_size,
83                     task.full_compilation_info.release_info.time,
84                     task.full_compilation_info.debug_info.error_message)
85
86        # print incremental compile result
87        logging.info("--incremental compilation result:")
88        for inc_task in task.incre_compilation_info.values():
89            logging.info("incre test: %s", inc_task.name)
90            logging.info("debug: %s, abc_size(byte) %s, time(s) %s, error message: %s",
91                         inc_task.debug_info.result,
92                         inc_task.debug_info.abc_size,
93                         inc_task.debug_info.time,
94                         inc_task.debug_info.error_message)
95            logging.info("release: %s, abc_size(byte) %s, time(s) %s, error message: %s",
96                         inc_task.release_info.result,
97                         inc_task.release_info.abc_size,
98                         inc_task.release_info.time,
99                         inc_task.release_info.error_message)
100
101        # print other tests result
102        for name, task_info in task.other_tests.items():
103            logging.info("--test name: %s", name)
104            logging.info("result: %s, error message: %s",
105                         task_info.result,
106                         task_info.error_message)
107
108        logging.info("-----")
109        logging.info("========================================")
110
111
112def is_full_compilation_passed(task_info):
113    if not options.arguments.compile_mode in ['all', 'full']:
114        return True
115
116    passed_debug = True
117    passed_release = True
118
119    if options.arguments.hap_mode in ['all', 'release']:
120        passed_release = task_info.release_info.result == options.TaskResult.passed
121    if options.arguments.hap_mode == ['all', 'debug']:
122        passed_debug = task_info.debug_info.result == options.TaskResult.passed
123
124    return passed_debug and passed_release
125
126
127def is_incremental_compilation_passed(task_info):
128    if not options.arguments.compile_mode in ['all', 'incremental']:
129        return True
130
131    if len(task_info) == 0:
132        return False
133
134    passed_debug = True
135    passed_release = True
136    for inc_task in task_info.values():
137        if options.arguments.hap_mode in ['all', 'release']:
138            passed_release = passed_release and inc_task.release_info.result == options.TaskResult.passed
139        if options.arguments.hap_mode == ['all', 'debug']:
140            passed_debug = passed_debug and inc_task.debug_info.result == options.TaskResult.passed
141
142    return passed_debug and passed_release
143
144
145def is_task_passed(task):
146    passed = is_full_compilation_passed(task.full_compilation_info) and \
147        is_incremental_compilation_passed(task.incre_compilation_info)
148
149    for test in task.other_tests.values():
150        passed = passed and (test.result == options.TaskResult.passed)
151
152    return passed
153
154
155def collect_result(test_result, test_tasks, start_time):
156    for task in test_tasks:
157        if not is_task_passed(task):
158            test_result.failed.append(task)
159        else:
160            test_result.passed.append(task)
161
162    end_time = time.time()
163    test_result.time = round(end_time - start_time, 3)
164
165
166def get_result_symbol(result_type):
167    if result_type == options.TaskResult.passed:
168        return '√'
169    elif result_type == options.TaskResult.failed:
170        return '×'
171    else:
172        return '-'
173
174
175def generate_summary_data(test_result, test_tasks):
176    # collect summary data
177    passed_task_name_list = []
178    for task in test_result.passed:
179        passed_task_name_list.append(task.name)
180    failed_task_name_list = []
181    for task in test_result.failed:
182        failed_task_name_list.append(task.name)
183
184    summary_data = {
185        'Total Test Number': len(test_tasks),
186        'Passed Test Number': len(test_result.passed),
187        'Failed Test Number': len(test_result.failed),
188        'Passed Tests': ','.join(passed_task_name_list),
189        'Failed Tests': ','.join(failed_task_name_list),
190        'Test Took Time(s)': test_result.time
191    }
192
193    return summary_data
194
195
196def generate_detail_data(test_tasks):
197    time_size_data = []
198    result_data = []
199
200    idx = 0
201    for task in test_tasks:
202        idx += 1
203        task_time_size_data = {
204            'Task Index': idx,
205            'Task Name': task.name
206        }
207        task_result_data = copy.deepcopy(task_time_size_data)
208        task_result_data['Task Type'] = ','.join(task.type)
209
210        full_compilation_debug, full_compilation_release = get_full_build_test_result(task, task_result_data,
211                                                                                      task_time_size_data)
212        get_incremental_build_test_result(task, task_result_data, task_time_size_data)
213
214        get_other_test_result(task, task_result_data)
215
216        task_time_size_data['[Abc Size(byte)]\n[Debug]'] = full_compilation_debug.abc_size
217        task_time_size_data['[Abc Size(byte)]\n[Release]'] = full_compilation_release.abc_size
218        time_size_data.append(task_time_size_data)
219        result_data.append(task_result_data)
220
221    detail_data = {
222        'result_data': result_data,
223        'time_size_data': time_size_data
224    }
225    return detail_data
226
227
228def get_full_build_test_result(task, task_result_data, task_time_size_data):
229    full_compilation_debug = task.full_compilation_info.debug_info
230    full_compilation_release = task.full_compilation_info.release_info
231    task_time_size_data[
232        '[Full Compilation]\n[Debug]\n[Compilation Time(s)]'] = full_compilation_debug.time
233    task_time_size_data[
234        '[Full Compilation]\n[Release]\n[Compilation Time(s)]'] = full_compilation_release.time
235    task_result_data['[Debug]'] = get_result_symbol(
236        full_compilation_debug.result)
237    task_result_data['[Debug-runtime]'] = get_result_symbol(
238        full_compilation_debug.runtime_result)
239    task_result_data['[Release]'] = get_result_symbol(
240        full_compilation_release.result)
241    task_result_data['[Release-runtime]'] = get_result_symbol(
242        full_compilation_release.runtime_result)
243    return full_compilation_debug, full_compilation_release
244
245
246def get_incremental_build_test_result(task, task_result_data, task_time_size_data):
247    for test in incremetal_compile_tests:
248        debug_result = options.TaskResult.undefind
249        debug_runtime_result = options.TaskResult.undefind
250        release_result = options.TaskResult.undefind
251        release_runtime_result = options.TaskResult.undefind
252        if test in task.incre_compilation_info.keys():
253            inc_task_info = task.incre_compilation_info[test]
254            debug_result = inc_task_info.debug_info.result
255            debug_runtime_result = inc_task_info.debug_info.runtime_result
256            release_result = inc_task_info.release_info.result
257            release_runtime_result = inc_task_info.release_info.runtime_result
258        task_result_data[f'[Debug]\n{test}'] = get_result_symbol(
259            debug_result)
260        task_result_data[f'[Debug-runtime]\n{test}'] = get_result_symbol(
261            debug_runtime_result)
262        task_result_data[f'[Release]\n{test}'] = get_result_symbol(
263            release_result)
264        task_result_data[f'[Release-runtime]\n{test}'] = get_result_symbol(
265            release_runtime_result)
266
267        if test == 'add_oneline':
268            debug_test_time = 0
269            release_test_time = 0
270            if test in task.incre_compilation_info.keys():
271                inc_task_info = task.incre_compilation_info[test]
272                debug_test_time = inc_task_info.debug_info.time
273                release_test_time = inc_task_info.release_info.time
274
275            task_time_size_data[
276                '[Incremental Compilation]\n[Debug]\n[Compilation Time(s)]'] = debug_test_time
277            task_time_size_data[
278                '[Incremental Compilation]\n[Release]\n[Compilation Time(s)]'] = release_test_time
279
280
281def get_other_test_result(task, task_result_data):
282    for test in other_tests:
283        result = options.TaskResult.undefind
284        runtime_result = options.TaskResult.undefind
285        if test in task.other_tests.keys():
286            task_info = task.other_tests[test]
287            result = task_info.result
288            runtime_result = task_info.runtime_result
289        task_result_data[f'{test}'] = get_result_symbol(result)
290        task_result_data[f'{test}-runtime'] = get_result_symbol(runtime_result)
291
292
293def rotate_data(df):
294    num_rows, num_cols = df.shape
295    rotated_df = pandas.DataFrame(columns=range(num_rows), index=range(num_cols))
296    for i in range(num_rows):
297        for j in range(num_cols):
298            rotated_df.iloc[j, i] = df.iloc[i, j]
299    return rotated_df
300
301
302def get_merge_data(rotated_df):
303    data = rotated_df.iloc[3:, :].values.tolist()
304    merged_data = []
305    for i in range(0, len(data) - 1, 2):
306        row = [value for sublist in zip(data[i], data[i + 1]) for value in sublist]
307        merged_data.append(row)
308    return merged_data
309
310
311def get_result_table_content(result_df_rotate):
312    merged_data = get_merge_data(result_df_rotate)
313    content = '<tr><th colspan="2" rowspan="2">Full Compilation</th><th>[Debug]</th>' + \
314    ''.join(
315        [f'<th>{column}</th>' for column in merged_data[0]]) + '</tr>'
316    content += '<tr><th>[Release]</th>' + \
317              ''.join(
318                  [f'<th>{column}</th>' for column in merged_data[1]]) + '</tr>'
319    content += f'<tr><th rowspan={len(incremetal_compile_tests) * 2}>Incremental Compilation</th>'
320    start_index = 2
321    for index, item in enumerate(incremetal_compile_tests):
322        incre_debug_result = ''.join([f'<th>{column}</th>' for column in merged_data[start_index]])
323        content = ''.join([content, f'<th rowspan="2">{item}</th><th>[Debug]</th>', incre_debug_result, '</tr>'])
324        incre_release_result = ''.join([f'<th>{column}</th>' for column in merged_data[start_index+1]])
325        content = ''.join([content, '<tr><th>[Release]</th>', incre_release_result, '</tr>'])
326        start_index = start_index + 2
327    content += f'<tr><th colspan=2 rowspan={len(other_tests) * 2}>Other Tests</th>'
328    for index, item in enumerate(other_tests):
329        other_result = ''.join([f'<th>{column}</th>' for column in merged_data[start_index]])
330        content = ''.join([content, f'<th>{item}</th>', other_result, '</tr>'])
331        start_index = start_index + 1
332
333    return content
334
335
336def generate_data_html(summary_data, detail_data):
337    # summary table
338    key_value_pairs = [
339        f'<tr><td>{key}</td><td>{value}</td></tr>' for key, value in summary_data.items()]
340    summary_table_content = ''.join(key_value_pairs)
341    summary_table = f'<table id=sdk>{summary_table_content}</table>'
342
343    # time and size table
344    time_size_data = detail_data.get('time_size_data')
345    time_size_df = pandas.DataFrame(time_size_data)
346
347    time_size_table_header = '<tr>' + \
348        ''.join(
349            [f'<th rowspan="2">{column}</th>' for column in time_size_df.columns[:2]])
350    time_size_table_header += '<th colspan="2">Full Compilation Time(s)</th>' + \
351        f'<th colspan="2">Incremental Compilation Time(s)</th>' + \
352        f'<th colspan="2">Abc Size(byte)</th></tr>'
353    time_size_table_sub_header = '<tr>' + \
354        f'<th>[Debug]</th><th>[Release]</th>' * 3 + '</tr>'
355
356    time_size_table_content = ''.join([
357        '<tr>' + ''.join([f'<td>{value}</td>' for _,
358                         value in row.items()]) + '</tr>'
359        for _, row in time_size_df.iterrows()
360    ])
361    time_size_table = f'<table id=sdk> \
362        {time_size_table_header}{time_size_table_sub_header}{time_size_table_content}</table>'
363
364    # result table
365    result_data = detail_data.get('result_data')
366    result_df = pandas.DataFrame(result_data)
367    result_df_rotate = rotate_data(result_df)
368
369    result_table_header = '<tr><th colspan="3">Task Index</th>' + \
370            ''.join(
371            [f'<th colspan="2">{column}</th>' for column in result_df.iloc[:, 0].tolist()]) + '</tr>'
372    result_table_header += '<tr><th colspan="3">Task Name</th>' + \
373           ''.join(
374               [f'<th colspan="2">{column}</th>' for column in result_df.iloc[:, 1].tolist()]) + '</tr>'
375    result_table_header += '<tr><th colspan="3">Task Type</th>' + \
376                           ''.join(
377                               [f'<th colspan="2">{column}</th>' for column in result_df.iloc[:, 2].tolist()]) + '</tr>'
378    result_table_sub_header = f"<tr><th colspan=3>Build && Run </th>{'<th>[build]</th><th>[runtime]</th>' * result_df.shape[0]}</tr>"
379    result_table_content = get_result_table_content(result_df_rotate)
380
381    result_table = f'<table id=sdk> \
382        {result_table_header}{result_table_sub_header}{result_table_content}</table>'
383
384    return summary_table, time_size_table, result_table
385
386
387def get_html_style():
388    html_style = '''
389         #sdk body {
390            font-family: Arial, sans-serif;
391            margin: 20px;
392        }
393        #sdk h2 {
394            color: #333;
395        }
396        #sdk {
397            border-collapse: collapse;
398            width: 100%;
399            margin-bottom: 20px;
400        }
401        #sdk th, #sdk td {
402            padding: 8px;
403            border: 1px solid #ddd;
404        }
405        #sdk th {
406            background-color: #f2f2f2;
407            font-weight: bold;
408        }
409        #sdk tr:nth-child(odd) {
410            background-color: #f9f9f9;
411        }
412    '''
413    return html_style
414
415
416def generate_report_html(summary_data, detail_data):
417    [summary_table, time_size_table, result_table] = generate_data_html(
418        summary_data, detail_data)
419    html_style = get_html_style()
420
421    html_content = f'''
422    <html>
423    <head>
424    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
425    <style>
426    {html_style}
427    </style>
428    </head>
429    <body>
430    <h2>SDK Test Results</h2>
431    <h3>Summary</h3>
432    {summary_table}
433    <h3>Detail Information</h3>
434    <h4>Test Result</h4>
435    {result_table}
436    <h4>Compilation Time And Abc Size</h4>
437    {time_size_table}
438    <p>
439    Notes:<br>
440    1. Incremental compilation time refers to add-one line incremental compile.<br>
441    2. For details compile output or error message during compile, please refer to attachment of log file.<br>
442    3. For sdk commit tags, please refer to attachment of manifest file(to be added).
443    </p>
444    </body>
445    </html>
446    '''
447
448    daily_report_file = options.configs.get('output_html_file')
449    with open(daily_report_file, 'w', encoding='utf-8') as report:
450        report.write(html_content)
451
452
453def generate_log_file():
454    logger = logging.getLogger()
455    if not hasattr(logger.handlers[0], 'baseFilename'):
456        return
457    log_file = logger.handlers[0].baseFilename
458    logger.handlers[0].close()
459    output_log_file = options.configs.get('log_file')
460    os.rename(log_file, output_log_file)
461
462
463def generate_result_reports(test_result, test_tasks):
464    summary_data = generate_summary_data(test_result, test_tasks)
465    detail_data = generate_detail_data(test_tasks)
466    generate_report_html(summary_data, detail_data)
467    generate_log_file()
468
469
470def process_test_result(test_tasks, start_time):
471    test_result = TestResult()
472
473    collect_result(test_result, test_tasks, start_time)
474    print_result(test_result, test_tasks)
475    generate_result_reports(test_result, test_tasks)
476
477