Vishal Bhoj | dc338f6 | 2020-05-18 15:38:18 +0530 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 2 | import argparse |
| 3 | import csv |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 4 | import cmd |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 5 | import copy |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 6 | import json |
| 7 | import logging |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 8 | import netrc |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 9 | import os |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 10 | import re |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 11 | import shlex |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 12 | import shutil |
Milosz Wasilewski | 970431b | 2016-11-25 14:10:08 +0000 | [diff] [blame] | 13 | import subprocess |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 14 | import sys |
Milosz Wasilewski | 259ba19 | 2017-07-27 10:59:25 +0100 | [diff] [blame] | 15 | import textwrap |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 16 | import time |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 17 | from uuid import uuid4 |
Milosz Wasilewski | 31ac985 | 2021-11-18 11:21:28 +0000 | [diff] [blame] | 18 | from datetime import datetime |
Chase Qi | ea54335 | 2017-09-21 16:44:30 +0800 | [diff] [blame] | 19 | from distutils.spawn import find_executable |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 20 | |
| 21 | |
Chase Qi | faf7d28 | 2016-08-29 19:34:01 +0800 | [diff] [blame] | 22 | try: |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 23 | from squad_client.core.api import SquadApi |
| 24 | from squad_client.shortcuts import submit_results |
| 25 | from squad_client.core.models import Squad |
| 26 | from urllib.parse import urlparse |
| 27 | except ImportError as e: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 28 | logger = logging.getLogger("RUNNER") |
| 29 | logger.warning("squad_client is needed if you want to upload to qa-reports") |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 30 | |
| 31 | |
| 32 | try: |
Chase Qi | faf7d28 | 2016-08-29 19:34:01 +0800 | [diff] [blame] | 33 | import pexpect |
| 34 | import yaml |
| 35 | except ImportError as e: |
| 36 | print(e) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 37 | print("Please run the below command to install modules required") |
| 38 | print("pip3 install -r ${REPO_PATH}/automated/utils/requirements.txt") |
Chase Qi | faf7d28 | 2016-08-29 19:34:01 +0800 | [diff] [blame] | 39 | sys.exit(1) |
| 40 | |
| 41 | |
Milosz Wasilewski | 259ba19 | 2017-07-27 10:59:25 +0100 | [diff] [blame] | 42 | class StoreDictKeyPair(argparse.Action): |
| 43 | def __init__(self, option_strings, dest, nargs=None, **kwargs): |
| 44 | self._nargs = nargs |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 45 | super(StoreDictKeyPair, self).__init__( |
| 46 | option_strings, dest, nargs=nargs, **kwargs |
| 47 | ) |
Milosz Wasilewski | 259ba19 | 2017-07-27 10:59:25 +0100 | [diff] [blame] | 48 | |
| 49 | def __call__(self, parser, namespace, values, option_string=None): |
| 50 | my_dict = {} |
| 51 | for kv in values: |
Milosz Wasilewski | f9c8c06 | 2017-08-02 16:10:40 +0100 | [diff] [blame] | 52 | if "=" in kv: |
| 53 | k, v = kv.split("=", 1) |
| 54 | my_dict[k] = v |
| 55 | else: |
| 56 | print("Invalid parameter: %s" % kv) |
Milosz Wasilewski | 259ba19 | 2017-07-27 10:59:25 +0100 | [diff] [blame] | 57 | setattr(namespace, self.dest, my_dict) |
| 58 | |
| 59 | |
Milosz Wasilewski | df71a76 | 2017-07-20 13:26:21 +0100 | [diff] [blame] | 60 | # quit gracefully if the connection is closed by remote host |
| 61 | SSH_PARAMS = "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ServerAliveInterval=5" |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 62 | |
| 63 | |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 64 | def run_command(command, target=None): |
Anders Roxell | d5c6cab | 2021-04-29 00:52:35 +0200 | [diff] [blame] | 65 | """Run a shell command. If target is specified, ssh to the given target first.""" |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 66 | |
| 67 | run = command |
| 68 | if target: |
| 69 | run = 'ssh {} {} "{}"'.format(SSH_PARAMS, target, command) |
| 70 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 71 | logger = logging.getLogger("RUNNER.run_command") |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 72 | logger.debug(run) |
Chase Qi | 3efa769 | 2017-06-26 15:54:05 +0800 | [diff] [blame] | 73 | if sys.version_info[0] < 3: |
| 74 | return subprocess.check_output(shlex.split(run)).strip() |
| 75 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 76 | return subprocess.check_output(shlex.split(run)).strip().decode("utf-8") |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 77 | |
| 78 | |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 79 | class TestPlan(object): |
| 80 | """ |
| 81 | Analysis args specified, then generate test plan. |
| 82 | """ |
| 83 | |
| 84 | def __init__(self, args): |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 85 | self.test_def = args.test_def |
| 86 | self.test_plan = args.test_plan |
| 87 | self.timeout = args.timeout |
| 88 | self.skip_install = args.skip_install |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 89 | self.logger = logging.getLogger("RUNNER.TestPlan") |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 90 | self.overlay = args.overlay |
| 91 | |
| 92 | def apply_overlay(self, test_list): |
| 93 | fixed_test_list = copy.deepcopy(test_list) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 94 | logger = logging.getLogger("RUNNER.TestPlan.Overlay") |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 95 | with open(self.overlay) as f: |
Milosz Wasilewski | d6ffade | 2022-02-21 15:14:17 +0000 | [diff] [blame] | 96 | data = yaml.load(f, Loader=yaml.SafeLoader) |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 97 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 98 | if data.get("skip"): |
| 99 | skip_tests = data["skip"] |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 100 | for test in test_list: |
| 101 | for skip_test in skip_tests: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 102 | if ( |
| 103 | test["path"] == skip_test["path"] |
| 104 | and test["repository"] == skip_test["repository"] |
| 105 | ): |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 106 | fixed_test_list.remove(test) |
| 107 | logger.info("Skipped: {}".format(test)) |
| 108 | else: |
| 109 | continue |
| 110 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 111 | if data.get("amend"): |
| 112 | amend_tests = data["amend"] |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 113 | for test in fixed_test_list: |
| 114 | for amend_test in amend_tests: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 115 | if ( |
| 116 | test["path"] == amend_test["path"] |
Milosz Wasilewski | d6ffade | 2022-02-21 15:14:17 +0000 | [diff] [blame] | 117 | and test["repository"] == amend_test["repository"] |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 118 | ): |
| 119 | if amend_test.get("parameters"): |
| 120 | if test.get("parameters"): |
| 121 | test["parameters"].update(amend_test["parameters"]) |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 122 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 123 | test["parameters"] = amend_test["parameters"] |
| 124 | logger.info("Updated: {}".format(test)) |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 125 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 126 | logger.warning( |
| 127 | "'parameters' not found in {}, nothing to amend.".format( |
| 128 | amend_test |
| 129 | ) |
| 130 | ) |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 131 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 132 | if data.get("add"): |
| 133 | add_tests = data["add"] |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 134 | unique_add_tests = [] |
| 135 | for test in add_tests: |
| 136 | if test not in unique_add_tests: |
| 137 | unique_add_tests.append(test) |
| 138 | else: |
| 139 | logger.warning("Skipping duplicate test {}".format(test)) |
| 140 | |
| 141 | for test in test_list: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 142 | del test["uuid"] |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 143 | |
| 144 | for add_test in unique_add_tests: |
| 145 | if add_test in test_list: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 146 | logger.warning( |
| 147 | "{} already included in test plan, do nothing.".format(add_test) |
| 148 | ) |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 149 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 150 | add_test["uuid"] = str(uuid4()) |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 151 | fixed_test_list.append(add_test) |
| 152 | logger.info("Added: {}".format(add_test)) |
| 153 | |
| 154 | return fixed_test_list |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 155 | |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 156 | def test_list(self, kind="automated"): |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 157 | if self.test_def: |
| 158 | if not os.path.exists(self.test_def): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 159 | self.logger.error(" %s NOT found, exiting..." % self.test_def) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 160 | sys.exit(1) |
| 161 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 162 | test_list = [{"path": self.test_def}] |
| 163 | test_list[0]["uuid"] = str(uuid4()) |
| 164 | test_list[0]["timeout"] = self.timeout |
| 165 | test_list[0]["skip_install"] = self.skip_install |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 166 | elif self.test_plan: |
| 167 | if not os.path.exists(self.test_plan): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 168 | self.logger.error(" %s NOT found, exiting..." % self.test_plan) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 169 | sys.exit(1) |
| 170 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 171 | with open(self.test_plan, "r") as f: |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 172 | test_plan = yaml.safe_load(f) |
| 173 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 174 | plan_version = test_plan["metadata"].get("format") |
| 175 | self.logger.info("Test plan version: {}".format(plan_version)) |
Milosz Wasilewski | 8d64bb2 | 2019-06-18 12:32:08 +0100 | [diff] [blame] | 176 | tests = [] |
Chase Qi | dca4fb6 | 2017-11-22 12:09:42 +0800 | [diff] [blame] | 177 | if plan_version == "Linaro Test Plan v2": |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 178 | tests = test_plan["tests"][kind] |
Chase Qi | dca4fb6 | 2017-11-22 12:09:42 +0800 | [diff] [blame] | 179 | elif plan_version == "Linaro Test Plan v1" or plan_version is None: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 180 | for requirement in test_plan["requirements"]: |
| 181 | if "tests" in requirement.keys(): |
| 182 | if ( |
| 183 | requirement["tests"] |
| 184 | and kind in requirement["tests"].keys() |
| 185 | and requirement["tests"][kind] |
| 186 | ): |
| 187 | for test in requirement["tests"][kind]: |
Chase Qi | dca4fb6 | 2017-11-22 12:09:42 +0800 | [diff] [blame] | 188 | tests.append(test) |
| 189 | |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 190 | test_list = [] |
Dan Rue | d9d7b65 | 2017-05-26 14:00:10 -0500 | [diff] [blame] | 191 | unique_tests = [] # List of test hashes |
Chase Qi | dca4fb6 | 2017-11-22 12:09:42 +0800 | [diff] [blame] | 192 | for test in tests: |
| 193 | test_hash = hash(json.dumps(test, sort_keys=True)) |
| 194 | if test_hash in unique_tests: |
| 195 | # Test is already in the test_list; don't add it again. |
| 196 | self.logger.warning("Skipping duplicate test {}".format(test)) |
| 197 | continue |
| 198 | unique_tests.append(test_hash) |
| 199 | test_list.append(test) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 200 | for test in test_list: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 201 | test["uuid"] = str(uuid4()) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 202 | except KeyError as e: |
| 203 | self.logger.error("%s is missing from test plan" % str(e)) |
| 204 | sys.exit(1) |
| 205 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 206 | self.logger.error("Plese specify a test or test plan.") |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 207 | sys.exit(1) |
| 208 | |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 209 | if self.overlay is None: |
| 210 | return test_list |
| 211 | else: |
| 212 | return self.apply_overlay(test_list) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 213 | |
| 214 | |
| 215 | class TestSetup(object): |
| 216 | """ |
| 217 | Create directories required, then copy files needed to these directories. |
| 218 | """ |
| 219 | |
| 220 | def __init__(self, test, args): |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 221 | self.test = test |
| 222 | self.args = args |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 223 | self.logger = logging.getLogger("RUNNER.TestSetup") |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 224 | self.test_kind = args.kind |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 225 | self.test_version = test.get("version", None) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 226 | |
| 227 | def validate_env(self): |
| 228 | # Inspect if environment set properly. |
| 229 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 230 | self.repo_path = os.environ["REPO_PATH"] |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 231 | except KeyError: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 232 | self.logger.error("KeyError: REPO_PATH") |
| 233 | self.logger.error( |
| 234 | "Please run '. ./bin/setenv.sh' to setup test environment" |
| 235 | ) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 236 | sys.exit(1) |
| 237 | |
| 238 | def create_dir(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 239 | if not os.path.exists(self.test["output"]): |
| 240 | os.makedirs(self.test["output"]) |
| 241 | self.logger.info("Output directory created: %s" % self.test["output"]) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 242 | |
| 243 | def copy_test_repo(self): |
| 244 | self.validate_env() |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 245 | shutil.rmtree(self.test["test_path"], ignore_errors=True) |
| 246 | if self.repo_path in self.test["test_path"]: |
| 247 | self.logger.error( |
| 248 | "Cannot copy repository into itself. Please choose output directory outside repository path" |
| 249 | ) |
Chase Qi | 33eb765 | 2016-12-02 10:43:46 +0800 | [diff] [blame] | 250 | sys.exit(1) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 251 | shutil.copytree(self.repo_path, self.test["test_path"], symlinks=True) |
| 252 | self.logger.info("Test repo copied to: %s" % self.test["test_path"]) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 253 | |
Milosz Wasilewski | 970431b | 2016-11-25 14:10:08 +0000 | [diff] [blame] | 254 | def checkout_version(self): |
| 255 | if self.test_version: |
| 256 | path = os.getcwd() |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 257 | os.chdir(self.test["test_path"]) |
Milosz Wasilewski | 970431b | 2016-11-25 14:10:08 +0000 | [diff] [blame] | 258 | subprocess.call("git checkout %s" % self.test_version, shell=True) |
| 259 | os.chdir(path) |
| 260 | |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 261 | def create_uuid_file(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 262 | with open("%s/uuid" % self.test["test_path"], "w") as f: |
| 263 | f.write(self.test["uuid"]) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 264 | |
| 265 | |
| 266 | class TestDefinition(object): |
| 267 | """ |
| 268 | Convert test definition to testdef.yaml, testdef_metadata and run.sh. |
| 269 | """ |
| 270 | |
| 271 | def __init__(self, test, args): |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 272 | self.test = test |
| 273 | self.args = args |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 274 | self.logger = logging.getLogger("RUNNER.TestDef") |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 275 | self.skip_install = args.skip_install |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 276 | self.is_manual = False |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 277 | if "skip_install" in test: |
| 278 | self.skip_install = test["skip_install"] |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 279 | self.custom_params = None |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 280 | if "parameters" in test: |
| 281 | self.custom_params = test["parameters"] |
| 282 | if "params" in test: |
| 283 | self.custom_params = test["params"] |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 284 | self.exists = False |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 285 | if os.path.isfile(self.test["path"]): |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 286 | self.exists = True |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 287 | with open(self.test["path"], "r") as f: |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 288 | self.testdef = yaml.safe_load(f) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 289 | if self.testdef["metadata"]["format"].startswith( |
| 290 | "Manual Test Definition" |
| 291 | ): |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 292 | self.is_manual = True |
Nicolas Dechesne | 51b85a8 | 2017-02-04 00:45:48 +0100 | [diff] [blame] | 293 | if self.is_manual: |
| 294 | self.runner = ManualTestRun(test, args) |
Chase Qi | 43bb912 | 2017-05-23 14:37:48 +0800 | [diff] [blame] | 295 | elif self.args.target is not None: |
Nicolas Dechesne | 51b85a8 | 2017-02-04 00:45:48 +0100 | [diff] [blame] | 296 | self.runner = RemoteTestRun(test, args) |
Chase Qi | 43bb912 | 2017-05-23 14:37:48 +0800 | [diff] [blame] | 297 | else: |
| 298 | self.runner = AutomatedTestRun(test, args) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 299 | |
| 300 | def definition(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 301 | with open("%s/testdef.yaml" % self.test["test_path"], "wb") as f: |
| 302 | f.write(yaml.dump(self.testdef, encoding="utf-8", allow_unicode=True)) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 303 | |
| 304 | def metadata(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 305 | with open("%s/testdef_metadata" % self.test["test_path"], "wb") as f: |
| 306 | f.write( |
| 307 | yaml.dump( |
| 308 | self.testdef["metadata"], encoding="utf-8", allow_unicode=True |
| 309 | ) |
| 310 | ) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 311 | |
Nicolas Dechesne | 51b85a8 | 2017-02-04 00:45:48 +0100 | [diff] [blame] | 312 | def mkrun(self): |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 313 | if not self.is_manual: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 314 | with open("%s/run.sh" % self.test["test_path"], "a") as f: |
| 315 | f.write("#!/bin/sh\n") |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 316 | |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 317 | self.parameters = self.handle_parameters() |
| 318 | if self.parameters: |
| 319 | for line in self.parameters: |
| 320 | f.write(line) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 321 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 322 | f.write("set -e\n") |
| 323 | f.write("set -x\n") |
| 324 | f.write("export TESTRUN_ID=%s\n" % self.testdef["metadata"]["name"]) |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 325 | if self.args.target is None: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 326 | f.write("cd %s\n" % (self.test["test_path"])) |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 327 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 328 | f.write("cd %s\n" % (self.test["target_test_path"])) |
| 329 | f.write("UUID=`cat uuid`\n") |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 330 | f.write('echo "<STARTRUN $TESTRUN_ID $UUID>"\n') |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 331 | f.write( |
| 332 | "export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin\n" |
| 333 | ) |
| 334 | steps = self.testdef["run"].get("steps", []) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 335 | if steps: |
Dan Rue | b592da1 | 2017-06-07 16:32:43 -0500 | [diff] [blame] | 336 | for step in steps: |
| 337 | command = step |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 338 | if "--cmd" in step or "--shell" in step: |
| 339 | command = re.sub(r"\$(\d+)\b", r"\\$\1", step) |
| 340 | f.write("%s\n" % command) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 341 | f.write('echo "<ENDRUN $TESTRUN_ID $UUID>"\n') |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 342 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 343 | os.chmod("%s/run.sh" % self.test["test_path"], 0o755) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 344 | |
Nicolas Dechesne | 51b85a8 | 2017-02-04 00:45:48 +0100 | [diff] [blame] | 345 | def run(self): |
| 346 | self.runner.run() |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 347 | |
| 348 | def handle_parameters(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 349 | ret_val = ["###default parameters from test definition###\n"] |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 350 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 351 | if "params" in self.testdef: |
| 352 | for def_param_name, def_param_value in list(self.testdef["params"].items()): |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 353 | # ?'yaml_line' |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 354 | if def_param_name == "yaml_line": |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 355 | continue |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 356 | ret_val.append("%s='%s'\n" % (def_param_name, def_param_value)) |
| 357 | elif "parameters" in self.testdef: |
| 358 | for def_param_name, def_param_value in list( |
| 359 | self.testdef["parameters"].items() |
| 360 | ): |
| 361 | if def_param_name == "yaml_line": |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 362 | continue |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 363 | ret_val.append("%s='%s'\n" % (def_param_name, def_param_value)) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 364 | else: |
| 365 | return None |
| 366 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 367 | ret_val.append("######\n") |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 368 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 369 | ret_val.append("###custom parameters from test plan###\n") |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 370 | if self.custom_params: |
| 371 | for param_name, param_value in list(self.custom_params.items()): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 372 | if param_name == "yaml_line": |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 373 | continue |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 374 | ret_val.append("%s='%s'\n" % (param_name, param_value)) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 375 | |
| 376 | if self.skip_install: |
| 377 | ret_val.append('SKIP_INSTALL="True"\n') |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 378 | ret_val.append("######\n") |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 379 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 380 | ret_val.append("###custom parameters from command line###\n") |
Milosz Wasilewski | 259ba19 | 2017-07-27 10:59:25 +0100 | [diff] [blame] | 381 | if self.args.test_def_params: |
| 382 | for param_name, param_value in self.args.test_def_params.items(): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 383 | ret_val.append("%s='%s'\n" % (param_name, param_value)) |
| 384 | ret_val.append("######\n") |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 385 | return ret_val |
| 386 | |
| 387 | |
| 388 | class TestRun(object): |
| 389 | def __init__(self, test, args): |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 390 | self.test = test |
| 391 | self.args = args |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 392 | self.logger = logging.getLogger("RUNNER.TestRun") |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 393 | self.test_timeout = self.args.timeout |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 394 | if "timeout" in test: |
| 395 | self.test_timeout = test["timeout"] |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 396 | |
| 397 | def run(self): |
| 398 | raise NotImplementedError |
| 399 | |
| 400 | def check_result(self): |
| 401 | raise NotImplementedError |
| 402 | |
| 403 | |
| 404 | class AutomatedTestRun(TestRun): |
| 405 | def run(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 406 | self.logger.info("Executing %s/run.sh" % self.test["test_path"]) |
| 407 | shell_cmd = "%s/run.sh 2>&1 | tee %s/stdout.log" % ( |
| 408 | self.test["test_path"], |
| 409 | self.test["test_path"], |
| 410 | ) |
| 411 | self.child = pexpect.spawnu("/bin/sh", ["-c", shell_cmd]) |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 412 | self.check_result() |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 413 | |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 414 | def check_result(self): |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 415 | if self.test_timeout: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 416 | self.logger.info("Test timeout: %s" % self.test_timeout) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 417 | test_end = time.time() + self.test_timeout |
| 418 | |
| 419 | while self.child.isalive(): |
| 420 | if self.test_timeout and time.time() > test_end: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 421 | self.logger.warning( |
| 422 | "%s test timed out, killing test process..." |
| 423 | % self.test["test_uuid"] |
| 424 | ) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 425 | self.child.terminate(force=True) |
| 426 | break |
| 427 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 428 | self.child.expect("\r\n") |
Milosz Wasilewski | d71f28a | 2020-06-10 20:05:13 +0100 | [diff] [blame] | 429 | print(self.child.before) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 430 | except pexpect.TIMEOUT: |
| 431 | continue |
| 432 | except pexpect.EOF: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 433 | self.logger.info("%s test finished.\n" % self.test["test_uuid"]) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 434 | break |
| 435 | |
| 436 | |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 437 | class RemoteTestRun(AutomatedTestRun): |
| 438 | def copy_to_target(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 439 | os.chdir(self.test["test_path"]) |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 440 | tarball_name = "target-test-files.tar" |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 441 | |
| 442 | self.logger.info("Archiving test files") |
| 443 | run_command( |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 444 | "tar -caf %s run.sh uuid automated/lib automated/bin automated/utils %s" |
| 445 | % (tarball_name, self.test["tc_relative_dir"]) |
| 446 | ) |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 447 | |
| 448 | self.logger.info("Creating test path") |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 449 | run_command("mkdir -p %s" % (self.test["target_test_path"]), self.args.target) |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 450 | |
| 451 | self.logger.info("Copying test archive to target host") |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 452 | run_command( |
| 453 | "scp %s ./%s %s:%s" |
| 454 | % ( |
| 455 | SSH_PARAMS, |
| 456 | tarball_name, |
| 457 | self.args.target, |
| 458 | self.test["target_test_path"], |
| 459 | ) |
| 460 | ) |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 461 | |
| 462 | self.logger.info("Unarchiving test files on target") |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 463 | run_command( |
| 464 | "cd %s && tar -xf %s" % (self.test["target_test_path"], tarball_name), |
| 465 | self.args.target, |
| 466 | ) |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 467 | |
| 468 | self.logger.info("Removing test file archive from target") |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 469 | run_command( |
| 470 | "rm %s/%s" % (self.test["target_test_path"], tarball_name), self.args.target |
| 471 | ) |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 472 | |
Milosz Wasilewski | 4cf59a8 | 2021-11-22 15:17:28 +0000 | [diff] [blame] | 473 | def cleanup_target(self): |
| 474 | self.logger.info("Removing files from target after testing") |
| 475 | run_command("rm -rf %s" % (self.test["target_test_path"]), self.args.target) |
| 476 | |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 477 | def run(self): |
| 478 | self.copy_to_target() |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 479 | self.logger.info( |
| 480 | "Executing %s/run.sh remotely on %s" |
| 481 | % (self.test["target_test_path"], self.args.target) |
| 482 | ) |
| 483 | shell_cmd = 'ssh %s %s "%s/run.sh 2>&1"' % ( |
| 484 | SSH_PARAMS, |
| 485 | self.args.target, |
| 486 | self.test["target_test_path"], |
| 487 | ) |
| 488 | self.logger.debug("shell_cmd: %s" % shell_cmd) |
| 489 | output = open("%s/stdout.log" % self.test["test_path"], "w") |
Milosz Wasilewski | d71f28a | 2020-06-10 20:05:13 +0100 | [diff] [blame] | 490 | self.child = pexpect.spawnu(shell_cmd) |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 491 | self.child.logfile = output |
| 492 | self.check_result() |
Milosz Wasilewski | 4cf59a8 | 2021-11-22 15:17:28 +0000 | [diff] [blame] | 493 | self.cleanup_target() |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 494 | |
| 495 | |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 496 | class ManualTestShell(cmd.Cmd): |
Nicolas Dechesne | 1945d3f | 2020-10-07 23:36:34 +0200 | [diff] [blame] | 497 | def __init__(self, test_dict, result_path, test_case_id): |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 498 | cmd.Cmd.__init__(self) |
| 499 | self.test_dict = test_dict |
Nicolas Dechesne | 1945d3f | 2020-10-07 23:36:34 +0200 | [diff] [blame] | 500 | self.test_case_id = test_case_id |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 501 | self.result_path = result_path |
| 502 | self.current_step_index = 0 |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 503 | self.steps = self.test_dict["run"]["steps"] |
| 504 | self.expected = self.test_dict["run"]["expected"] |
| 505 | self.prompt = "%s[%s] > " % ( |
| 506 | self.test_dict["metadata"]["name"], |
| 507 | self.test_case_id, |
| 508 | ) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 509 | self.result = None |
| 510 | self.intro = """ |
| 511 | Welcome to manual test executor. Type 'help' for available commands. |
| 512 | This shell is meant to be executed on your computer, not on the system |
| 513 | under test. Please execute the steps from the test case, compare to |
| 514 | expected result and record the test result as 'pass' or 'fail'. If there |
| 515 | is an issue that prevents from executing the step, please record the result |
| 516 | as 'skip'. |
| 517 | """ |
| 518 | |
| 519 | def do_quit(self, line): |
| 520 | """ |
| 521 | Exit test execution |
| 522 | """ |
| 523 | if self.result is not None: |
| 524 | return True |
| 525 | if line.find("-f") >= 0: |
| 526 | self._record_result("skip") |
| 527 | return True |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 528 | print( |
| 529 | "Test result not recorded. Use -f to force. Forced quit records result as 'skip'" |
| 530 | ) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 531 | |
| 532 | do_EOF = do_quit |
| 533 | |
| 534 | def do_description(self, line): |
| 535 | """ |
| 536 | Prints current test overall description |
| 537 | """ |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 538 | print(self.test_dict["metadata"]["description"]) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 539 | |
| 540 | def do_steps(self, line): |
| 541 | """ |
| 542 | Prints all steps of the current test case |
| 543 | """ |
| 544 | for index, step in enumerate(self.steps): |
Chase Qi | c69235d | 2017-05-23 14:56:47 +0800 | [diff] [blame] | 545 | print("%s. %s" % (index, step)) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 546 | |
| 547 | def do_expected(self, line): |
| 548 | """ |
| 549 | Prints all expected results of the current test case |
| 550 | """ |
| 551 | for index, expected in enumerate(self.expected): |
Chase Qi | c69235d | 2017-05-23 14:56:47 +0800 | [diff] [blame] | 552 | print("%s. %s" % (index, expected)) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 553 | |
| 554 | def do_current(self, line): |
| 555 | """ |
| 556 | Prints current test step |
| 557 | """ |
| 558 | self._print_step() |
| 559 | |
| 560 | do_start = do_current |
| 561 | |
| 562 | def do_next(self, line): |
| 563 | """ |
| 564 | Prints next test step |
| 565 | """ |
| 566 | if len(self.steps) > self.current_step_index + 1: |
| 567 | self.current_step_index += 1 |
| 568 | self._print_step() |
| 569 | |
| 570 | def _print_step(self): |
Chase Qi | c69235d | 2017-05-23 14:56:47 +0800 | [diff] [blame] | 571 | print("%s. %s" % (self.current_step_index, self.steps[self.current_step_index])) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 572 | |
| 573 | def _record_result(self, result): |
Chase Qi | c69235d | 2017-05-23 14:56:47 +0800 | [diff] [blame] | 574 | print("Recording %s in %s/stdout.log" % (result, self.result_path)) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 575 | with open("%s/stdout.log" % self.result_path, "a") as f: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 576 | f.write( |
| 577 | "<LAVA_SIGNAL_TESTCASE TEST_CASE_ID=%s RESULT=%s>" |
| 578 | % (self.test_case_id, result) |
| 579 | ) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 580 | |
| 581 | def do_pass(self, line): |
| 582 | """ |
| 583 | Records PASS as test result |
| 584 | """ |
| 585 | self.result = "pass" |
| 586 | self._record_result(self.result) |
| 587 | return True |
| 588 | |
| 589 | def do_fail(self, line): |
| 590 | """ |
| 591 | Records FAIL as test result |
| 592 | """ |
| 593 | self.result = "fail" |
| 594 | self._record_result(self.result) |
| 595 | return True |
| 596 | |
| 597 | def do_skip(self, line): |
| 598 | """ |
| 599 | Records SKIP as test result |
| 600 | """ |
| 601 | self.result = "skip" |
| 602 | self._record_result(self.result) |
| 603 | return True |
| 604 | |
| 605 | |
| 606 | class ManualTestRun(TestRun, cmd.Cmd): |
| 607 | def run(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 608 | print(self.test["test_name"]) |
| 609 | with open("%s/testdef.yaml" % self.test["test_path"], "r") as f: |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 610 | self.testdef = yaml.safe_load(f) |
| 611 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 612 | if "name" in self.test: |
| 613 | test_case_id = self.test["name"] |
Nicolas Dechesne | 1945d3f | 2020-10-07 23:36:34 +0200 | [diff] [blame] | 614 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 615 | test_case_id = self.testdef["metadata"]["name"] |
Nicolas Dechesne | 1945d3f | 2020-10-07 23:36:34 +0200 | [diff] [blame] | 616 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 617 | ManualTestShell(self.testdef, self.test["test_path"], test_case_id).cmdloop() |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 618 | |
| 619 | def check_result(self): |
| 620 | pass |
| 621 | |
| 622 | |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 623 | def get_packages(linux_distribution, target=None): |
Benjamin Copeland | e7debbe | 2021-02-22 09:31:14 +0000 | [diff] [blame] | 624 | """Return a list of installed packages with versions |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 625 | |
Benjamin Copeland | e7debbe | 2021-02-22 09:31:14 +0000 | [diff] [blame] | 626 | linux_distribution is a string that may be 'debian', |
| 627 | 'ubuntu', 'centos', or 'fedora'. |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 628 | |
Benjamin Copeland | e7debbe | 2021-02-22 09:31:14 +0000 | [diff] [blame] | 629 | For example (ubuntu): |
| 630 | 'packages': ['acl-2.2.52-2', |
| 631 | 'adduser-3.113+nmu3', |
| 632 | ... |
| 633 | 'zlib1g:amd64-1:1.2.8.dfsg-2+b1', |
| 634 | 'zlib1g-dev:amd64-1:1.2.8.dfsg-2+b1'] |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 635 | |
Benjamin Copeland | e7debbe | 2021-02-22 09:31:14 +0000 | [diff] [blame] | 636 | (centos): |
| 637 | "packages": ["acl-2.2.51-12.el7", |
| 638 | "apr-1.4.8-3.el7", |
| 639 | ... |
| 640 | "zlib-1.2.7-17.el7", |
| 641 | "zlib-devel-1.2.7-17.el7" |
| 642 | ] |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 643 | """ |
| 644 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 645 | logger = logging.getLogger("RUNNER.get_packages") |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 646 | packages = [] |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 647 | if linux_distribution in ["debian", "ubuntu"]: |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 648 | # Debian (apt) based system |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 649 | packages = run_command( |
| 650 | "dpkg-query -W -f '${package}-${version}\n'", target |
| 651 | ).splitlines() |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 652 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 653 | elif linux_distribution in ["centos", "fedora"]: |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 654 | # RedHat (rpm) based system |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 655 | packages = run_command( |
| 656 | "rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}\n'", target |
| 657 | ).splitlines() |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 658 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 659 | logger.warning( |
| 660 | "Unknown linux distribution '{}'; package list not populated.".format( |
| 661 | linux_distribution |
| 662 | ) |
| 663 | ) |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 664 | |
| 665 | packages.sort() |
| 666 | return packages |
| 667 | |
| 668 | |
| 669 | def get_environment(target=None, skip_collection=False): |
Benjamin Copeland | e7debbe | 2021-02-22 09:31:14 +0000 | [diff] [blame] | 670 | """Return a dictionary with environmental information |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 671 | |
Benjamin Copeland | e7debbe | 2021-02-22 09:31:14 +0000 | [diff] [blame] | 672 | target: optional ssh host string to gather environment remotely. |
| 673 | skip_collection: Skip data collection and return an empty dictionary. |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 674 | |
Benjamin Copeland | e7debbe | 2021-02-22 09:31:14 +0000 | [diff] [blame] | 675 | For example (on a HiSilicon D03): |
| 676 | { |
| 677 | "bios_version": "Hisilicon D03 UEFI 16.12 Release", |
| 678 | "board_name": "D03", |
| 679 | "board_vendor": "Huawei", |
| 680 | "kernel": "4.9.0-20.gitedc2a1c.linaro.aarch64", |
| 681 | "linux_distribution": "centos", |
Mikko Rapeli | 82ebc05 | 2022-10-21 13:47:51 +0100 | [diff] [blame] | 682 | "linux_distribution_version": "6.2", |
Benjamin Copeland | e7debbe | 2021-02-22 09:31:14 +0000 | [diff] [blame] | 683 | "packages": [ |
| 684 | "GeoIP-1.5.0-11.el7", |
| 685 | "NetworkManager-1.4.0-20.el7_3", |
| 686 | ... |
| 687 | "yum-plugin-fastestmirror-1.1.31-40.el7", |
| 688 | "zlib-1.2.7-17.el7" |
| 689 | ], |
| 690 | "uname": "Linux localhost.localdomain 4.9.0-20.gitedc2a1c.linaro.aarch64 #1 SMP Wed Dec 14 17:50:15 UTC 2016 aarch64 aarch64 aarch64 GNU/Linux" |
| 691 | } |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 692 | """ |
| 693 | |
| 694 | environment = {} |
| 695 | if skip_collection: |
| 696 | return environment |
Milosz Wasilewski | df71a76 | 2017-07-20 13:26:21 +0100 | [diff] [blame] | 697 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 698 | environment["linux_distribution"] = ( |
| 699 | run_command("grep ^ID= /etc/os-release", target) |
| 700 | .split("=")[-1] |
| 701 | .strip('"') |
| 702 | .lower() |
| 703 | ) |
Milosz Wasilewski | df71a76 | 2017-07-20 13:26:21 +0100 | [diff] [blame] | 704 | except subprocess.CalledProcessError: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 705 | environment["linux_distribution"] = "" |
Milosz Wasilewski | df71a76 | 2017-07-20 13:26:21 +0100 | [diff] [blame] | 706 | |
| 707 | try: |
Mikko Rapeli | 82ebc05 | 2022-10-21 13:47:51 +0100 | [diff] [blame] | 708 | environment["linux_distribution_version"] = ( |
| 709 | run_command("grep ^VERSION= /etc/os-release", target) |
| 710 | .split("=")[-1] |
| 711 | .strip('"') |
| 712 | ) |
| 713 | except subprocess.CalledProcessError: |
| 714 | environment["linux_distribution_version"] = "" |
| 715 | |
| 716 | try: |
| 717 | environment["linux_distribution_version_id"] = ( |
| 718 | run_command("grep ^VERSION_ID= /etc/os-release", target) |
| 719 | .split("=")[-1] |
| 720 | .strip('"') |
| 721 | ) |
| 722 | except subprocess.CalledProcessError: |
| 723 | environment["linux_distribution_version_id"] = "" |
| 724 | |
| 725 | try: |
| 726 | environment["linux_distribution_version_codename"] = ( |
| 727 | run_command("grep ^VERSION_CODENAME= /etc/os-release", target) |
| 728 | .split("=")[-1] |
| 729 | .strip('"') |
| 730 | ) |
| 731 | except subprocess.CalledProcessError: |
| 732 | environment["linux_distribution_version_codename"] = "" |
| 733 | |
| 734 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 735 | environment["kernel"] = run_command("uname -r", target) |
Milosz Wasilewski | df71a76 | 2017-07-20 13:26:21 +0100 | [diff] [blame] | 736 | except subprocess.CalledProcessError: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 737 | environment["kernel"] = "" |
Milosz Wasilewski | df71a76 | 2017-07-20 13:26:21 +0100 | [diff] [blame] | 738 | |
| 739 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 740 | environment["uname"] = run_command("uname -a", target) |
Milosz Wasilewski | df71a76 | 2017-07-20 13:26:21 +0100 | [diff] [blame] | 741 | except subprocess.CalledProcessError: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 742 | environment["uname"] = "" |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 743 | |
| 744 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 745 | environment["bios_version"] = run_command( |
| 746 | "cat /sys/devices/virtual/dmi/id/bios_version", target |
| 747 | ) |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 748 | except subprocess.CalledProcessError: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 749 | environment["bios_version"] = "" |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 750 | |
| 751 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 752 | environment["board_vendor"] = run_command( |
| 753 | "cat /sys/devices/virtual/dmi/id/board_vendor", target |
| 754 | ) |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 755 | except subprocess.CalledProcessError: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 756 | environment["board_vendor"] = "" |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 757 | |
| 758 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 759 | environment["board_name"] = run_command( |
| 760 | "cat /sys/devices/virtual/dmi/id/board_name", target |
| 761 | ) |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 762 | except subprocess.CalledProcessError: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 763 | environment["board_name"] = "" |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 764 | |
Milosz Wasilewski | df71a76 | 2017-07-20 13:26:21 +0100 | [diff] [blame] | 765 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 766 | environment["packages"] = get_packages( |
| 767 | environment["linux_distribution"], target |
| 768 | ) |
Milosz Wasilewski | df71a76 | 2017-07-20 13:26:21 +0100 | [diff] [blame] | 769 | except subprocess.CalledProcessError: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 770 | environment["packages"] = [] |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 771 | return environment |
| 772 | |
| 773 | |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 774 | class ResultParser(object): |
| 775 | def __init__(self, test, args): |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 776 | self.test = test |
| 777 | self.args = args |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 778 | self.metrics = [] |
| 779 | self.results = {} |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 780 | self.results["test"] = test["test_name"] |
| 781 | self.results["id"] = test["test_uuid"] |
| 782 | self.results["test_plan"] = args.test_plan |
| 783 | self.results["environment"] = get_environment( |
| 784 | target=self.args.target, skip_collection=self.args.skip_environment |
| 785 | ) |
| 786 | self.logger = logging.getLogger("RUNNER.ResultParser") |
| 787 | self.results["params"] = {} |
Chase Qi | e94ba52 | 2017-05-26 12:05:18 +0800 | [diff] [blame] | 788 | self.pattern = None |
| 789 | self.fixup = None |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 790 | self.qa_reports_server = args.qa_reports_server |
| 791 | if args.qa_reports_token is not None: |
| 792 | self.qa_reports_token = args.qa_reports_token |
| 793 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 794 | self.qa_reports_token = os.environ.get( |
| 795 | "QA_REPORTS_TOKEN", get_token_from_netrc(self.qa_reports_server) |
| 796 | ) |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 797 | self.qa_reports_project = args.qa_reports_project |
| 798 | self.qa_reports_group = args.qa_reports_group |
| 799 | self.qa_reports_env = args.qa_reports_env |
| 800 | self.qa_reports_build_version = args.qa_reports_build_version |
Milosz Wasilewski | b4b40de | 2020-06-11 12:54:18 +0100 | [diff] [blame] | 801 | self.qa_reports_disable_metadata = args.qa_reports_disable_metadata |
Milosz Wasilewski | 37848d3 | 2020-06-11 14:33:43 +0100 | [diff] [blame] | 802 | self.qa_reports_metadata = args.qa_reports_metadata |
Milosz Wasilewski | abe2dcc | 2020-06-12 10:54:46 +0100 | [diff] [blame] | 803 | self.qa_reports_metadata_file = args.qa_reports_metadata_file |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 804 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 805 | with open(os.path.join(self.test["test_path"], "testdef.yaml"), "r") as f: |
Milosz Wasilewski | a76e8dd | 2016-11-25 14:13:25 +0000 | [diff] [blame] | 806 | self.testdef = yaml.safe_load(f) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 807 | self.results["name"] = "" |
| 808 | if ( |
| 809 | "metadata" in self.testdef.keys() |
| 810 | and "name" in self.testdef["metadata"].keys() |
| 811 | ): |
| 812 | self.results["name"] = self.testdef["metadata"]["name"] |
| 813 | if "params" in self.testdef.keys(): |
| 814 | self.results["params"] = self.testdef["params"] |
Nicolas Dechesne | faa343d | 2017-10-23 00:33:10 +0200 | [diff] [blame] | 815 | if self.args.test_def_params: |
| 816 | for param_name, param_value in self.args.test_def_params.items(): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 817 | self.results["params"][param_name] = param_value |
| 818 | if ( |
| 819 | "parse" in self.testdef.keys() |
| 820 | and "pattern" in self.testdef["parse"].keys() |
| 821 | ): |
| 822 | self.pattern = self.testdef["parse"]["pattern"] |
Chase Qi | e94ba52 | 2017-05-26 12:05:18 +0800 | [diff] [blame] | 823 | self.logger.info("Enabling log parse pattern: %s" % self.pattern) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 824 | if "fixupdict" in self.testdef["parse"].keys(): |
| 825 | self.fixup = self.testdef["parse"]["fixupdict"] |
| 826 | self.logger.info( |
| 827 | "Enabling log parse pattern fixup: %s" % self.fixup |
| 828 | ) |
| 829 | if "parameters" in test.keys(): |
| 830 | self.results["params"].update(test["parameters"]) |
| 831 | if "params" in test.keys(): |
| 832 | self.results["params"].update(test["params"]) |
| 833 | if "version" in test.keys(): |
| 834 | self.results["version"] = test["version"] |
Milosz Wasilewski | 970431b | 2016-11-25 14:10:08 +0000 | [diff] [blame] | 835 | else: |
| 836 | path = os.getcwd() |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 837 | os.chdir(self.test["test_path"]) |
Chase Qi | 3efa769 | 2017-06-26 15:54:05 +0800 | [diff] [blame] | 838 | if sys.version_info[0] < 3: |
| 839 | test_version = subprocess.check_output("git rev-parse HEAD", shell=True) |
| 840 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 841 | test_version = subprocess.check_output( |
| 842 | "git rev-parse HEAD", shell=True |
| 843 | ).decode("utf-8") |
| 844 | self.results["version"] = test_version.rstrip() |
Milosz Wasilewski | 970431b | 2016-11-25 14:10:08 +0000 | [diff] [blame] | 845 | os.chdir(path) |
Chase Qi | ea54335 | 2017-09-21 16:44:30 +0800 | [diff] [blame] | 846 | self.lava_run = args.lava_run |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 847 | if self.lava_run and not find_executable("lava-test-case"): |
| 848 | self.logger.info( |
| 849 | "lava-test-case not found, '-l' or '--lava_run' option ignored'" |
| 850 | ) |
Chase Qi | ea54335 | 2017-09-21 16:44:30 +0800 | [diff] [blame] | 851 | self.lava_run = False |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 852 | |
| 853 | def run(self): |
| 854 | self.parse_stdout() |
Chase Qi | e94ba52 | 2017-05-26 12:05:18 +0800 | [diff] [blame] | 855 | if self.pattern: |
| 856 | self.parse_pattern() |
| 857 | # If 'metrics' is empty, add 'no-result-found fail'. |
| 858 | if not self.metrics: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 859 | self.metrics = [ |
| 860 | { |
| 861 | "test_case_id": "no-result-found", |
| 862 | "result": "fail", |
| 863 | "measurement": "", |
| 864 | "units": "", |
| 865 | } |
| 866 | ] |
| 867 | self.results["metrics"] = self.metrics |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 868 | self.dict_to_json() |
| 869 | self.dict_to_csv() |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 870 | self.send_to_qa_reports() |
Milosz Wasilewski | 31ac985 | 2021-11-18 11:21:28 +0000 | [diff] [blame] | 871 | self.send_to_fiotest() |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 872 | self.logger.info("Result files saved to: %s" % self.test["test_path"]) |
| 873 | print("--- Printing result.csv ---") |
| 874 | with open("%s/result.csv" % self.test["test_path"]) as f: |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 875 | print(f.read()) |
| 876 | |
| 877 | def parse_stdout(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 878 | with open("%s/stdout.log" % self.test["test_path"], "r") as f: |
Milosz Wasilewski | 5a4dd44 | 2016-12-07 15:01:57 +0000 | [diff] [blame] | 879 | test_case_re = re.compile("TEST_CASE_ID=(.*)") |
| 880 | result_re = re.compile("RESULT=(.*)") |
| 881 | measurement_re = re.compile("MEASUREMENT=(.*)") |
| 882 | units_re = re.compile("UNITS=(.*)") |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 883 | for line in f: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 884 | if re.match(r"\<(|LAVA_SIGNAL_TESTCASE )TEST_CASE_ID=.*", line): |
| 885 | line = line.strip("\n").strip("\r").strip("<>").split(" ") |
| 886 | data = { |
| 887 | "test_case_id": "", |
| 888 | "result": "", |
| 889 | "measurement": "", |
| 890 | "units": "", |
| 891 | } |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 892 | |
| 893 | for string in line: |
Milosz Wasilewski | ff69562 | 2016-12-05 16:00:06 +0000 | [diff] [blame] | 894 | test_case_match = test_case_re.match(string) |
| 895 | result_match = result_re.match(string) |
| 896 | measurement_match = measurement_re.match(string) |
| 897 | units_match = units_re.match(string) |
| 898 | if test_case_match: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 899 | data["test_case_id"] = test_case_match.group(1) |
Milosz Wasilewski | ff69562 | 2016-12-05 16:00:06 +0000 | [diff] [blame] | 900 | if result_match: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 901 | data["result"] = result_match.group(1) |
Milosz Wasilewski | ff69562 | 2016-12-05 16:00:06 +0000 | [diff] [blame] | 902 | if measurement_match: |
Nicolas Dechesne | 508a227 | 2020-10-03 11:10:37 +0200 | [diff] [blame] | 903 | try: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 904 | data["measurement"] = float(measurement_match.group(1)) |
Nicolas Dechesne | 508a227 | 2020-10-03 11:10:37 +0200 | [diff] [blame] | 905 | except ValueError as e: |
| 906 | pass |
Milosz Wasilewski | ff69562 | 2016-12-05 16:00:06 +0000 | [diff] [blame] | 907 | if units_match: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 908 | data["units"] = units_match.group(1) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 909 | |
| 910 | self.metrics.append(data.copy()) |
| 911 | |
Chase Qi | ea54335 | 2017-09-21 16:44:30 +0800 | [diff] [blame] | 912 | if self.lava_run: |
| 913 | self.send_to_lava(data) |
| 914 | |
Chase Qi | e94ba52 | 2017-05-26 12:05:18 +0800 | [diff] [blame] | 915 | def parse_pattern(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 916 | with open("%s/stdout.log" % self.test["test_path"], "r") as f: |
| 917 | rex_pattern = re.compile(r"%s" % self.pattern) |
Chase Qi | e94ba52 | 2017-05-26 12:05:18 +0800 | [diff] [blame] | 918 | for line in f: |
| 919 | data = {} |
AnÃbal Limón | c36cb79 | 2017-11-29 11:54:30 -0600 | [diff] [blame] | 920 | m = rex_pattern.search(line) |
Chase Qi | e94ba52 | 2017-05-26 12:05:18 +0800 | [diff] [blame] | 921 | if m: |
| 922 | data = m.groupdict() |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 923 | for x in ["measurement", "units"]: |
Chase Qi | e94ba52 | 2017-05-26 12:05:18 +0800 | [diff] [blame] | 924 | if x not in data: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 925 | data[x] = "" |
| 926 | if self.fixup and data["result"] in self.fixup: |
| 927 | data["result"] = self.fixup[data["result"]] |
Chase Qi | 1f2a9a0 | 2017-03-09 15:45:04 +0100 | [diff] [blame] | 928 | |
Chase Qi | e94ba52 | 2017-05-26 12:05:18 +0800 | [diff] [blame] | 929 | self.metrics.append(data.copy()) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 930 | |
Chase Qi | ea54335 | 2017-09-21 16:44:30 +0800 | [diff] [blame] | 931 | if self.lava_run: |
| 932 | self.send_to_lava(data) |
| 933 | |
| 934 | def send_to_lava(self, data): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 935 | cmd = "lava-test-case {} --result {}".format( |
| 936 | data["test_case_id"], data["result"] |
| 937 | ) |
| 938 | if data["measurement"]: |
| 939 | cmd = "{} --measurement {} --units {}".format( |
| 940 | cmd, data["measurement"], data["units"] |
| 941 | ) |
| 942 | self.logger.debug("lava-run: cmd: {}".format(cmd)) |
Chase Qi | ea54335 | 2017-09-21 16:44:30 +0800 | [diff] [blame] | 943 | subprocess.call(shlex.split(cmd)) |
| 944 | |
Milosz Wasilewski | 31ac985 | 2021-11-18 11:21:28 +0000 | [diff] [blame] | 945 | def send_to_fiotest(self): |
| 946 | """ |
| 947 | This method saves results as filesystem tree. This is required by |
| 948 | fiotest: https://github.com/foundriesio/fiotest/ |
| 949 | """ |
| 950 | # check if TEST_DIR variable is set |
| 951 | test_path = os.environ.get("TEST_DIR") |
| 952 | if not test_path: |
Milosz Wasilewski | d92aff1 | 2021-11-18 11:21:28 +0000 | [diff] [blame] | 953 | self.logger.debug("TEST_DIR is not set") |
| 954 | self.logger.debug("NOT reporting result to fiotest") |
Milosz Wasilewski | 31ac985 | 2021-11-18 11:21:28 +0000 | [diff] [blame] | 955 | return |
| 956 | # create directory with test name |
| 957 | try: |
| 958 | for metric in self.metrics: |
| 959 | local_ts = datetime.now() |
| 960 | dir_name = "{}-{}".format(local_ts.timestamp(), metric["test_case_id"]) |
| 961 | os.makedirs(os.path.join(test_path, dir_name), exist_ok=True) |
| 962 | if metric["measurement"] != "": |
| 963 | metrics_dir = os.path.join(test_path, dir_name, "metrics") |
| 964 | os.makedirs(metrics_dir, exist_ok=True) |
| 965 | with open(os.path.join(metrics_dir, "value"), "w") as value_file: |
| 966 | value_file.write(metric["measurement"]) |
| 967 | else: |
| 968 | if metric["result"] == "fail": |
| 969 | os.makedirs( |
| 970 | os.path.join(test_path, dir_name, "failed"), exist_ok=True |
| 971 | ) |
| 972 | if metric["result"] == "skip": |
| 973 | os.makedirs( |
| 974 | os.path.join(test_path, dir_name, "skipped"), exist_ok=True |
| 975 | ) |
| 976 | except PermissionError: |
| 977 | self.logger.error( |
| 978 | "Unable to prepare fiotest results due to lack of permissions" |
| 979 | ) |
| 980 | |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 981 | def send_to_qa_reports(self): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 982 | if None in ( |
| 983 | self.qa_reports_server, |
| 984 | self.qa_reports_token, |
| 985 | self.qa_reports_group, |
| 986 | self.qa_reports_project, |
| 987 | self.qa_reports_build_version, |
| 988 | self.qa_reports_env, |
| 989 | ): |
| 990 | self.logger.warning( |
| 991 | "All parameters for qa reports are not set, results will not be pushed to qa reports" |
| 992 | ) |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 993 | return |
| 994 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 995 | SquadApi.configure(url=self.qa_reports_server, token=self.qa_reports_token) |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 996 | tests = {} |
| 997 | metrics = {} |
| 998 | for metric in self.metrics: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 999 | if metric["measurement"] != "": |
| 1000 | metrics[ |
| 1001 | "{}/{}".format(self.test["test_name"], metric["test_case_id"]) |
| 1002 | ] = metric["measurement"] |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 1003 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1004 | tests[ |
| 1005 | "{}/{}".format(self.test["test_name"], metric["test_case_id"]) |
| 1006 | ] = metric["result"] |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 1007 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1008 | with open("{}/stdout.log".format(self.test["test_path"]), "r") as logfile: |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 1009 | log = logfile.read() |
| 1010 | |
Milosz Wasilewski | abe2dcc | 2020-06-12 10:54:46 +0100 | [diff] [blame] | 1011 | metadata = {} |
Milosz Wasilewski | b00ceff | 2020-06-15 13:48:01 +0100 | [diff] [blame] | 1012 | if not self.qa_reports_disable_metadata: |
| 1013 | if self.qa_reports_metadata: |
| 1014 | metadata.update(self.qa_reports_metadata) |
| 1015 | if self.qa_reports_metadata_file: |
| 1016 | try: |
| 1017 | with open(self.qa_reports_metadata_file, "r") as metadata_file: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1018 | loaded_metadata = yaml.load( |
| 1019 | metadata_file, Loader=yaml.SafeLoader |
| 1020 | ) |
Milosz Wasilewski | b00ceff | 2020-06-15 13:48:01 +0100 | [diff] [blame] | 1021 | # check if loaded metadata is key=value and both are strings |
| 1022 | for key, value in loaded_metadata.items(): |
| 1023 | if type(key) == str and type(value) == str: |
| 1024 | # only update metadata with simple keys |
| 1025 | # ignore all other items in the dictionary |
| 1026 | metadata.update({key: value}) |
| 1027 | else: |
| 1028 | self.logger.warning("Ignoring key: %s" % key) |
| 1029 | except FileNotFoundError: |
| 1030 | self.logger.warning("Metadata file not found") |
| 1031 | except PermissionError: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1032 | self.logger.warning( |
| 1033 | "Insufficient permissions to open metadata file" |
| 1034 | ) |
Milosz Wasilewski | f883f10 | 2020-06-11 12:46:58 +0100 | [diff] [blame] | 1035 | if submit_results( |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1036 | group_project_slug="{}/{}".format( |
| 1037 | self.qa_reports_group, self.qa_reports_project |
| 1038 | ), |
| 1039 | build_version=self.qa_reports_build_version, |
| 1040 | env_slug=self.qa_reports_env, |
| 1041 | tests=tests, |
| 1042 | metrics=metrics, |
| 1043 | log=log, |
| 1044 | metadata=metadata, |
| 1045 | attachments=None, |
| 1046 | ): |
Milosz Wasilewski | f883f10 | 2020-06-11 12:46:58 +0100 | [diff] [blame] | 1047 | self.logger.info("Results pushed to QA Reports") |
| 1048 | else: |
| 1049 | self.logger.warning("Results upload to QA Reports failed!") |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 1050 | |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1051 | def dict_to_json(self): |
Chase Qi | 87f4f40 | 2016-11-07 15:32:01 +0800 | [diff] [blame] | 1052 | # Save test results to output/test_id/result.json |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1053 | with open("%s/result.json" % self.test["test_path"], "w") as f: |
Chase Qi | 87f4f40 | 2016-11-07 15:32:01 +0800 | [diff] [blame] | 1054 | json.dump([self.results], f, indent=4) |
| 1055 | |
Milosz Wasilewski | ba21eeb | 2022-02-15 10:23:11 +0000 | [diff] [blame] | 1056 | if not self.args.cleanup: |
| 1057 | # Collect test results of all tests in output/result.json |
| 1058 | feeds = [] |
| 1059 | if os.path.isfile("%s/result.json" % self.test["output"]): |
| 1060 | with open("%s/result.json" % self.test["output"], "r") as f: |
| 1061 | feeds = json.load(f) |
Chase Qi | 87f4f40 | 2016-11-07 15:32:01 +0800 | [diff] [blame] | 1062 | |
Milosz Wasilewski | ba21eeb | 2022-02-15 10:23:11 +0000 | [diff] [blame] | 1063 | feeds.append(self.results) |
| 1064 | with open("%s/result.json" % self.test["output"], "w") as f: |
| 1065 | json.dump(feeds, f, indent=4) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1066 | |
| 1067 | def dict_to_csv(self): |
Chase Qi | ca15cf5 | 2016-11-10 17:00:22 +0800 | [diff] [blame] | 1068 | # Convert dict self.results['params'] to a string. |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1069 | test_params = "" |
| 1070 | if self.results["params"]: |
| 1071 | params_dict = self.results["params"] |
| 1072 | test_params = ";".join(["%s=%s" % (k, v) for k, v in params_dict.items()]) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1073 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1074 | for metric in self.results["metrics"]: |
| 1075 | metric["name"] = self.results["name"] |
| 1076 | metric["test_params"] = test_params |
Chase Qi | ca15cf5 | 2016-11-10 17:00:22 +0800 | [diff] [blame] | 1077 | |
| 1078 | # Save test results to output/test_id/result.csv |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1079 | fieldnames = [ |
| 1080 | "name", |
| 1081 | "test_case_id", |
| 1082 | "result", |
| 1083 | "measurement", |
| 1084 | "units", |
| 1085 | "test_params", |
| 1086 | ] |
| 1087 | with open("%s/result.csv" % self.test["test_path"], "w") as f: |
Chase Qi | ca15cf5 | 2016-11-10 17:00:22 +0800 | [diff] [blame] | 1088 | writer = csv.DictWriter(f, fieldnames=fieldnames) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1089 | writer.writeheader() |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1090 | for metric in self.results["metrics"]: |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1091 | writer.writerow(metric) |
| 1092 | |
Milosz Wasilewski | ba21eeb | 2022-02-15 10:23:11 +0000 | [diff] [blame] | 1093 | if not self.args.cleanup: |
| 1094 | # Collect test results of all tests in output/result.csv |
| 1095 | if not os.path.isfile("%s/result.csv" % self.test["output"]): |
| 1096 | with open("%s/result.csv" % self.test["output"], "w") as f: |
| 1097 | writer = csv.DictWriter(f, fieldnames=fieldnames) |
| 1098 | writer.writeheader() |
Chase Qi | 87f4f40 | 2016-11-07 15:32:01 +0800 | [diff] [blame] | 1099 | |
Milosz Wasilewski | ba21eeb | 2022-02-15 10:23:11 +0000 | [diff] [blame] | 1100 | with open("%s/result.csv" % self.test["output"], "a") as f: |
| 1101 | writer = csv.DictWriter(f, fieldnames=fieldnames) |
| 1102 | for metric in self.results["metrics"]: |
| 1103 | writer.writerow(metric) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1104 | |
| 1105 | |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 1106 | def get_token_from_netrc(qa_reports_server): |
| 1107 | if qa_reports_server is None: |
| 1108 | return |
| 1109 | parse = urlparse(qa_reports_server) |
| 1110 | netrc_local = netrc.netrc() |
| 1111 | authTokens = netrc_local.authenticators("{}".format(parse.netloc)) |
| 1112 | if authTokens is not None: |
| 1113 | hostname, username, authToken = authTokens |
| 1114 | return authToken |
| 1115 | # Unable to find Token hence returning None |
| 1116 | return |
| 1117 | |
| 1118 | |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1119 | def get_args(): |
Milosz Wasilewski | 259ba19 | 2017-07-27 10:59:25 +0100 | [diff] [blame] | 1120 | parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1121 | parser.add_argument( |
| 1122 | "-o", |
| 1123 | "--output", |
| 1124 | default=os.getenv("HOME", "") + "/output", |
| 1125 | dest="output", |
| 1126 | help=textwrap.dedent( |
| 1127 | """\ |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1128 | specify a directory to store test and result files. |
Nicolas Dechesne | f6c4c21 | 2017-01-18 17:30:04 +0100 | [diff] [blame] | 1129 | Default: $HOME/output |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1130 | """ |
| 1131 | ), |
| 1132 | ) |
| 1133 | parser.add_argument( |
| 1134 | "-p", |
| 1135 | "--test_plan", |
| 1136 | default=None, |
| 1137 | dest="test_plan", |
| 1138 | help=textwrap.dedent( |
| 1139 | """\ |
Thomas Böhler | 4b0ab50 | 2024-06-12 15:44:30 +0200 | [diff] [blame] | 1140 | specify a test plan file which has tests and related |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1141 | params listed in yaml format. |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1142 | """ |
| 1143 | ), |
| 1144 | ) |
| 1145 | parser.add_argument( |
| 1146 | "-d", |
| 1147 | "--test_def", |
| 1148 | default=None, |
| 1149 | dest="test_def", |
| 1150 | help=textwrap.dedent( |
| 1151 | """\ |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1152 | base on test definition repo location, specify relative |
| 1153 | path to the test definition to run. |
| 1154 | Format example: "ubuntu/smoke-tests-basic.yaml" |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1155 | """ |
| 1156 | ), |
| 1157 | ) |
| 1158 | parser.add_argument( |
| 1159 | "-r", |
| 1160 | "--test_def_params", |
| 1161 | default={}, |
| 1162 | dest="test_def_params", |
| 1163 | action=StoreDictKeyPair, |
| 1164 | nargs="+", |
| 1165 | metavar="KEY=VALUE", |
| 1166 | help=textwrap.dedent( |
| 1167 | """\ |
Milosz Wasilewski | 259ba19 | 2017-07-27 10:59:25 +0100 | [diff] [blame] | 1168 | Set additional parameters when using test definition without |
| 1169 | a test plan. The name values are set similarily to environment |
| 1170 | variables: |
| 1171 | --test_def_params KEY1=VALUE1 KEY2=VALUE2 ... |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1172 | """ |
| 1173 | ), |
| 1174 | ) |
| 1175 | parser.add_argument( |
| 1176 | "-k", |
| 1177 | "--kind", |
| 1178 | default="automated", |
| 1179 | dest="kind", |
| 1180 | choices=["automated", "manual"], |
| 1181 | help=textwrap.dedent( |
| 1182 | """\ |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 1183 | Selects type of tests to be executed from the test plan. |
| 1184 | Possible options: automated, manual |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1185 | """ |
| 1186 | ), |
| 1187 | ) |
| 1188 | parser.add_argument( |
| 1189 | "-t", |
| 1190 | "--timeout", |
| 1191 | type=int, |
| 1192 | default=None, |
| 1193 | dest="timeout", |
| 1194 | help="Specify test timeout", |
| 1195 | ) |
| 1196 | parser.add_argument( |
| 1197 | "-g", |
| 1198 | "--target", |
| 1199 | default=None, |
| 1200 | dest="target", |
| 1201 | help=textwrap.dedent( |
| 1202 | """\ |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1203 | Specify SSH target to execute tests. |
| 1204 | Format: user@host |
| 1205 | Note: ssh authentication must be paswordless |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1206 | """ |
| 1207 | ), |
| 1208 | ) |
| 1209 | parser.add_argument( |
| 1210 | "-s", |
| 1211 | "--skip_install", |
| 1212 | dest="skip_install", |
| 1213 | default=False, |
| 1214 | action="store_true", |
| 1215 | help="skip install section defined in test definition.", |
| 1216 | ) |
| 1217 | parser.add_argument( |
| 1218 | "-e", |
| 1219 | "--skip_environment", |
| 1220 | dest="skip_environment", |
| 1221 | default=False, |
| 1222 | action="store_true", |
| 1223 | help="skip environmental data collection (board name, distro, etc)", |
| 1224 | ) |
| 1225 | parser.add_argument( |
| 1226 | "-l", |
| 1227 | "--lava_run", |
| 1228 | dest="lava_run", |
| 1229 | default=False, |
| 1230 | action="store_true", |
| 1231 | help="send test result to LAVA with lava-test-case.", |
| 1232 | ) |
| 1233 | parser.add_argument( |
| 1234 | "-O", |
| 1235 | "--overlay", |
| 1236 | default=None, |
| 1237 | dest="overlay", |
| 1238 | help=textwrap.dedent( |
| 1239 | """\ |
Thomas Böhler | 4b0ab50 | 2024-06-12 15:44:30 +0200 | [diff] [blame] | 1240 | Specify test plan overlay file to: |
Chase Qi | a158efe | 2017-11-17 12:35:11 +0800 | [diff] [blame] | 1241 | * skip tests |
| 1242 | * amend test parameters |
| 1243 | * add new tests |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1244 | """ |
| 1245 | ), |
| 1246 | ) |
| 1247 | parser.add_argument( |
| 1248 | "-v", |
| 1249 | "--verbose", |
| 1250 | action="store_true", |
| 1251 | dest="verbose", |
| 1252 | default=False, |
| 1253 | help="Set log level to DEBUG.", |
| 1254 | ) |
Vishal Bhoj | e94da4a | 2020-05-15 12:22:40 +0530 | [diff] [blame] | 1255 | parser.add_argument( |
| 1256 | "--qa-reports-server", |
| 1257 | dest="qa_reports_server", |
| 1258 | default=None, |
| 1259 | help="qa reports server where the results have to be sent", |
| 1260 | ) |
| 1261 | parser.add_argument( |
| 1262 | "--qa-reports-token", |
| 1263 | dest="qa_reports_token", |
| 1264 | default=None, |
| 1265 | help="qa reports token to upload the results to qa_reports_server", |
| 1266 | ) |
| 1267 | parser.add_argument( |
| 1268 | "--qa-reports-project", |
| 1269 | dest="qa_reports_project", |
| 1270 | default=None, |
| 1271 | help="qa reports projects to which the results have to be uploaded", |
| 1272 | ) |
| 1273 | parser.add_argument( |
| 1274 | "--qa-reports-group", |
| 1275 | dest="qa_reports_group", |
| 1276 | default=None, |
| 1277 | help="qa reports group in which the results have to be stored", |
| 1278 | ) |
| 1279 | parser.add_argument( |
| 1280 | "--qa-reports-env", |
| 1281 | dest="qa_reports_env", |
| 1282 | default=None, |
| 1283 | help="qa reports environment for the results that have to be stored", |
| 1284 | ) |
| 1285 | parser.add_argument( |
| 1286 | "--qa-reports-build-version", |
| 1287 | dest="qa_reports_build_version", |
| 1288 | default=None, |
| 1289 | help="qa reports build id for the result set", |
| 1290 | ) |
Milosz Wasilewski | b4b40de | 2020-06-11 12:54:18 +0100 | [diff] [blame] | 1291 | parser.add_argument( |
| 1292 | "--qa-reports-disable-metadata", |
| 1293 | dest="qa_reports_disable_metadata", |
| 1294 | default=False, |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1295 | action="store_true", |
Milosz Wasilewski | 37848d3 | 2020-06-11 14:33:43 +0100 | [diff] [blame] | 1296 | help="Disable sending metadata to SQUAD. Default: false", |
| 1297 | ) |
| 1298 | parser.add_argument( |
| 1299 | "--qa-reports-metadata", |
| 1300 | dest="qa_reports_metadata", |
| 1301 | default={}, |
| 1302 | action=StoreDictKeyPair, |
| 1303 | nargs="+", |
| 1304 | metavar="KEY=VALUE", |
| 1305 | help="List of metadata key=value pairs to be sent to SQUAD", |
Milosz Wasilewski | b4b40de | 2020-06-11 12:54:18 +0100 | [diff] [blame] | 1306 | ) |
Milosz Wasilewski | abe2dcc | 2020-06-12 10:54:46 +0100 | [diff] [blame] | 1307 | parser.add_argument( |
| 1308 | "--qa-reports-metadata-file", |
| 1309 | dest="qa_reports_metadata_file", |
| 1310 | default=None, |
| 1311 | help="YAML file that defines metadata to be reported to SQUAD", |
| 1312 | ) |
Milosz Wasilewski | ba21eeb | 2022-02-15 10:23:11 +0000 | [diff] [blame] | 1313 | parser.add_argument( |
| 1314 | "--cleanup", |
| 1315 | dest="cleanup", |
| 1316 | default=False, |
| 1317 | action="store_true", |
Thomas Böhler | 56ff585 | 2024-06-12 15:44:51 +0200 | [diff] [blame^] | 1318 | help=textwrap.dedent( |
| 1319 | """\ |
| 1320 | If set to true, test-runner will remove all temporary files after |
| 1321 | running the test. It includes all collected logs and test results. This |
| 1322 | option should only be used if uploading results to SQUAD or LAVA. |
| 1323 | Default: false |
| 1324 | """ |
| 1325 | ), |
Milosz Wasilewski | ba21eeb | 2022-02-15 10:23:11 +0000 | [diff] [blame] | 1326 | ) |
Milosz Wasilewski | b4b40de | 2020-06-11 12:54:18 +0100 | [diff] [blame] | 1327 | |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1328 | args = parser.parse_args() |
| 1329 | return args |
| 1330 | |
| 1331 | |
| 1332 | def main(): |
Chase Qi | 0e9b36e | 2017-12-07 16:13:44 +0800 | [diff] [blame] | 1333 | args = get_args() |
| 1334 | |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1335 | # Setup logger. |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1336 | logger = logging.getLogger("RUNNER") |
Chase Qi | 0e9b36e | 2017-12-07 16:13:44 +0800 | [diff] [blame] | 1337 | logger.setLevel(logging.INFO) |
| 1338 | if args.verbose: |
| 1339 | logger.setLevel(logging.DEBUG) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1340 | ch = logging.StreamHandler() |
| 1341 | ch.setLevel(logging.DEBUG) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1342 | formatter = logging.Formatter("%(asctime)s - %(name)s: %(levelname)s: %(message)s") |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1343 | ch.setFormatter(formatter) |
| 1344 | logger.addHandler(ch) |
| 1345 | |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1346 | logger.debug("Test job arguments: %s" % args) |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1347 | if args.kind != "manual" and args.target is None: |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 1348 | if os.geteuid() != 0: |
| 1349 | logger.error("Sorry, you need to run this as root") |
| 1350 | sys.exit(1) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1351 | |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1352 | # Validate target argument format and connectivity. |
| 1353 | if args.target: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1354 | rex = re.compile(".+@.+") |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1355 | if not rex.match(args.target): |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1356 | logger.error("Usage: -g username@host") |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1357 | sys.exit(1) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1358 | if pexpect.which("ssh") is None: |
| 1359 | logger.error("openssh client must be installed on the host.") |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1360 | sys.exit(1) |
| 1361 | try: |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 1362 | run_command("exit", args.target) |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1363 | except subprocess.CalledProcessError as e: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1364 | logger.error("ssh login failed.") |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1365 | print(e) |
| 1366 | sys.exit(1) |
| 1367 | |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1368 | # Generate test plan. |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1369 | test_plan = TestPlan(args) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 1370 | test_list = test_plan.test_list(args.kind) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1371 | logger.info("Tests to run:") |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1372 | for test in test_list: |
| 1373 | print(test) |
| 1374 | |
| 1375 | # Run tests. |
| 1376 | for test in test_list: |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1377 | # Set and save test params to test dictionary. |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1378 | test["test_name"] = os.path.splitext(test["path"].split("/")[-1])[0] |
| 1379 | test["test_uuid"] = "%s_%s" % (test["test_name"], test["uuid"]) |
| 1380 | test["output"] = os.path.realpath(args.output) |
| 1381 | if args.target is not None and "-o" not in sys.argv: |
| 1382 | test["output"] = os.path.join(test["output"], args.target) |
| 1383 | test["test_path"] = os.path.join(test["output"], test["test_uuid"]) |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1384 | if args.target is not None: |
Chase Qi | 204b542 | 2017-04-06 11:01:58 +0800 | [diff] [blame] | 1385 | # Get relative directory path of yaml file for partial file copy. |
| 1386 | # '-d' takes any relative paths to the yaml file, so get the realpath first. |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1387 | tc_realpath = os.path.realpath(test["path"]) |
Chase Qi | 204b542 | 2017-04-06 11:01:58 +0800 | [diff] [blame] | 1388 | tc_dirname = os.path.dirname(tc_realpath) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1389 | test["tc_relative_dir"] = "%s%s" % ( |
| 1390 | args.kind, |
| 1391 | tc_dirname.split(args.kind)[1], |
| 1392 | ) |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 1393 | target_user_home = run_command("echo $HOME", args.target) |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1394 | test["target_test_path"] = "%s/output/%s" % ( |
| 1395 | target_user_home, |
| 1396 | test["test_uuid"], |
| 1397 | ) |
| 1398 | logger.debug("Test parameters: %s" % test) |
Milosz Wasilewski | 682120e | 2017-03-13 13:37:18 +0000 | [diff] [blame] | 1399 | |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1400 | # Create directories and copy files needed. |
| 1401 | setup = TestSetup(test, args) |
| 1402 | setup.create_dir() |
| 1403 | setup.copy_test_repo() |
Milosz Wasilewski | 970431b | 2016-11-25 14:10:08 +0000 | [diff] [blame] | 1404 | setup.checkout_version() |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1405 | setup.create_uuid_file() |
| 1406 | |
| 1407 | # Convert test definition. |
| 1408 | test_def = TestDefinition(test, args) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 1409 | if test_def.exists: |
| 1410 | test_def.definition() |
| 1411 | test_def.metadata() |
Nicolas Dechesne | 51b85a8 | 2017-02-04 00:45:48 +0100 | [diff] [blame] | 1412 | test_def.mkrun() |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1413 | |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 1414 | # Run test. |
Nicolas Dechesne | 51b85a8 | 2017-02-04 00:45:48 +0100 | [diff] [blame] | 1415 | test_def.run() |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1416 | |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 1417 | # Parse test output, save results in json and csv format. |
| 1418 | result_parser = ResultParser(test, args) |
| 1419 | result_parser.run() |
Milosz Wasilewski | ba21eeb | 2022-02-15 10:23:11 +0000 | [diff] [blame] | 1420 | if args.cleanup: |
| 1421 | # remove a copy of test-definitions |
| 1422 | logger.warning("Removing a copy of test-definitions") |
| 1423 | logger.warning("Removing all collected logs") |
| 1424 | shutil.rmtree(test["test_path"]) |
Milosz Wasilewski | 2fea70e | 2016-11-11 12:16:09 +0000 | [diff] [blame] | 1425 | else: |
Benjamin Copeland | 15d743e | 2021-02-22 08:35:10 +0000 | [diff] [blame] | 1426 | logger.warning("Requested test definition %s doesn't exist" % test["path"]) |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1427 | |
Dan Rue | a9eb01c | 2017-06-07 16:29:09 -0500 | [diff] [blame] | 1428 | |
Chase Qi | 09edc7f | 2016-08-18 13:18:50 +0800 | [diff] [blame] | 1429 | if __name__ == "__main__": |
| 1430 | main() |