Diff
Added: trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/bug_trackers/__init__.py (0 => 291766)
--- trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/bug_trackers/__init__.py (rev 0)
+++ trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/bug_trackers/__init__.py 2022-03-23 21:21:59 UTC (rev 291766)
@@ -0,0 +1 @@
+# DO NOTHING
Added: trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/bug_trackers/bug_description.py (0 => 291766)
--- trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/bug_trackers/bug_description.py (rev 0)
+++ trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/bug_trackers/bug_description.py 2022-03-23 21:21:59 UTC (rev 291766)
@@ -0,0 +1,88 @@
+# Copyright (C) 2022 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from functools import reduce
+
+from resultsdbpy.model.test_context import Expectations
+from resultsdbpy.controller.configuration import Configuration
+
+
+def translate_selected_dots_to_bug_title_and_description(commit_context, selected_rows, test, suite, repositories, will_filter_expected):
+
+ failed_scopes = []
+ for selected_row in selected_rows:
+ config = selected_row['config']
+ results = selected_row['results']
+ init_failure_type, init_failure_number = Expectations.get_test_result_status(results[0], will_filter_expected)
+ if not init_failure_type:
+ continue
+ sucess_results = list(filter(lambda result: not Expectations.get_test_result_status(result, will_filter_expected)[0], results))
+ if len(sucess_results):
+ last_sucess = sucess_results[0]
+ else:
+ last_sucess = None
+ failed_scopes.append((results[0], init_failure_type, init_failure_number, last_sucess, config))
+
+ if len(failed_scopes) == 0:
+ raise ValueError('No failures dectected')
+
+ title_components = set()
+ description_components = []
+
+ for failed_scope in failed_scopes:
+ result, init_failure_type, init_failure_number, last_sucess, config = failed_scope
+ version_name = config['version_name'] if 'version_name' in config else Configuration.integer_to_version(config['version'])
+ title_components.add('{} on {}({})'.format(init_failure_type, version_name, config['model']))
+ if init_failure_number is not None:
+ description_components.append('{} {}'.format(init_failure_number, init_failure_type))
+ description_components.append('Hardware: \t{}'.format(config['model']))
+ description_components.append('Architecture: \t{}'.format(config['architecture']))
+ description_components.append('OS: \t{}'.format(version_name))
+ description_components.append('Style: \t{}'.format(config['style']))
+ if suite == 'layout-tests':
+ description_components.append('Flavor \t{}'.format(config['flavor']))
+ if 'sdk' in config:
+ description_components.append('SDK: \t{}'.format(config['sdk']))
+ description_components.append('-----------------------------')
+ failure_commits = reduce(
+ lambda a, b: a + b,
+ map(lambda repo: commit_context.find_commits_by_uuid(repo, 'main', result['uuid']),
+ repositories)
+ )
+ description_components.append('Most recent failures:')
+ description_components += list(
+ map(lambda commit: '{}: {}'.format(commit.identifier, commit_context.url(commit)),
+ failure_commits)
+ )
+ description_components.append('-----------------------------')
+ if last_sucess:
+ description_components.append('Last success:')
+ last_sucess_commits = reduce(
+ lambda a, b: a + b,
+ map(lambda repo: commit_context.find_commits_by_uuid(repo, 'main', last_sucess['uuid']),
+ repositories)
+ )
+ description_components += list(
+ map(lambda commit: '{}: {}'.format(commit.identifier, commit_context.url(commit)),
+ last_sucess_commits)
+ )
+ return title_components, description_components
Added: trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/bug_trackers/bugzilla.py (0 => 291766)
--- trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/bug_trackers/bugzilla.py (rev 0)
+++ trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/bug_trackers/bugzilla.py 2022-03-23 21:21:59 UTC (rev 291766)
@@ -0,0 +1,172 @@
+# Copyright (C) 2022 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from ast import keyword
+from urllib.parse import quote
+from resultsdbpy.bug_trackers.bug_description import translate_selected_dots_to_bug_title_and_description
+from resultsdbpy.controller.bug_tracker_controller import BugTrackerConfig
+
+
+class WebKitBugzilla(BugTrackerConfig):
+
+ COMPONENTS_LAYOUT_TEST_MAPPING = {
+ 'Accessibility': {'lower': True},
+ 'ANGLE': {'skip': True},
+ 'Animations': {'lower': True},
+ 'Bindings': {'lower': True},
+ 'bmalloc': {'skip': True},
+ 'Canvas': {'lower': True},
+ 'CMake': {'skip': True},
+ 'Compositing': {'lower': True},
+ 'CSS': {'keywords': [
+ 'css', 'css1', 'css2.1', 'css3', 'cssom', 'css-custom-properties-api',
+ 'css-dark-mode', 'css-typedom', 'backgrounds', 'borders', 'box-decoration-break',
+ 'box-shadow', 'box-sizing', 'flexbox', 'gradients', 'inline', 'inline-block',
+ 'line-grid', 'multicol', 'overflow', 'transforms']},
+ 'DOM': {'keywords': ['dom', 'shadow-dom']},
+ 'Evangelism': {'skip': True},
+ 'Forms': {'lower': True},
+ 'Frames': {'lower': True},
+ 'History': {'lower': True},
+ 'HTML Editing': {'keywords': ['editing']},
+ 'Images': {'lower': True},
+ '_javascript_Core': {'keywords': ['js', 'jquery', 'regex']},
+ 'Layout and Rendering': {'keywords': ['rendering', 'layers']},
+ 'MathML': {'lower': True},
+ 'Media': {'keywords': ['media', 'mediacapturefromelement', 'mediasession', 'mediastream']},
+ 'New Bugs': {'keywords': ['imported']},
+ 'Page Loading': {'skip': True},
+ 'PDF': {'skip': True},
+ 'Platform': {'skip': True},
+ 'Plug-ins': {'skip': True},
+ 'Printing': {'lower': True},
+ 'Scrolling': {'keywords': ['scrolling', 'fast-moblie-scrolling']},
+ 'Service Workers': {'keywords': ['workers']},
+ 'SVG': {'lower': True},
+ 'Tables': {'lower': True},
+ 'Text': {'lower': True},
+ 'Tools / Tests': {'skip': True},
+ 'UI Events': {'keywords': ['events', 'eventloop']},
+ 'WebAssembly': {'keywords': ['wasm']},
+ 'Web Audio': {'keywords': ['webaudio']},
+ 'WebCore _javascript_': {'skip': True}, # FIXME maybe need move some keywords from _javascript_Core
+ 'WebCore Misc.': {'keywords': ['misc']},
+ 'WebDriver': {'skip': True},
+ 'WebGL': {'lower': True},
+ 'WebGPU': {'keywords': ['gpu-process']},
+ 'Web Inspector': {'keywords': ['inspector']},
+ 'WebKit2': {'skip': True},
+ 'WebKit API': {'skip': True},
+ 'WebKitGTK': {'skip': True},
+ 'WebKit Misc.': {'keywords': ['misc']},
+ 'WebKit Process Model': {'skip': True},
+ 'WebKit Website': {'skip': True},
+ 'WebRTC': {'lower': True},
+ 'Website Storage': {'keywords': ['storage']},
+ 'Web Template Framework'
+ 'WebXR': {'lower': True},
+ 'WPE WebKit': {'skip': True},
+ 'XML': {'lower': True},
+ }
+
+ def __init__(self):
+ super().__init__('bugzilla')
+
+ def create_bug(self, content):
+ """
+ content formats:
+ {
+ "selectedRows": [{
+ "config": {
+ "model": ...,
+ "architecture": ...,
+ "style": ...,
+ "flavor": ...,
+ "sdk": ...,
+ "version_name": ...,
+ "suite": ...
+ },
+ "results": [
+ {
+ "uuid": ...,
+ "actual": ...,
+ "expected": ...,
+ "start_time": ...,
+ "time": ...
+ }
+ ]
+ }],
+ "willFilterExpected": ...,
+ "repositories": [],
+ "suite": ...,
+ "test": ...
+ }
+ """
+
+ selected_rows = content['selectedRows']
+ will_filter_expected = content['willFilterExpected']
+ repositories = content['repositories']
+ suite = content['suite']
+ test = content['test']
+
+ title_components, description_components = translate_selected_dots_to_bug_title_and_description(self.commit_context, selected_rows, test, suite, repositories, will_filter_expected)
+
+ component = 'New Bugs'
+ version = 'WebKit Nightly Build'
+ if suite == 'api-tests':
+ component = 'WebKit API'
+ if suite == 'webkitpy-tests':
+ component = 'Tools / Tests'
+ if suite == 'internal-media-tests':
+ component = 'Media'
+ if suite == '_javascript_core-tests':
+ component = '_javascript_Core'
+ if test and any([suite == 'layout-tests', suite == 'internal-security-tests']):
+ test_name_parts = test.split('/')
+ for k, v in self.COMPONENTS_LAYOUT_TEST_MAPPING:
+ if 'skip' in v and v['skip']:
+ continue
+ if 'lower' in v and v['lower']:
+ if k.lower() in test_name_parts:
+ component = k
+ break
+ if 'keywords' in v and len(v['keywords']):
+ if any(map(lambda keyword: keyword in test_name_parts, v['keywords'])):
+ component = k
+ break
+
+ bugzilla_url_components = [
+ 'component={}'.format(quote(component)),
+ 'version={}'.format(quote(version)),
+ 'short_desc={} {}'.format(
+ quote(test if test else suite),
+ quote(' and '.join(list(title_components)))
+ ),
+ 'comment={}'.format(
+ quote('\n'.join(list(description_components)))
+ )
+ ]
+
+ return {
+ 'url': 'https://bugs.webkit.org/enter_bug.cgi?product=WebKit&{}'.format('&'.join(bugzilla_url_components)),
+ 'newWindow': True
+ }
Modified: trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/controller/api_routes.py (291765 => 291766)
--- trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/controller/api_routes.py 2022-03-23 20:26:40 UTC (rev 291765)
+++ trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/controller/api_routes.py 2022-03-23 21:21:59 UTC (rev 291766)
@@ -32,6 +32,7 @@
from resultsdbpy.controller.test_controller import TestController
from resultsdbpy.controller.upload_controller import UploadController
from resultsdbpy.controller.bug_tracker_controller import BugTrackerController
+from resultsdbpy.bug_trackers.bugzilla import WebKitBugzilla
from webkitflaskpy import AuthedBlueprint
from werkzeug.exceptions import HTTPException
@@ -50,7 +51,8 @@
self.ci_controller = CIController(ci_context=model.ci_context, upload_context=model.upload_context)
self.archive_controller = ArchiveController(commit_controller=self.commit_controller, archive_context=model.archive_context, upload_context=model.upload_context)
- self.bug_tracker_controller = BugTrackerController(bug_tracker_configs=bug_tracker_configs, commit_context=model.commit_context)
+ self.bug_tracker_configs = [WebKitBugzilla()] if not len(bug_tracker_configs) else bug_tracker_configs
+ self.bug_tracker_controller = BugTrackerController(bug_tracker_configs=self.bug_tracker_configs, commit_context=model.commit_context)
for code in [400, 404, 405]:
self.register_error_handler(code, self.error_response)
@@ -81,9 +83,8 @@
self.add_url_rule('/urls/queue', 'queue-urls', self.ci_controller.urls_for_queue_endpoint, methods=('GET',))
self.add_url_rule('/urls', 'build-urls', self.ci_controller.urls_for_builds_endpoint, methods=('GET',))
- if bug_tracker_configs:
- self.add_url_rule('/bug-trackers', 'bug-trackers', self.bug_tracker_controller.list_trackers, methods=('GET',))
- self.add_url_rule('/bug-trackers/<path:tracker>/create-bug', 'create-bug', self.bug_tracker_controller.create_bug, methods=('PUT',))
+ self.add_url_rule('/bug-trackers', 'bug-trackers', self.bug_tracker_controller.list_trackers, methods=('GET',))
+ self.add_url_rule('/bug-trackers/<path:tracker>/create-bug', 'create-bug', self.bug_tracker_controller.create_bug, methods=('PUT',))
def error_response(self, error):
response = jsonify(status='error', error=error.name, description=error.description)
Modified: trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/view/static/js/timeline.js (291765 => 291766)
--- trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/view/static/js/timeline.js 2022-03-23 20:26:40 UTC (rev 291765)
+++ trunk/Tools/Scripts/libraries/resultsdbpy/resultsdbpy/view/static/js/timeline.js 2022-03-23 21:21:59 UTC (rev 291766)
@@ -37,6 +37,11 @@
let willFilterExpected = false;
let showTestTimes = false;
+const BUG_TRACKER_COLORS = {
+ radar: 'var(--purple)',
+ bugzilla: 'var(--blue)'
+}
+
function minimumUuidForResults(results, limit) {
const now = Math.floor(Date.now() / 10);
let minDisplayedUuid = now;
@@ -550,66 +555,69 @@
}
_renderSelectedDotsButtonGroup(element) {
- DOM.inject(element, this.bugTrackers.map(bugTracker => {
- const buttonText = `${bugTracker}`;
- const buttonRef = REF.createRef({
- state: {
- loading: false
- },
- onStateUpdate: (element, stateDiff) => {
- if (stateDiff.loading)
- element.innerText = 'Waiting...';
- else
- element.innerText = buttonText;
- }
- });
-
- buttonRef.fromEvent('click').action(e => {
- const requestPayload = {
- selectedRows: [],
- willFilterExpected: InvestigateDrawer.willFilterExpected,
- repositories: this.repositories,
- suite: this.suite,
- test: this.test
- };
- Array.from(this.selectedDots.keys()).forEach(config => {
- const dots = this.selectedDots.get(config);
- requestPayload.selectedRows.push({
- config,
- results: dots
+ DOM.inject(element,
+ `<div class="row">
+ ${this.bugTrackers.map(bugTracker => {
+ const buttonText = `${bugTracker[0].toUpperCase()}${bugTracker.substring(1)}`;
+ const buttonRef = REF.createRef({
+ state: {
+ loading: false
+ },
+ onStateUpdate: (element, stateDiff) => {
+ if (stateDiff.loading)
+ element.innerText = 'Waiting...';
+ else
+ element.innerText = buttonText;
+ }
});
- });
- buttonRef.setState({loading: true});
- fetch(`api/bug-trackers/${bugTracker}/create-bug`, {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(requestPayload)
- }).then(res => {
- if (res.ok)
- return res.json()
- return res.json().then((data) => {
- throw new Error(data.description);
+
+ buttonRef.fromEvent('click').action(e => {
+ const requestPayload = {
+ selectedRows: [],
+ willFilterExpected: InvestigateDrawer.willFilterExpected,
+ repositories: this.repositories,
+ suite: this.suite,
+ test: this.test
+ };
+ Array.from(this.selectedDots.keys()).forEach(config => {
+ const dots = this.selectedDots.get(config);
+ requestPayload.selectedRows.push({
+ config,
+ results: dots
+ });
+ });
+ buttonRef.setState({loading: true});
+ fetch(`api/bug-trackers/${bugTracker}/create-bug`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(requestPayload)
+ }).then(res => {
+ if (res.ok)
+ return res.json()
+ return res.json().then((data) => {
+ throw new Error(data.description);
+ });
+ }).then(data ="" {
+ const bugLinkElement = document.createElement('a');
+ if (data['newWindow'])
+ bugLinkElement.setAttribute('target', '_blank');
+ bugLinkElement.setAttribute('href', data['url']);
+ bugLinkElement.click();
+ }).catch(e => {
+ alert(e);
+ }).finally(() => {
+ buttonRef.setState({loading: false});
+ });
+
});
- }).then(data ="" {
- const bugLinkElement = document.createElement('a');
- bugLinkElement.setAttribute('href', data['url']);
- bugLinkElement.click();
- }).catch(e => {
- alert(e);
- }).finally(() => {
- buttonRef.setState({loading: false});
- });
-
- });
-
- return `<div>
- <button class="button tiny" style="position:absolute; background: var(--purple); color: var(--white)" ref="${buttonRef}">
- ${buttonText}
- </button>
- </div>`;
- }).join(''));
+ return `
+ <button class="button tiny" style="background: ${BUG_TRACKER_COLORS[bugTracker]}; color: var(--white)" ref="${buttonRef}">
+ ${buttonText}
+ </button>`;
+ }).join('')}
+ </div>`);
}
render(limit) {
@@ -994,7 +1002,7 @@
self.notifyRerender = notifyRerender;
}));
return Timeline.CanvasContainer({
- customizedLayer: `<div class="row" style="position:absolute" ref="${this.selectedDotsButtonGroupRef}"></div>`,
+ customizedLayer: `<div style="position:absolute" ref="${this.selectedDotsButtonGroupRef}"></div>`,
onSelecting: (e) => {
this.selectedDotsButtonGroupRef.setState({show: false});
},