Commit ba46f11f authored by Emily Hanley's avatar Emily Hanley Committed by Commit Bot

Converting kraken, jetstream and octane to new press harness.

Bug: 714231
Change-Id: I441067d621effc11fbd8d497716d6693e7bb5971
Reviewed-on: https://chromium-review.googlesource.com/c/1320200
Commit-Queue: Emily Hanley <eyaich@chromium.org>
Reviewed-by: default avatarCaleb Rouleau <crouleau@chromium.org>
Reviewed-by: default avatarNed Nguyen <nednguyen@google.com>
Cr-Commit-Position: refs/heads/master@{#606526}
parent b3a685fd
......@@ -17,80 +17,18 @@ smoothness. Some benchmarks demonstrate trade-offs, and aggressive or
specialized optimization for one benchmark might make another benchmark slower.
"""
import json
import os
from core import perf_benchmark
from telemetry import benchmark
from telemetry import page as page_module
from telemetry.page import legacy_page_test
from telemetry import story
from telemetry.util import statistics
from telemetry.value import list_of_scalar_values
class _JetstreamMeasurement(legacy_page_test.LegacyPageTest):
def __init__(self):
super(_JetstreamMeasurement, self).__init__()
import page_sets
from benchmarks import press
def WillNavigateToPage(self, page, tab):
page.script_to_evaluate_on_commit = """
var __results = [];
var __real_log = window.console.log;
window.console.log = function() {
__results.push(Array.prototype.join.call(arguments, ' '));
__real_log.apply(this, arguments);
}
"""
def ValidateAndMeasurePage(self, page, tab, results):
del page # unused
tab.WaitForDocumentReadyStateToBeComplete()
tab.EvaluateJavaScript('JetStream.start()')
result = tab.WaitForJavaScriptCondition("""
(function() {
for (var i = 0; i < __results.length; i++) {
if (!__results[i].indexOf('Raw results: ')) return __results[i];
}
return null;
})();
""", timeout=60*20)
result = json.loads(result.partition(': ')[2])
all_score_lists = []
for k, v in result.iteritems():
results.AddValue(list_of_scalar_values.ListOfScalarValues(
results.current_page, k.replace('.', '_'), 'score', v['result'],
important=False))
# Collect all test scores to compute geometric mean.
for i, score in enumerate(v['result']):
if len(all_score_lists) <= i:
all_score_lists.append([])
all_score_lists[i].append(score)
all_scores = []
for score_list in all_score_lists:
all_scores.append(statistics.GeometricMean(score_list))
results.AddSummaryValue(list_of_scalar_values.ListOfScalarValues(
None, 'Score', 'score', all_scores))
@benchmark.Info(emails=['hablich@chromium.org'],
component='Blink>JavaScript')
class Jetstream(perf_benchmark.PerfBenchmark):
test = _JetstreamMeasurement
class Jetstream(press._PressBenchmark): # pylint: disable=protected-access
@classmethod
def Name(cls):
return 'jetstream'
def CreateStorySet(self, options):
ps = story.StorySet(
archive_data_file='../page_sets/data/jetstream.json',
base_dir=os.path.dirname(os.path.abspath(__file__)),
cloud_storage_bucket=story.INTERNAL_BUCKET)
ps.AddStory(page_module.Page(
'http://browserbench.org/JetStream/', ps, ps.base_dir,
make_javascript_deterministic=False,
name='http://browserbench.org/JetStream/'))
return ps
return page_sets.JetstreamStorySet()
......@@ -4,124 +4,22 @@
"""Runs Mozilla's Kraken JavaScript benchmark."""
import json
import os
from core import perf_benchmark
from telemetry import benchmark
from telemetry import page as page_module
from telemetry.page import legacy_page_test
from telemetry import story
from telemetry.value import list_of_scalar_values
from telemetry.value import scalar
DESCRIPTIONS = {
'ai-astar':
'This benchmark uses the [A* search algorithm]'
'(http://en.wikipedia.org/wiki/A*_search_algorithm) to automatically '
'plot an efficient path between two points, in the presence of '
'obstacles. Adapted from code by [Brian Gringstead]'
'(http://www.briangrinstead.com/blog/astar-search-algorithm-in-'
'javascript).',
'audio-beat-detection':
'This benchmark performs [beat detection]'
'(http://en.wikipedia.org/wiki/Beat_detection) on an Audio sample '
'using [code](http://beatdetektor.svn.sourceforge.net/viewvc'
'/beatdetektor/trunk/core/js/beatdetektor.js?revision=18&view=markup) '
'from [BeatDetektor](http://www.cubicproductions.com/index.php'
'?option=com_content&view=article&id=67&Itemid=82) and '
'[DSP.js](http://github.com/corbanbrook/dsp.js/).',
'audio-dft':
'This benchmark performs a [Discrete Fourier Transform]'
'(http://en.wikipedia.org/wiki/Discrete_Fourier_transform) on an '
'Audio sample using code from [DSP.js]'
'(http://github.com/corbanbrook/dsp.js).',
'audio-fft':
'This benchmark performs a [Fast Fourier Transform]'
'(http://en.wikipedia.org/wiki/Fast_Fourier_transform) on an Audio '
'sample using code from [DSP.js]'
'(http://github.com/corbanbrook/dsp.js/).',
'audio-oscillator':
'This benchmark generates a soundwave using code from [DSP.js]'
'(http://github.com/corbanbrook/dsp.js/).',
'imaging-darkroom':
'This benchmark performs a variety of photo manipulations such as '
'Fill, Brightness, Contrast, Saturation, and Temperature.',
'imaging-desaturate':
'This benchmark [desaturates]'
'(http://en.wikipedia.org/wiki/Colorfulness) a photo using code from '
'[Pixastic](http://www.pixastic.com/).',
'imaging-gaussian-blur':
'This benchmark performs a [Gaussian blur]'
'(http://en.wikipedia.org/wiki/Gaussian_blur) on a photo.',
'json-parse-financial':
'This benchmark parses [JSON](http://www.json.org) records.',
'json-stringify-tinderbox':
'This benchmark serializes [Tinderbox]'
'(http://tests.themasta.com/tinderboxpushlog/?tree=Firefox) build '
'data to [JSON](http://www.json.org).',
}
def _Mean(l):
return float(sum(l)) / len(l) if len(l) > 0 else 0.0
class _KrakenMeasurement(legacy_page_test.LegacyPageTest):
def __init__(self):
super(_KrakenMeasurement, self).__init__()
def ValidateAndMeasurePage(self, page, tab, results):
tab.WaitForJavaScriptCondition(
'document.title.indexOf("Results") != -1', timeout=700)
tab.WaitForDocumentReadyStateToBeComplete()
result_dict = json.loads(tab.EvaluateJavaScript("""
var formElement = document.getElementsByTagName("input")[0];
decodeURIComponent(formElement.value.split("?")[1]);
"""))
total = 0
for key in result_dict:
if key == 'v':
continue
results.AddValue(list_of_scalar_values.ListOfScalarValues(
results.current_page, key, 'ms', result_dict[key], important=False,
description=DESCRIPTIONS.get(key)))
total += _Mean(result_dict[key])
# TODO(tonyg/nednguyen): This measurement shouldn't calculate Total. The
# results system should do that for us.
results.AddValue(scalar.ScalarValue(
results.current_page, 'Total', 'ms', total,
description='Total of the means of the results for each type '
'of benchmark in [Mozilla\'s Kraken JavaScript benchmark]'
'(http://krakenbenchmark.mozilla.org/)'))
import page_sets
from benchmarks import press
@benchmark.Info(emails=['hablich@chromium.org'],
component='Blink>JavaScript')
class Kraken(perf_benchmark.PerfBenchmark):
class Kraken(press._PressBenchmark): # pylint: disable=protected-access
"""Mozilla's Kraken JavaScript benchmark.
http://krakenbenchmark.mozilla.org/
"""
test = _KrakenMeasurement
@classmethod
def Name(cls):
return 'kraken'
def CreateStorySet(self, options):
ps = story.StorySet(
archive_data_file='../page_sets/data/kraken.json',
base_dir=os.path.dirname(os.path.abspath(__file__)),
cloud_storage_bucket=story.PARTNER_BUCKET)
ps.AddStory(page_module.Page(
'http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html',
ps, ps.base_dir,
name='http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html'))
return ps
return page_sets.KrakenStorySet()
......@@ -11,132 +11,21 @@ found in large, real-world web applications.
Octane 2.0 consists of 17 tests, four more than Octane v1.
"""
import os
from core import perf_benchmark
from telemetry import benchmark
from telemetry import page as page_module
from telemetry.page import legacy_page_test
from telemetry import story
from telemetry.util import statistics
from telemetry.value import scalar
_GB = 1024 * 1024 * 1024
DESCRIPTIONS = {
'CodeLoad':
'Measures how quickly a JavaScript engine can start executing code '
'after loading a large JavaScript program, social widget being a '
'common example. The source for test is derived from open source '
'libraries (Closure, jQuery) (1,530 lines).',
'Crypto':
'Encryption and decryption benchmark based on code by Tom Wu '
'(1698 lines).',
'DeltaBlue':
'One-way constraint solver, originally written in Smalltalk by John '
'Maloney and Mario Wolczko (880 lines).',
'EarleyBoyer':
'Classic Scheme benchmarks, translated to JavaScript by Florian '
'Loitsch\'s Scheme2Js compiler (4684 lines).',
'Gameboy':
'Emulate the portable console\'s architecture and runs a demanding 3D '
'simulation, all in JavaScript (11,097 lines).',
'Mandreel':
'Runs the 3D Bullet Physics Engine ported from C++ to JavaScript via '
'Mandreel (277,377 lines).',
'NavierStokes':
'2D NavierStokes equations solver, heavily manipulates double '
'precision arrays. Based on Oliver Hunt\'s code (387 lines).',
'PdfJS':
'Mozilla\'s PDF Reader implemented in JavaScript. It measures decoding '
'and interpretation time (33,056 lines).',
'RayTrace':
'Ray tracer benchmark based on code by Adam Burmister (904 lines).',
'RegExp':
'Regular expression benchmark generated by extracting regular '
'expression operations from 50 of the most popular web pages '
'(1761 lines).',
'Richards':
'OS kernel simulation benchmark, originally written in BCPL by Martin '
'Richards (539 lines).',
'Splay':
'Data manipulation benchmark that deals with splay trees and exercises '
'the automatic memory management subsystem (394 lines).',
}
class _OctaneMeasurement(legacy_page_test.LegacyPageTest):
def __init__(self):
super(_OctaneMeasurement, self).__init__()
def WillNavigateToPage(self, page, tab):
total_memory = tab.browser.platform.GetSystemTotalPhysicalMemory()
if total_memory is not None and total_memory < 1 * _GB:
skipBenchmarks = '"zlib"'
else:
skipBenchmarks = ''
page.script_to_evaluate_on_commit = """
var __results = [];
var __real_log = window.console.log;
window.console.log = function(msg) {
__results.push(msg);
__real_log.apply(this, [msg]);
}
skipBenchmarks = [%s]
""" % (skipBenchmarks)
def ValidateAndMeasurePage(self, page, tab, results):
tab.WaitForJavaScriptCondition('window.completed', timeout=10)
tab.WaitForJavaScriptCondition(
'!document.getElementById("progress-bar-container")', timeout=1200)
results_log = tab.EvaluateJavaScript('__results')
all_scores = []
for output in results_log:
# Split the results into score and test name.
# results log e.g., "Richards: 18343"
score_and_name = output.split(': ', 2)
assert len(score_and_name) == 2, \
'Unexpected result format "%s"' % score_and_name
if 'Skipped' not in score_and_name[1]:
name = score_and_name[0]
score = float(score_and_name[1])
results.AddValue(scalar.ScalarValue(
results.current_page, name, 'score', score, important=False,
description=DESCRIPTIONS.get(name)))
# Collect all test scores to compute geometric mean.
all_scores.append(score)
total = statistics.GeometricMean(all_scores)
results.AddSummaryValue(
scalar.ScalarValue(None, 'Total.Score', 'score', total,
description='Geometric mean of the scores of each '
'individual benchmark in the Octane '
'benchmark collection.'))
import page_sets
from benchmarks import press
@benchmark.Info(emails=['hablich@chromium.org'],
component='Blink>JavaScript')
class Octane(perf_benchmark.PerfBenchmark):
class Octane(press._PressBenchmark): # pylint: disable=protected-access
"""Google's Octane JavaScript benchmark.
http://chromium.github.io/octane/index.html?auto=1
"""
test = _OctaneMeasurement
@classmethod
def Name(cls):
return 'octane'
def CreateStorySet(self, options):
ps = story.StorySet(
archive_data_file='../page_sets/data/octane.json',
base_dir=os.path.dirname(os.path.abspath(__file__)),
cloud_storage_bucket=story.PUBLIC_BUCKET)
ps.AddStory(page_module.Page(
'http://chromium.github.io/octane/index.html?auto=1',
ps, ps.base_dir, make_javascript_deterministic=False,
name='http://chromium.github.io/octane/index.html?auto=1'))
return ps
return page_sets.OctaneStorySet()
......@@ -12,7 +12,7 @@ class DualMetricMeasurement(story_test.StoryTest):
Assumes both javascript as well as tracing metrics might be defined.
All pages associated with this measurement must implement
GetJavascriptMetricValues()
GetJavascriptMetricValues() and GetJavascriptMetricSummaryValues()
"""
def __init__(self, tbm_options):
super(DualMetricMeasurement, self).__init__()
......@@ -31,6 +31,8 @@ class DualMetricMeasurement(story_test.StoryTest):
def Measure(self, platform, results):
for value in results.current_page.GetJavascriptMetricValues():
results.AddValue(value)
for value in results.current_page.GetJavascriptMetricSummaryValues():
results.AddSummaryValue(value)
# This call is necessary to convert the current ScalarValues to
# histograms before more histograms are added. If we don't,
# when histograms get added by TBM2 page_test_results will see those and
......
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
from page_sets import press_story
from telemetry import story
from telemetry.util import statistics
from telemetry.value import list_of_scalar_values
class JetstreamStory(press_story.PressStory):
URL='http://browserbench.org/JetStream/'
def __init__(self, ps):
super(JetstreamStory, self).__init__(ps)
self.script_to_evaluate_on_commit = """
var __results = [];
var __real_log = window.console.log;
window.console.log = function() {
__results.push(Array.prototype.join.call(arguments, ' '));
__real_log.apply(this, arguments);
}
"""
def ExecuteTest(self, action_runner):
action_runner.tab.WaitForDocumentReadyStateToBeComplete()
action_runner.EvaluateJavaScript('JetStream.start()')
def ParseTestResults(self, action_runner):
result = action_runner.WaitForJavaScriptCondition("""
(function() {
for (var i = 0; i < __results.length; i++) {
if (!__results[i].indexOf('Raw results: ')) return __results[i];
}
return null;
})();
""", timeout=60*20)
result = json.loads(result.partition(': ')[2])
all_score_lists = []
for k, v in result.iteritems():
self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
self, k.replace('.', '_'), 'score', v['result'],
important=False))
# Collect all test scores to compute geometric mean.
for i, score in enumerate(v['result']):
if len(all_score_lists) <= i:
all_score_lists.append([])
all_score_lists[i].append(score)
all_scores = []
for score_list in all_score_lists:
all_scores.append(statistics.GeometricMean(score_list))
self.AddJavascriptMetricSummaryValue(
list_of_scalar_values.ListOfScalarValues(
None, 'Score', 'score', all_scores))
class JetstreamStorySet(story.StorySet):
def __init__(self):
super(JetstreamStorySet, self).__init__(
archive_data_file='data/jetstream.json',
cloud_storage_bucket=story.INTERNAL_BUCKET)
self.AddStory(JetstreamStory(self))
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
from page_sets import press_story
from telemetry import story
from telemetry.value import list_of_scalar_values
from telemetry.value import scalar
DESCRIPTIONS = {
'ai-astar':
'This benchmark uses the [A* search algorithm]'
'(http://en.wikipedia.org/wiki/A*_search_algorithm) to automatically '
'plot an efficient path between two points, in the presence of '
'obstacles. Adapted from code by [Brian Gringstead]'
'(http://www.briangrinstead.com/blog/astar-search-algorithm-in-'
'javascript).',
'audio-beat-detection':
'This benchmark performs [beat detection]'
'(http://en.wikipedia.org/wiki/Beat_detection) on an Audio sample '
'using [code](http://beatdetektor.svn.sourceforge.net/viewvc'
'/beatdetektor/trunk/core/js/beatdetektor.js?revision=18&view=markup) '
'from [BeatDetektor](http://www.cubicproductions.com/index.php'
'?option=com_content&view=article&id=67&Itemid=82) and '
'[DSP.js](http://github.com/corbanbrook/dsp.js/).',
'audio-dft':
'This benchmark performs a [Discrete Fourier Transform]'
'(http://en.wikipedia.org/wiki/Discrete_Fourier_transform) on an '
'Audio sample using code from [DSP.js]'
'(http://github.com/corbanbrook/dsp.js).',
'audio-fft':
'This benchmark performs a [Fast Fourier Transform]'
'(http://en.wikipedia.org/wiki/Fast_Fourier_transform) on an Audio '
'sample using code from [DSP.js]'
'(http://github.com/corbanbrook/dsp.js/).',
'audio-oscillator':
'This benchmark generates a soundwave using code from [DSP.js]'
'(http://github.com/corbanbrook/dsp.js/).',
'imaging-darkroom':
'This benchmark performs a variety of photo manipulations such as '
'Fill, Brightness, Contrast, Saturation, and Temperature.',
'imaging-desaturate':
'This benchmark [desaturates]'
'(http://en.wikipedia.org/wiki/Colorfulness) a photo using code from '
'[Pixastic](http://www.pixastic.com/).',
'imaging-gaussian-blur':
'This benchmark performs a [Gaussian blur]'
'(http://en.wikipedia.org/wiki/Gaussian_blur) on a photo.',
'json-parse-financial':
'This benchmark parses [JSON](http://www.json.org) records.',
'json-stringify-tinderbox':
'This benchmark serializes [Tinderbox]'
'(http://tests.themasta.com/tinderboxpushlog/?tree=Firefox) build '
'data to [JSON](http://www.json.org).',
}
def _Mean(l):
return float(sum(l)) / len(l) if len(l) > 0 else 0.0
class KrakenStory(press_story.PressStory):
URL='http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html'
def ExecuteTest(self, action_runner):
action_runner.WaitForJavaScriptCondition(
'document.title.indexOf("Results") != -1', timeout=700)
action_runner.tab.WaitForDocumentReadyStateToBeComplete()
def ParseTestResults(self, action_runner):
result_dict = json.loads(action_runner.EvaluateJavaScript("""
var formElement = document.getElementsByTagName("input")[0];
decodeURIComponent(formElement.value.split("?")[1]);
"""))
total = 0
for key in result_dict:
if key == 'v':
continue
self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
self, key, 'ms', result_dict[key], important=False,
description=DESCRIPTIONS.get(key)))
total += _Mean(result_dict[key])
# TODO(tonyg/nednguyen): This measurement shouldn't calculate Total. The
# results system should do that for us.
self.AddJavascriptMetricValue(scalar.ScalarValue(
self, 'Total', 'ms', total,
description='Total of the means of the results for each type '
'of benchmark in [Mozilla\'s Kraken JavaScript benchmark]'
'(http://krakenbenchmark.mozilla.org/)'))
class KrakenStorySet(story.StorySet):
def __init__(self):
super(KrakenStorySet, self).__init__(
archive_data_file='data/kraken.json',
cloud_storage_bucket=story.PARTNER_BUCKET)
self.AddStory(KrakenStory(self))
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from telemetry import story
from telemetry.util import statistics
from telemetry.value import scalar
from page_sets import press_story
_GB = 1024 * 1024 * 1024
DESCRIPTIONS = {
'CodeLoad':
'Measures how quickly a JavaScript engine can start executing code '
'after loading a large JavaScript program, social widget being a '
'common example. The source for test is derived from open source '
'libraries (Closure, jQuery) (1,530 lines).',
'Crypto':
'Encryption and decryption benchmark based on code by Tom Wu '
'(1698 lines).',
'DeltaBlue':
'One-way constraint solver, originally written in Smalltalk by John '
'Maloney and Mario Wolczko (880 lines).',
'EarleyBoyer':
'Classic Scheme benchmarks, translated to JavaScript by Florian '
'Loitsch\'s Scheme2Js compiler (4684 lines).',
'Gameboy':
'Emulate the portable console\'s architecture and runs a demanding 3D '
'simulation, all in JavaScript (11,097 lines).',
'Mandreel':
'Runs the 3D Bullet Physics Engine ported from C++ to JavaScript via '
'Mandreel (277,377 lines).',
'NavierStokes':
'2D NavierStokes equations solver, heavily manipulates double '
'precision arrays. Based on Oliver Hunt\'s code (387 lines).',
'PdfJS':
'Mozilla\'s PDF Reader implemented in JavaScript. It measures decoding '
'and interpretation time (33,056 lines).',
'RayTrace':
'Ray tracer benchmark based on code by Adam Burmister (904 lines).',
'RegExp':
'Regular expression benchmark generated by extracting regular '
'expression operations from 50 of the most popular web pages '
'(1761 lines).',
'Richards':
'OS kernel simulation benchmark, originally written in BCPL by Martin '
'Richards (539 lines).',
'Splay':
'Data manipulation benchmark that deals with splay trees and exercises '
'the automatic memory management subsystem (394 lines).',
}
class OctaneStory(press_story.PressStory):
URL='http://chromium.github.io/octane/index.html?auto=1'
def RunNavigateSteps(self, action_runner):
total_memory = (
action_runner.tab.browser.platform.GetSystemTotalPhysicalMemory())
if total_memory is not None and total_memory < 1 * _GB:
skipBenchmarks = '"zlib"'
else:
skipBenchmarks = ''
self.script_to_evaluate_on_commit = """
var __results = [];
var __real_log = window.console.log;
window.console.log = function(msg) {
__results.push(msg);
__real_log.apply(this, [msg]);
}
skipBenchmarks = [%s]
""" % (skipBenchmarks)
super(OctaneStory, self).RunNavigateSteps(action_runner)
def ExecuteTest(self, action_runner):
action_runner.WaitForJavaScriptCondition('window.completed', timeout=10)
action_runner.WaitForJavaScriptCondition(
'!document.getElementById("progress-bar-container")', timeout=1200)
def ParseTestResults(self, action_runner):
results_log = action_runner.EvaluateJavaScript('__results')
all_scores = []
for output in results_log:
# Split the results into score and test name.
# results log e.g., "Richards: 18343"
score_and_name = output.split(': ', 2)
assert len(score_and_name) == 2, \
'Unexpected result format "%s"' % score_and_name
if 'Skipped' not in score_and_name[1]:
name = score_and_name[0]
score = float(score_and_name[1])
self.AddJavascriptMetricValue(scalar.ScalarValue(
self, name, 'score', score, important=False,
description=DESCRIPTIONS.get(name)))
# Collect all test scores to compute geometric mean.
all_scores.append(score)
total = statistics.GeometricMean(all_scores)
self.AddJavascriptMetricSummaryValue(
scalar.ScalarValue(None, 'Total.Score', 'score', total,
description='Geometric mean of the scores of each '
'individual benchmark in the Octane '
'benchmark collection.'))
class OctaneStorySet(story.StorySet):
def __init__(self):
super(OctaneStorySet, self).__init__(
archive_data_file='data/octane.json',
cloud_storage_bucket=story.PUBLIC_BUCKET)
self.AddStory(OctaneStory(self))
......@@ -34,6 +34,7 @@ class PressStory(page_module.Page):
make_javascript_deterministic=self.DETERMINISTIC_JS,
name=self.NAME if self.NAME else self.URL)
self._values = []
self._summary_values = []
def GetJavascriptMetricValues(self):
return self._values
......@@ -41,6 +42,12 @@ class PressStory(page_module.Page):
def AddJavascriptMetricValue(self, value):
self._values.append(value)
def GetJavascriptMetricSummaryValues(self):
return self._summary_values
def AddJavascriptMetricSummaryValue(self, value):
self._summary_values.append(value)
def ExecuteTest(self, action_runner):
pass
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment