diff options
-rw-r--r-- | LICENSE | 201 | ||||
-rw-r--r-- | README | 82 | ||||
-rwxr-xr-x | agl-test | 3 | ||||
-rw-r--r-- | conftest.py | 170 | ||||
-rw-r--r-- | plugins/agl_test_conf.py | 19 | ||||
-rw-r--r-- | plugins/agl_test_log.py | 65 | ||||
-rw-r--r-- | plugins/agl_test_report.py | 120 | ||||
-rw-r--r-- | plugins/agl_test_utils.py | 31 | ||||
-rw-r--r-- | pytest.ini | 7 |
9 files changed, 698 insertions, 0 deletions
@@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. @@ -0,0 +1,82 @@ +#AGL Test Framwork + +This repository stores the code of agl-test-framework + +The "agl-test framework" encapsulates pytest,which aims to provide a +unified test set execution entrance. It supports to run various test sets, +even these test sets come from different test frameworks, processing these +test logs uniformly, and generating complete test report. + +In this way, it is convenient to test as many targets as possible, +in a wide range, so that the test covers a wider range of objects, +and is more comprehensive. + +At present, we plan to support the following test sets in "agl-test": + 1. Transplant test sets under Fuego and AGL-JTA + 2. Retain the test set under pyagl and agl-ptest + (so will depend on "agl-ptest") + 3. Migrate new test sets (with upstream) + 4. Append new test sets (without upstream) + +The output of test run is summarized by levels. +The first level is the summary of all test sets, and the second level is +the summary of a single test set. Now, they are displayed in HTML format, +and other formats also can be considered later. + +You can compile "agl-test-framework" and "qa-test-misc" +by setting the feature "agl-test" when building AGL Image +Refer to the compilation document of AGL community + https://docs.automotivelinux.org/en/marlin + 0_Getting_Started + 2_Building_AGL_Image + 0_Build_Process + +##Running + +1. extract the image into the boot device +2. run agl-test cmd with root + +###Run all of the test sets: + + agl-test + +###Run default test sets: + + agl-test -m oss_default + +###Run test sets of custom task: + + agl-test -m ${custom_task} + +###Run the specified test set: + + agl-test tests/${test_set_name}/run_tests.py + +##Directory Structure + +├── agl-test +├── conftest.py +├── LICENSE +├── plugins +│ ├── agl_test_conf.py +│ ├── agl_test_log.py +│ ├── agl_test_report.py +│ └── agl_test_utils.py +├── pytest.ini +└── README + +###agl-test: + +The command to start the test + +###conftest.py: + +Fixture function define + +###plugins: + +General script files + +###pytest.ini: + +Customize the contents of test sets diff --git a/agl-test b/agl-test new file mode 100755 index 0000000..569784f --- /dev/null +++ b/agl-test @@ -0,0 +1,3 @@ +#!/bin/sh +cd /usr/AGL/agl-test/ +exec pytest "$@" diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..eade841 --- /dev/null +++ b/conftest.py @@ -0,0 +1,170 @@ +# -*- coding:utf-8 -*- +import pytest +import json +import shutil +import subprocess + +from plugins.agl_test_conf import BASE_LOGS_DIR +from plugins.agl_test_conf import TMP_LOGS_DIR +from plugins.agl_test_conf import REPORT_LOGS_DIR +from plugins.agl_test_conf import TMP_TEST_REPORT + + +@pytest.fixture(scope='session' ,autouse=True) +def setup_compress_function(): + #Before the test start, clean the env + report_json = TMP_LOGS_DIR + "report.json" + output = subprocess.run(['ls',report_json],stdout=subprocess.PIPE,stderr=subprocess.PIPE) + if(output.returncode == 0): + subprocess.run(['rm',report_json]) + + #Makdir of TMP_TEST_REPORT and REPORT_LOGS_DIR + subprocess.run(['mkdir','-p',TMP_TEST_REPORT]) + subprocess.run(['mkdir','-p',REPORT_LOGS_DIR]) + + yield + #Collect report.json from all test sets to generate a report.json for all the test sets + report_files = TMP_LOGS_DIR + "report_files" + with open(report_files,'w') as report_f: + subprocess.run(['find','-name','report.json'],cwd=TMP_LOGS_DIR,stdout=report_f) + report_f.close() + + #Get the summary data and write to report.json file + summary_data = get_summary_data(report_files) + summary_json = TMP_LOGS_DIR + "/report.json" + with open(summary_json, 'w') as summary_file: + json.dump(summary_data,summary_file,indent=4,sort_keys=False) + summary_file.close() + + #Creat summary report in html + html = get_summary_report_html(summary_data) + html_path = TMP_LOGS_DIR + "test-report/summary-report.html" + html_file = open(html_path,"w") + html_file.write(html) + html_file.close() + + #Copy summary report file + source_file = TMP_LOGS_DIR + "test-report/summary-report.html" + target_file = REPORT_LOGS_DIR + "summary-report.html" + shutil.copyfile(source_file,target_file) + + #Package the test report + #TODO The name of the zip file is formatted as follows: + # agl-test-log-13.0.1-raspberrypi4-20200808.zip + base_name = REPORT_LOGS_DIR + "agl-test-log-xxx" + root_dir = TMP_LOGS_DIR + "test-report" + shutil.make_archive(base_name,"zip",root_dir) + + # TODO: + # Upload the final log to Artifactorial + +#Summarize all reports.json file +def get_summary_data(report_files): + summary_data = {} + summary_total = summary_passed = summary_failed = summary_skipped = 0 + files = open(report_files) + while True: + report = files.readline() + if not report: + break + report = report[1:-1] + report_json = TMP_LOGS_DIR + report + with open(report_json,'r') as f: + data = json.load(f) + + total = passed = failed = skipped = 0 + total = data["collected"] + passed = data["passed"] + failed = data["failed"] + skipped = data["skipped"] + test_status = data["test_status"] + test_name = data["test_name"] + + this_summary = { + 'total': total, + 'passed': passed, + 'failed': failed, + 'skipped': skipped, + 'test_status': test_status, + } + summary_data[test_name] = this_summary + + summary_total = summary_total + 1 + if(test_status=="passed"): + summary_passed = summary_passed + 1 + elif(test_status=="failed"): + summary_failed = summary_failed + 1 + else: + summary_skipped = summary_skipped + 1 + f.close() + summary_data["summary"] = { + "summary_total": summary_total, + "summary_passed": summary_passed, + "summary_failed": summary_failed, + "summary_skipped": summary_skipped, + } + + return summary_data + +#Generate content for summary report json file +def get_summary_report_html(summary_data): + status = "fail" + if(summary_data["summary"]["summary_total"]==summary_data["summary"]["summary_passed"]): + status = "success" + html = "<html>" + + #<head> </head> + html = html + "<head>" + html = html + "<title>" + html = html + "Summary Report" + html = html + "</title>" + html = html + "</head>" + + #<body> </body> + html = html + "<body>" + html = html + "<h1>" + "Summary Report" + "</h1>" + html = html + "<p>" + "Status :" + status + "</p>" + html = html + "<p>" + "Total: " + str(summary_data["summary"]["summary_total"]) + html = html + " Pass: " + str(summary_data["summary"]["summary_passed"]) + html = html + " Fail: " + str(summary_data["summary"]["summary_failed"]) + html = html + " Skip: " + str(summary_data["summary"]["summary_skipped"]) + "</p>" + html = html + "<p>Details : </p>" + + #<table> </table> + html = html + "<table border=\"1\" cellspacing=\"2\" >" + html = html + "<tr bgcolor = \"2400B0\">" + html = html + "<th><font color = \"white\">test suite</font></th>" + html = html + "<th><font color = \"white\">status</font></th>" + html = html + "<th><font color = \"white\">pass</font></th>" + html = html + "<th><font color = \"white\">fail</font></th>" + html = html + "<th><font color = \"white\">skip</font></th>" + html = html + "</tr>" + + #Add content to the table + bgcolor = 0 + for test_suite in summary_data: + if test_suite == "summary": + continue + if bgcolor == 0: + html = html + "<tr bgcolor = \"CCCBE4\">" + bgcolor = 1 + else: + html = html + "<tr bgcolor = \"E8E7F2\">" + bgcolor = 0 + html = html + "<th>" + test_suite + "</th>" + html = html + "<th>" + summary_data[test_suite]["test_status"] + "</th>" + html = html + "<th>" + str(summary_data[test_suite]["passed"]) + "</th>" + html = html + "<th>" + str(summary_data[test_suite]["failed"]) + "</th>" + html = html + "<th>" + str(summary_data[test_suite]["skipped"]) + "</th>" + html = html + "</tr>" + + html = html + "</table>" + html = html + "<p></p>" + html = html + "<font>Detail log :</font>" + #TODO update the link address for agl-test-log-xxx.zip + html = html + "<a href=\"" + "address of agl-test-log-xxx.zip " + html = html + "\">agl-test-log-13.0.1-raspberrypi4-20200808.zip</a>" + html = html + "</body>" + html = html + "</html>" + + return html diff --git a/plugins/agl_test_conf.py b/plugins/agl_test_conf.py new file mode 100644 index 0000000..83f19f5 --- /dev/null +++ b/plugins/agl_test_conf.py @@ -0,0 +1,19 @@ +#The dir of test_suits +WORK_DIR = "/usr/AGL/agl-test/tests/" + +#The dir for saving log +BASE_LOGS_DIR = "/var/run/agl-test/logs/" + +#The dir for tmp log +TMP_LOGS_DIR = BASE_LOGS_DIR + "tmp-log/" + +#The dir for the file that will report +REPORT_LOGS_DIR = BASE_LOGS_DIR + "log-to-report/" + +#The dir for tmp test report +TMP_TEST_REPORT = TMP_LOGS_DIR + "test-report/" + +#Get the log file +def get_log_file(THIS_TEST): + log_file = TMP_LOGS_DIR + THIS_TEST + "/log/" + THIS_TEST + ".log" + return log_file diff --git a/plugins/agl_test_log.py b/plugins/agl_test_log.py new file mode 100644 index 0000000..f7a0721 --- /dev/null +++ b/plugins/agl_test_log.py @@ -0,0 +1,65 @@ +import pytest +import re + +''' +Process the log and init test_cases_values_and_status. + +log : the path of default log + +default log formate : + -> case_name: TEST-PASS + -> case_name: TEST-FAIL + -> case_name: TEST-SKIP +''' +def log_process_default(log): + pattern = '^ -> (.+?): (.+?)$' + parse_result = log_parse(log, pattern) + test_cases_values_and_status = [["test_id","values","status"]] + + if parse_result: + for item in parse_result: + item_result = [item[0], item[1], ""] + test_cases_values_and_status.append(item_result) + + return test_cases_values_and_status + +''' +Process the log create by gnome_desktop_testing +and init test_cases_values_and_status. + +log : the path of gnome_desktop_testing log + +gnome_desktop_testing log formate: + PASS: glib/tls-database.test + FAIL: glib/markup-escape.test + SKIP: glib/testname.test +''' +def log_process_gnome_desktop_testing(log): + pattern = '^(FAIL|PASS|SKIP.+?): (.+test?)' + parse_result = log_parse(log, pattern) + test_cases_values_and_status = [["test_id","values","status"]] + + if parse_result: + for item in parse_result: + item_result = [item[1], item[0], ""] + test_cases_values_and_status.append(item_result) + + return test_cases_values_and_status + +# parse log file with pattern +def log_parse(log, pattern): + regex = re.compile(pattern, re.MULTILINE) + + test_log = open(log, 'r') + + parse_result = [] + line = test_log.readline() + while line: + matchs = regex.search(line) + if matchs: + groups = matchs.groups() + parse_result.append(groups) + line=test_log.readline() + test_log.close() + return parse_result + diff --git a/plugins/agl_test_report.py b/plugins/agl_test_report.py new file mode 100644 index 0000000..cee1853 --- /dev/null +++ b/plugins/agl_test_report.py @@ -0,0 +1,120 @@ +import json +import shutil + +from plugins.agl_test_conf import REPORT_LOGS_DIR +from plugins.agl_test_conf import TMP_LOGS_DIR + + +#Compress the tmp log to .zip, and store the zip file under TMP_LOGS_DIR/test-report +def log_compress(THIS_TEST): + base_name = TMP_LOGS_DIR + "test-report/" + THIS_TEST + "/log" + root_dir = TMP_LOGS_DIR + THIS_TEST + "/log" + shutil.make_archive(base_name,'zip',root_dir) + + +#Get all test cases status +#The type of test_cases_values_and_status is list,it's looks like that: +#[['test_id', 'values', 'status'], ['rpm01', 'TEST-PASS', 'passed'],....] +#The type of case_status is directory,it's looks like: +#{'rpm03': 'passed', 'rpm02': 'passed', 'rpm01': 'passed'} +def get_case_status(test_cases_values_and_status): + num = len(test_cases_values_and_status) + case_status = {} + for i in range(num): + if (i==0): + continue + case_status[test_cases_values_and_status[i][0]] = test_cases_values_and_status[i][2] + return case_status + + +#Case_status is a dictionary type of data,Record the test name/id and final results of all test cases +#Get the summary of the test case status, the result is like that: +#Summary = [["collected",3],["passed",3],["failed",0],["skipped",0]] +def get_summary(case_status): + collected_num = passed_num = failed_num = skipped_num = 0 + collected_num = len(case_status) + for status in case_status.values(): + if (status == "passed"): + passed_num = passed_num + 1 + elif (status == "failed"): + failed_num = failed_num + 1 + else: + skipped_num = skipped_num + 1 + summary = [["collected",collected_num],["passed",passed_num],["failed",failed_num],["skipped",skipped_num]] + return summary + + +#Write the test result to a json file under the dir TMP_LOGS_DIR +def write_date_to_json(test_set_status,THIS_TEST,summary,case_status): + #The data that will be written into the json file + data = { + 'test_status': test_set_status, + 'test_name': THIS_TEST, + 'collected': summary[0][1], + 'passed': summary[1][1], + 'failed': summary[2][1], + 'skipped': summary[3][1], + 'case_status': case_status + } + + #Write the "data" to the json file + report_json = TMP_LOGS_DIR + THIS_TEST + "/" + "report.json" + with open(report_json,'w') as f: + json.dump(data,f,indent=4,sort_keys=False) + f.close() + +def get_report_html(THIS_TEST,test_set_status,summary,case_status): + html = "<html>" + + #<head> </head> + html = html + "<head>" + html = html + "<title>" + html = html + THIS_TEST + "test report" + html = html + "</title>" + html = html + "</head>" + + #<body> </body> + html = html + "<body>" + html = html + "<h1>" + THIS_TEST + " test report" + "</h1>" + html = html + "<p>" + "Status :" + test_set_status + "</p>" + html = html + "<p>" + "Total: " + str(summary[0][1]) + html = html + " Pass: " + str(summary[1][1]) + html = html + " Fail: " + str(summary[2][1]) + html = html + " Skip: " + str(summary[3][1]) + "</p>" + html = html + "<p>Details : </p>" + + #<table> </table> + html = html + "<table border=\"1\" cellspacing=\"2\" >" + html = html + "<tr bgcolor = \"2400B0\">" + html = html + "<th><font color = \"white\">test case</font></th>" + html = html + "<th><font color = \"white\">status</font></th>" + html = html + "</tr>" + + #Add content to the table + bgcolor = 0 + for test_case in case_status: + if bgcolor == 0: + html = html + "<tr bgcolor = \"CCCBE4\">" + bgcolor = 1 + else: + html = html + "<tr bgcolor = \"E8E7F2\">" + bgcolor = 0 + html = html + "<th>" + test_case + "</th>" + html = html + "<th>" + case_status[test_case] + "</th>" + html = html + "</tr>" + + html = html + "</table>" + html = html + "<p></p>" + html = html + "<font>Detail log :</font>" + #TODO update the link address for log.zip + html = html + "<a href=\"" + THIS_TEST + "/log.zip" + "\">log.zip</a>" + html = html + "</body>" + html = html + "</html>" + + return html + +def write_to_html_file(THIS_TEST,html): + html_path = TMP_LOGS_DIR + "test-report/" + THIS_TEST + "/report.html" + html_file = open(html_path,"w") + html_file.write(html) + html_file.close() diff --git a/plugins/agl_test_utils.py b/plugins/agl_test_utils.py new file mode 100644 index 0000000..b1204a0 --- /dev/null +++ b/plugins/agl_test_utils.py @@ -0,0 +1,31 @@ +import subprocess + +from plugins.agl_test_conf import REPORT_LOGS_DIR +from plugins.agl_test_conf import TMP_LOGS_DIR +from plugins.agl_test_conf import TMP_TEST_REPORT + + +#Check if there is the command that we needed +def find_cmd(cmd): + output = subprocess.run(['which',cmd],stdout=subprocess.PIPE) + if output.returncode==0: + return 0 + else: + print("error: {} is not found".format(cmd)) + return 1 + +#Make dir for THIS_TEST to save the log +def create_dir(THIS_TEST): + TMP_THIS_TEST_LOG = TMP_LOGS_DIR + THIS_TEST + "/log/" + TMP_TEST_REPORT_THIS = TMP_TEST_REPORT + THIS_TEST + subprocess.run(['mkdir','-p',TMP_THIS_TEST_LOG]) + subprocess.run(['mkdir','-p',TMP_TEST_REPORT_THIS]) + +# print errors +def printe(msg): + print("**** ERROR: " + msg) + +# print debug info +def printd(msg): + # TODO + print("==== DEBUG: " + msg) diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..e7d8505 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,7 @@ +[pytest] +python_files = *tests.py + +cache_dir = /var/run/agl-test/.pytest_cache + +markers = + oss_default: default test suites of OSS like rpm, etc. |