2019-11-08 12:33:47 +03:00
|
|
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
2020-05-06 19:56:53 +03:00
|
|
|
# Licensed under the Apache 2.0 License.
|
2019-11-08 12:33:47 +03:00
|
|
|
|
2020-01-29 18:09:28 +03:00
|
|
|
import infra.e2e_args
|
2020-07-07 17:46:44 +03:00
|
|
|
import infra.network
|
2019-11-08 12:33:47 +03:00
|
|
|
import suite.test_suite as s
|
|
|
|
import suite.test_requirements as reqs
|
2020-01-24 15:37:51 +03:00
|
|
|
import infra.logging_app as app
|
2021-05-20 19:08:16 +03:00
|
|
|
import infra.jwt_issuer
|
2019-11-08 12:33:47 +03:00
|
|
|
import time
|
|
|
|
import json
|
2020-01-24 15:37:51 +03:00
|
|
|
import sys
|
2021-03-16 22:23:54 +03:00
|
|
|
from enum import Enum, auto
|
2020-04-07 13:05:24 +03:00
|
|
|
import random
|
|
|
|
import os
|
2021-03-02 19:27:54 +03:00
|
|
|
import re
|
2019-11-08 12:33:47 +03:00
|
|
|
|
|
|
|
from loguru import logger as LOG
|
|
|
|
|
|
|
|
|
|
|
|
class TestStatus(Enum):
|
2021-03-16 22:23:54 +03:00
|
|
|
success = auto()
|
|
|
|
failure = auto()
|
|
|
|
skipped = auto()
|
2019-11-08 12:33:47 +03:00
|
|
|
|
|
|
|
|
2020-12-07 16:47:17 +03:00
|
|
|
def mem_stats(network):
|
|
|
|
mem = {}
|
|
|
|
for node in network.get_joined_nodes():
|
|
|
|
try:
|
|
|
|
with node.client() as c:
|
2021-05-11 14:00:59 +03:00
|
|
|
r = c.get("/node/memory", timeout=0.1)
|
2021-05-20 19:08:16 +03:00
|
|
|
mem[node.local_node_id] = r.body.json()
|
2020-12-07 16:47:17 +03:00
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
return mem
|
|
|
|
|
|
|
|
|
2019-11-08 12:33:47 +03:00
|
|
|
def run(args):
|
2020-05-06 19:56:53 +03:00
|
|
|
chosen_suite = []
|
2020-05-07 12:46:51 +03:00
|
|
|
|
2021-08-13 13:57:05 +03:00
|
|
|
if args.dry_run:
|
|
|
|
LOG.warning("--dry-run set. Test execution will be skipped")
|
|
|
|
|
2020-05-07 12:46:51 +03:00
|
|
|
if not args.test_suite:
|
|
|
|
args.test_suite = ["all"]
|
|
|
|
|
2020-05-06 19:56:53 +03:00
|
|
|
for choice in args.test_suite:
|
|
|
|
try:
|
|
|
|
chosen_suite.extend(s.suites[choice])
|
2020-08-21 14:09:13 +03:00
|
|
|
except KeyError as e:
|
|
|
|
raise ValueError(f"Unhandled choice: {choice}") from e
|
2020-05-06 19:56:53 +03:00
|
|
|
|
2020-04-24 17:32:30 +03:00
|
|
|
seed = None
|
2020-04-07 13:05:24 +03:00
|
|
|
if os.getenv("SHUFFLE_SUITE"):
|
2020-04-17 14:53:01 +03:00
|
|
|
seed = os.getenv("SHUFFLE_SUITE_SEED")
|
|
|
|
if seed is None:
|
|
|
|
seed = time.time()
|
|
|
|
seed = int(seed)
|
2020-04-24 17:32:30 +03:00
|
|
|
LOG.success(f"Shuffling full suite with seed {seed}")
|
2020-04-07 13:05:24 +03:00
|
|
|
random.seed(seed)
|
2020-05-06 19:56:53 +03:00
|
|
|
random.shuffle(chosen_suite)
|
2023-05-18 18:55:38 +03:00
|
|
|
# Only time reqs can be safely ignored is if they are produced from a randomly shuffled suite
|
|
|
|
args.throws_if_reqs_not_met = False
|
2020-05-06 19:56:53 +03:00
|
|
|
s.validate_tests_signature(chosen_suite)
|
2019-11-08 12:33:47 +03:00
|
|
|
|
2021-05-20 19:08:16 +03:00
|
|
|
jwt_issuer = infra.jwt_issuer.JwtIssuer("https://localhost")
|
2021-08-13 13:57:05 +03:00
|
|
|
|
|
|
|
if not args.dry_run:
|
|
|
|
jwt_server = jwt_issuer.start_openid_server()
|
|
|
|
|
2021-05-20 19:08:16 +03:00
|
|
|
txs = app.LoggingTxs(jwt_issuer=jwt_issuer)
|
2020-07-07 17:46:44 +03:00
|
|
|
network = infra.network.Network(
|
2021-05-20 19:08:16 +03:00
|
|
|
args.nodes,
|
|
|
|
args.binary_dir,
|
|
|
|
args.debug_nodes,
|
|
|
|
args.perf_nodes,
|
|
|
|
txs=txs,
|
|
|
|
jwt_issuer=jwt_issuer,
|
2020-02-06 18:27:18 +03:00
|
|
|
)
|
2021-08-13 13:57:05 +03:00
|
|
|
|
|
|
|
if not args.dry_run:
|
2022-02-18 18:42:26 +03:00
|
|
|
network.start_and_open(args)
|
2019-11-08 12:33:47 +03:00
|
|
|
|
2020-05-06 19:56:53 +03:00
|
|
|
LOG.info(f"Running {len(chosen_suite)} tests for {args.test_duration} seconds")
|
2019-11-08 12:33:47 +03:00
|
|
|
|
|
|
|
run_tests = {}
|
2020-01-24 15:37:51 +03:00
|
|
|
success = True
|
2019-11-08 12:33:47 +03:00
|
|
|
elapsed = args.test_duration
|
|
|
|
|
2021-03-02 19:27:54 +03:00
|
|
|
if args.filter is not None:
|
|
|
|
filter_re = re.compile(args.filter)
|
|
|
|
|
|
|
|
def filter_fun(x):
|
|
|
|
return filter_re is None or filter_re.match(x[1].__name__)
|
|
|
|
|
|
|
|
tests_to_run = filter(filter_fun, enumerate(chosen_suite))
|
|
|
|
else:
|
|
|
|
tests_to_run = enumerate(chosen_suite)
|
|
|
|
|
|
|
|
for i, test in tests_to_run:
|
2019-11-08 12:33:47 +03:00
|
|
|
status = None
|
|
|
|
reason = None
|
|
|
|
|
|
|
|
if elapsed <= 0:
|
|
|
|
LOG.warning(f"Test duration time ({args.test_duration} seconds) is up!")
|
|
|
|
break
|
|
|
|
|
|
|
|
try:
|
2021-08-13 13:57:05 +03:00
|
|
|
if not args.dry_run:
|
|
|
|
LOG.debug(f"Running {s.test_name(test)}...")
|
2019-11-08 12:33:47 +03:00
|
|
|
test_time_before = time.time()
|
|
|
|
|
|
|
|
# Actually run the test
|
2021-08-13 13:57:05 +03:00
|
|
|
if not args.dry_run:
|
|
|
|
new_network = test(network, args)
|
|
|
|
else:
|
|
|
|
new_network = network
|
2019-11-08 12:33:47 +03:00
|
|
|
status = TestStatus.success
|
|
|
|
|
|
|
|
except reqs.TestRequirementsNotMet as ce:
|
|
|
|
LOG.warning(f"Test requirements for {s.test_name(test)} not met")
|
|
|
|
status = TestStatus.skipped
|
|
|
|
reason = str(ce)
|
|
|
|
new_network = network
|
|
|
|
|
2020-04-27 17:33:04 +03:00
|
|
|
except Exception:
|
2019-11-08 12:33:47 +03:00
|
|
|
LOG.exception(f"Test {s.test_name(test)} failed")
|
|
|
|
status = TestStatus.failure
|
|
|
|
new_network = network
|
|
|
|
|
|
|
|
test_elapsed = time.time() - test_time_before
|
|
|
|
|
|
|
|
# Construct test report
|
2021-08-13 13:57:05 +03:00
|
|
|
run_tests[i] = {"name": s.test_name(test)}
|
|
|
|
|
|
|
|
if not args.dry_run:
|
|
|
|
run_tests[i].update(
|
|
|
|
{
|
|
|
|
"status": status.name,
|
|
|
|
"elapsed (s)": round(test_elapsed, 2),
|
|
|
|
"memory": mem_stats(new_network),
|
|
|
|
}
|
|
|
|
)
|
2019-11-08 12:33:47 +03:00
|
|
|
|
|
|
|
if reason is not None:
|
|
|
|
run_tests[i]["reason"] = reason
|
|
|
|
|
|
|
|
# If the test function did not return a network, it is not possible to continue
|
|
|
|
if new_network is None:
|
|
|
|
raise ValueError(f"Network returned by {s.test_name(test)} is None")
|
|
|
|
|
2021-02-04 18:35:33 +03:00
|
|
|
# If the network was changed (e.g. recovery test), use the new network from now on
|
2019-11-08 12:33:47 +03:00
|
|
|
if new_network != network:
|
|
|
|
network = new_network
|
|
|
|
|
2021-08-13 13:57:05 +03:00
|
|
|
if not args.dry_run:
|
|
|
|
LOG.debug(f"Test {s.test_name(test)} took {test_elapsed:.2f} secs")
|
2019-11-08 12:33:47 +03:00
|
|
|
|
|
|
|
# For now, if a test fails, the entire test suite if stopped
|
|
|
|
if status is TestStatus.failure:
|
2020-01-24 15:37:51 +03:00
|
|
|
success = False
|
2019-11-08 12:33:47 +03:00
|
|
|
break
|
|
|
|
|
|
|
|
elapsed -= test_elapsed
|
|
|
|
|
2021-08-13 13:57:05 +03:00
|
|
|
if not args.dry_run:
|
2021-10-06 18:51:24 +03:00
|
|
|
network.stop_all_nodes(skip_verification=True)
|
2021-08-13 13:57:05 +03:00
|
|
|
jwt_server.stop()
|
2020-01-20 16:47:03 +03:00
|
|
|
|
2020-04-17 12:03:29 +03:00
|
|
|
if success:
|
2020-05-06 19:56:53 +03:00
|
|
|
LOG.success(f"Full suite passed. Ran {len(run_tests)}/{len(chosen_suite)}")
|
2020-04-17 12:03:29 +03:00
|
|
|
else:
|
2020-05-06 19:56:53 +03:00
|
|
|
LOG.error(f"Suite failed. Ran {len(run_tests)}/{len(chosen_suite)}")
|
2020-04-24 17:32:30 +03:00
|
|
|
|
|
|
|
if seed:
|
|
|
|
LOG.info(f"Full suite was shuffled with seed: {seed}")
|
2020-04-17 12:03:29 +03:00
|
|
|
|
|
|
|
for idx, test in run_tests.items():
|
2021-08-13 13:57:05 +03:00
|
|
|
if "status" not in test:
|
|
|
|
log_fn = LOG.info
|
2020-04-17 12:03:29 +03:00
|
|
|
else:
|
2021-08-13 13:57:05 +03:00
|
|
|
status = test["status"]
|
|
|
|
if status == TestStatus.success.name:
|
|
|
|
log_fn = LOG.success
|
|
|
|
elif status == TestStatus.skipped.name:
|
|
|
|
log_fn = LOG.warning
|
|
|
|
else:
|
|
|
|
log_fn = LOG.error
|
2020-04-17 12:03:29 +03:00
|
|
|
log_fn(f"Test #{idx}:\n{json.dumps(test, indent=4)}")
|
2019-11-08 12:33:47 +03:00
|
|
|
|
2020-01-24 15:37:51 +03:00
|
|
|
if not success:
|
|
|
|
sys.exit(1)
|
|
|
|
|
2019-11-08 12:33:47 +03:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
|
|
def add(parser):
|
|
|
|
parser.add_argument(
|
2023-05-12 12:05:12 +03:00
|
|
|
"--test-duration",
|
|
|
|
help="Duration of full suite (s)",
|
|
|
|
type=int,
|
|
|
|
required=True,
|
2019-11-08 12:33:47 +03:00
|
|
|
)
|
2020-05-06 19:56:53 +03:00
|
|
|
parser.add_argument(
|
|
|
|
"--test-suite",
|
|
|
|
help="List of test suites should be run",
|
|
|
|
action="append",
|
|
|
|
choices=s.suites.keys(),
|
|
|
|
)
|
2021-03-02 19:27:54 +03:00
|
|
|
parser.add_argument(
|
|
|
|
"--filter",
|
|
|
|
help="Regular expression specifying which tests of a test suite to run",
|
|
|
|
type=str,
|
|
|
|
default=None,
|
|
|
|
)
|
2021-08-13 13:57:05 +03:00
|
|
|
parser.add_argument(
|
|
|
|
"--dry-run",
|
|
|
|
action="store_true",
|
|
|
|
help="If set, tests execution is skipped",
|
|
|
|
default=False,
|
|
|
|
)
|
2022-10-26 18:39:26 +03:00
|
|
|
parser.add_argument(
|
|
|
|
"--jinja-templates-path",
|
|
|
|
help="Path to directory containing sample Jinja templates",
|
|
|
|
)
|
2019-11-08 12:33:47 +03:00
|
|
|
|
2020-01-29 18:09:28 +03:00
|
|
|
args = infra.e2e_args.cli_args(add)
|
2021-09-17 11:51:25 +03:00
|
|
|
args.package = "samples/apps/logging/liblogging"
|
2020-10-22 16:54:43 +03:00
|
|
|
args.nodes = infra.e2e_args.max_nodes(args, f=0)
|
2021-01-05 20:32:57 +03:00
|
|
|
args.initial_user_count = 3
|
2021-05-20 19:08:16 +03:00
|
|
|
args.jwt_key_refresh_interval_s = 1
|
2019-11-08 12:33:47 +03:00
|
|
|
run(args)
|