Skip to content

Commit 960321d

Browse files
authored
Merge branch 'master' into MOB-42258-fix-bza-client-retry
2 parents c386e73 + 9b2d6cd commit 960321d

File tree

9 files changed

+373
-83
lines changed

9 files changed

+373
-83
lines changed

bzt/modules/aggregator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ def add_sample(self, sample):
291291
# count times only if we have RCs
292292
if con_time:
293293
self.sum_cn += con_time
294-
self.sum_lt += latency
294+
if latency:
295+
self.sum_lt += latency
295296
self.sum_rt += r_time
296297

297298
if error is not None:

bzt/modules/javascript.py

Lines changed: 112 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,34 @@ def get_launch_cwd(self, *args):
4646
pass
4747

4848

49+
class IncrementalLineReader(object):
50+
"""
51+
Line reader (e.g. jsonl)
52+
"""
53+
54+
def __init__(self, parent_logger, filename):
55+
self.log = parent_logger.getChild(self.__class__.__name__)
56+
self.partial_buffer = ""
57+
self.file = FileReader(filename=filename, parent_logger=self.log)
58+
self.read_speed = 1024 * 1024
59+
60+
def read(self, last_pass=False):
61+
"""
62+
read data from file
63+
yield one complete row (ending with \n)
64+
:type last_pass: bool
65+
"""
66+
lines = self.file.get_lines(size=self.read_speed, last_pass=last_pass)
67+
68+
for line in lines:
69+
if not line.endswith("\n"):
70+
self.partial_buffer += line
71+
continue
72+
73+
line = "%s%s" % (self.partial_buffer, line)
74+
self.partial_buffer = ""
75+
yield line
76+
4977
class PlaywrightTester(JavaScriptExecutor):
5078

5179
"""
@@ -67,7 +95,7 @@ def prepare(self):
6795
self.script = self.get_script_path()
6896
if not self.script:
6997
raise TaurusConfigError("Script not passed to runner %s" % self)
70-
self.reader = PlaywrightLogReader(self.engine.artifacts_dir + "/playwright.out", self.log)
98+
self.reader = PlaywrightLogReader(self.engine.artifacts_dir + "/taurus-playwright-reporter.jsonl", self.log)
7199
if isinstance(self.engine.aggregator, ConsolidatingAggregator):
72100
self.engine.aggregator.add_underling(self.reader)
73101

@@ -80,10 +108,11 @@ def install_required_tools(self):
80108

81109
node_npx_module = self._get_tool(NodeNPXModule, tools_dir=self.get_launch_cwd(), node_tool=self.node, npm_tool=self.npm)
82110
playwright = self._get_tool(PLAYWRIGHT, tools_dir=self.get_launch_cwd())
111+
playwright_reporter = self._get_tool(PlaywrightCustomReporter, tools_dir=self.get_launch_cwd(), node_tool=self.node, npm_tool=self.npm)
83112

84113
npm_all_packages = self._get_tool(NPMModuleInstaller,node_tool=self.node, npm_tool=self.npm, tools_dir=self.get_launch_cwd())
85114

86-
tools = [tcl_lib, self.node, self.npm, node_npx_module, npm_all_packages, playwright]
115+
tools = [tcl_lib, self.node, self.npm, node_npx_module, npm_all_packages, playwright, playwright_reporter]
87116
self._check_tools(tools)
88117

89118
def get_launch_cmdline(self, *args):
@@ -100,22 +129,37 @@ def startup(self):
100129
env = config["settings"]["env"]
101130
if "BASE_URL" in env:
102131
self.env.set({"BASE_URL": env["BASE_URL"]})
103-
if isinstance(config["execution"], dict):
104-
concurrency = config["execution"].get("concurrency", 1)
105-
iterations = config["execution"].get("iterations", 1)
106-
else:
107-
concurrency = config["execution"][0].get("concurrency", 1)
108-
iterations = config["execution"][0].get("iterations", 1)
109132

110-
if isinstance(concurrency, dict):
111-
concurrency = concurrency.get("local", 1)
112-
113-
reporter = "json"
133+
load = self.get_load()
114134

135+
max_duration = None
136+
concurrency = max(1, load.concurrency)
137+
if load.duration > 0:
138+
max_duration = load.duration
139+
if load.iterations > 0:
140+
repeat_each = concurrency*load.iterations
141+
else:
142+
# playwright support approx 100000 tests (= num tests * repeat_each)
143+
# setting repeat each to some save value. Lets customer setup iterations
144+
# for their test to hold for expected duration....
145+
repeat_each = 1000
146+
else:
147+
iterations = max(1, load.iterations)
148+
repeat_each = concurrency*iterations
149+
150+
reporter = "@taurus/playwright-custom-reporter"
151+
152+
# self.env.set({"TAURUS_PWREPORT_VERBOSE": "true"})
153+
# TODO: set to false if we will add support for customer reporter
154+
# - keep stdout to customer reporter (or default)
155+
self.env.set({"TAURUS_PWREPORT_STDOUT": "true"})
156+
self.env.set({"TAURUS_PWREPORT_DIR": self.engine.artifacts_dir})
157+
if max_duration:
158+
self.env.set({"TAURUS_PWREPORT_DURATION": str(int(max_duration * 1000))})
115159
options = ["--reporter " + reporter,
116160
"--output " + self.engine.artifacts_dir + "/test-output",
117161
"--workers " + str(concurrency),
118-
"--repeat-each " + str(concurrency*iterations)]
162+
"--repeat-each " + str(repeat_each)]
119163

120164
if "browser" in self.get_scenario().data:
121165
options.append("--project=" + self.get_scenario().data["browser"])
@@ -136,36 +180,25 @@ def __init__(self, filename, parent_logger):
136180
super(PlaywrightLogReader, self).__init__()
137181
self.log = parent_logger.getChild(self.__class__.__name__)
138182
self.filename = filename
139-
self.has_reported = False
183+
self.jsonl_reader = IncrementalLineReader(self.log, filename)
184+
185+
def _safe_ms_to_s(self, t):
186+
if t:
187+
return t / 1000.0
188+
return t
140189

141190
def _read(self, final_pass=False):
142-
if final_pass:
143-
if self.has_reported:
144-
yield None
145-
self.file = FileReader(filename=self.filename, parent_logger=self.log)
146-
self.lines = list(self.file.get_lines(last_pass=final_pass))
147-
content = json.loads("\n".join(self.lines))
148-
concurrency = content.get("config", {}).get("metadata", {}).get("actualWorkers")
149-
150-
suites = content.get("suites", [])
151-
for suite in suites:
152-
specs = suite.get("specs", [])
153-
for spec in specs:
154-
label = spec.get("title", "unknown")
155-
tests = spec.get("tests", [])
156-
for test in tests:
157-
results = test.get("results", [])
158-
if len(results) > 0:
159-
start_time = results[0].get("startTime")
160-
timestamp = datetime.strptime(start_time, '%Y-%m-%dT%H:%M:%S.%fZ').timestamp()
161-
duration = results[0].get("duration", 0) / 1000
162-
errors = None
163-
errs = results[0].get("errors")
164-
if len(errs) > 0:
165-
errors = ", ".join(errs)
166-
yield timestamp, label, concurrency, duration, None, duration, duration, errors, None, None
167-
self.has_reported = True
168-
pass
191+
for line in self.jsonl_reader.read(final_pass):
192+
content = json.loads(line)
193+
# runDetails: title of test, worker, repetition and browser platform
194+
yield (self._safe_ms_to_s(content.get("timestamp")), content.get("label"), content.get("concurency"),
195+
self._safe_ms_to_s(content.get("duration")),
196+
self._safe_ms_to_s(content.get("connectTime", None)),
197+
self._safe_ms_to_s(content.get("latency", None)),
198+
1 - int(content.get("ok", True)),
199+
content.get("error", None),
200+
content.get("runDetails", None),
201+
content.get("byte_count", 0))
169202

170203

171204
class MochaTester(JavaScriptExecutor):
@@ -343,36 +376,6 @@ def check_if_installed(self):
343376
return False
344377

345378

346-
class PLAYWRIGHT(RequiredTool):
347-
def __init__(self, tools_dir, **kwargs):
348-
super(PLAYWRIGHT, self).__init__(installable=True, **kwargs)
349-
self.tools_dir = tools_dir
350-
351-
def check_if_installed(self):
352-
# currently there seems to be no reliable way to find out whether all Playwright requirements are installed
353-
return False
354-
355-
def install(self):
356-
cmd_line = ["npx", "playwright", "install"]
357-
self.install_cmd(cmd_line + ["--with-deps"])
358-
self.install_cmd(cmd_line)
359-
self.install_cmd(cmd_line + ["chromium"])
360-
self.install_cmd(cmd_line + ["firefox"])
361-
self.install_cmd(cmd_line + ["webkit"])
362-
363-
def install_cmd(self, cmdline):
364-
self.log.debug("Installing Playwright: %s", cmdline)
365-
try:
366-
out, err = self.call(cmdline,cwd=self.tools_dir)
367-
except CALL_PROBLEMS as exc:
368-
self.log.warning("'%s' install failed: %s", cmdline, exc)
369-
return
370-
if out:
371-
self.log.debug("%s install stdout: %s", self.tool_name, out)
372-
if err:
373-
self.log.warning("%s install stderr: %s", self.tool_name, err)
374-
375-
376379
class NPMPackage(RequiredTool):
377380
PACKAGE_NAME = ""
378381

@@ -500,3 +503,41 @@ class TaurusNewmanPlugin(RequiredTool):
500503
def __init__(self, **kwargs):
501504
tool_path = os.path.join(RESOURCES_DIR, "newman-reporter-taurus.js")
502505
super(TaurusNewmanPlugin, self).__init__(tool_path=tool_path, installable=False, **kwargs)
506+
507+
class PlaywrightCustomReporter(NPMLocalModulePackage):
508+
PACKAGE_NAME = "@taurus/playwright-custom-reporter@1.0.0"
509+
PACKAGE_LOCAL_PATH = "./playwright-custom-reporter"
510+
511+
def check_if_installed(self):
512+
# always run install for local module to update to latest version
513+
# npm version resolving for local modules is not reliable
514+
return False
515+
516+
class PLAYWRIGHT(RequiredTool):
517+
def __init__(self, tools_dir, **kwargs):
518+
super(PLAYWRIGHT, self).__init__(installable=True, **kwargs)
519+
self.tools_dir = tools_dir
520+
521+
def check_if_installed(self):
522+
# currently there seems to be no reliable way to find out whether all Playwright requirements are installed
523+
return False
524+
525+
def install(self):
526+
cmd_line = ["npx", "playwright", "install"]
527+
self.install_cmd(cmd_line + ["--with-deps"])
528+
self.install_cmd(cmd_line)
529+
self.install_cmd(cmd_line + ["chromium"])
530+
self.install_cmd(cmd_line + ["firefox"])
531+
self.install_cmd(cmd_line + ["webkit"])
532+
533+
def install_cmd(self, cmdline):
534+
self.log.debug("Installing Playwright: %s", cmdline)
535+
try:
536+
out, err = self.call(cmdline,cwd=self.tools_dir)
537+
except CALL_PROBLEMS as exc:
538+
self.log.warning("'%s' install failed: %s", cmdline, exc)
539+
return
540+
if out:
541+
self.log.debug("%s install stdout: %s", self.tool_name, out)
542+
if err:
543+
self.log.warning("%s install stderr: %s", self.tool_name, err)

bzt/resources/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"@taurus/playwright-custom-reporter": "file:playwright-custom-reporter"
4+
}
5+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "@taurus/playwright-custom-reporter",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "playwright-custom-reporter.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"compilerOptions": {
12+
"module": "CommonJS"
13+
},
14+
"dependencies": {
15+
"eight-colors": "^1.3.1"
16+
},
17+
"devDependencies": {
18+
"@playwright/test": "^1.55.1"
19+
}
20+
}

0 commit comments

Comments
 (0)