Changeset 194130 in webkit


Ignore:
Timestamp:
Dec 15, 2015 9:19:08 PM (8 years ago)
Author:
rniwa@webkit.org
Message:

Add v3 UI to perf dashboard
https://bugs.webkit.org/show_bug.cgi?id=152311

Reviewed by Chris Dumez.

Add the third iteration of the perf dashboard UI. UI for viewing and modifying analysis tasks is coming soon.
The v3 UI is focused on speed, and removes all third-party script dependencies including jQuery, d3, and Ember.
Both the DOM-based UI and graphing are implemented manually.

The entire app is structured using new component library implemented in components/base.js. Each component is
an instance of a subclass of ComponentBase which owns a single DOM element. Each subclass may supply static
methods named htmlTemplate and cssTemplate as the template for a component instance. ComponentBase automatically
clones the templates inside the associated element (or its shadow root on the supported browsers). Each subclass
must supply a method called "render()" which constructs and updates the DOM as needed.

There is a special component called Page, which represents an entire page. Each Page is opened by PageRouter's
"route()" function. Each subclass of Page supplies "open()" for initialization and "updateFromSerializedState()"
for a hash URL transition.

The key feature of the v3 UI is the split of time series into chunks called clusters (see r194120). On an internal
instance of the dashboard, the v2 UI downloads 27MB of data whereas the same page loads only 3MB of data in the v3.
The key logic for fetching time series in chunks is implemented by MeasurementSet in /v3/models/measurement-set.js.
We first fetch the cached primary cluster (the cluster that contains the newest data) at:
/data/measurement-set-<platform-id>-<metric-id>.json

If that's outdated according to lastModified in manifest.json, then we immediately re-fetch the primary cluster at:
/api/measurement-set/?platform=<platform-id>&metric=<metric-id>

Once the up-to-date primary cluster is fetched, we fetch all "secondary" clusters. For each cluster being fetched,
including the primary, we invoke registered callbacks.

In addition, the v3 UI reduces the initial page load time by loading a single bundled JS file generated by
tools/bundle-v3-scripts.py. index.html has a fallback to load all 44 JS files individually during development.

  • public/api/analysis-tasks.php:

(fetch_and_push_bugs_to_tasks): Added the code to fetch start and end run times. This is necessary in V3 UI
because no longer fetch the entire time series. See r194120 for the new measurement set JSON API.
(format_task): Compute the category of an analysis task based on "result" value. This will be re-vamped once
I add the UI for the analysis task page in v3.

  • public/include/json-header.php:

(require_format): CamelCase the name.
(require_match_one_of_values): Ditto.
(validate_arguments): Renamed from require_existence_of and used in measurement-set.php landed in r194120.

  • public/v3: Added.
  • public/v3/components: Added.
  • public/v3/components/base.js: Added.

(ComponentBase): The base component class.
(ComponentBase.prototype.element): Returns the DOM element associated with the DOM element.
(ComponentBase.prototype.content): Returns the shadow root if one exists and the associated element otherwise.
(ComponentBase.prototype.render): To be implemented by a subclass.
(ComponentBase.prototype.renderReplace): A helper function to "render" DOM contents.
(ComponentBase.prototype._constructShadowTree): Called inside the constructor to instantiate the templates.
(ComponentBase.prototype._recursivelyReplaceUnknownElementsByComponents): Instantiates components referred by
its element name inside the instantiated content.
(ComponentBase.isElementInViewport): A helper function. Returns true if the element is in the viewport and it has
non-zero width and height.
(ComponentBase.defineElement): Defines a custom element that can be automatically instantiated from htmlTemplate.
(ComponentBase.createElement): A helper function to create DOM tree to be used in "render()" method.
(ComponentBase._addContentToElement): A helper for "createElement".
(ComponentBase.createLink): A helper function to create a hyperlink or another clickable element (via callback).
(ComponentBase.createActionHandler): A helper function to create an event listener that prevents the default action
and stops the event propagation.

  • public/v3/components/button-base.js: Added.
  • public/v3/components/chart-status-view.js: Added.

(ChartStatusView): A component that reports the current status of time-series-chart. It's subclasses by
ChartPaneStatusView to provide additional information in the charts page's panes.

  • public/v3/components/close-button.js: Added.

(CloseButton):

  • public/v3/components/commit-log-viewer.js: Added.

(CommitLogViewer): A component that lists commit revisions along with commit messages for a range of data points.

  • public/v3/components/interactive-time-series-chart.js: Added.

(InteractiveTimeSeriesChart): A subclass of InteractiveTimeSeriesChart with interactivity (selection & indicator).
Selection and indicator are mutually exclusive.

  • public/v3/components/pane-selector.js: Added.

(PaneSelector): A component for selecting (platform, metric) pair to add in the charts page.

  • public/v3/components/spinner-icon.js: Added.
  • public/v3/components/time-series-chart.js: Added.

(TimeSeriesChart): A canvas-based chart component without interactivity. It takes a source list and options as
the constructor arguments. A source list is a list of measurement sets (measurement-set.js) with drawing options.
This component fetches data via MeasurementSet.fetchBetween inside TimeSeriesChart.prototype.setDomain and
progressively updates the charts as more data arrives. The canvas is updated on animation frame via rAF and all
layout and rendering metrics are lazily computed in _layout. In addition, this component samples data before
rendering the chart when there are more data points per pixel in _ensureSampledTimeSeries.

  • public/v3/index.html: Added. Loads bundled-scripts.js if it exists, or individual script files otherwise.
  • public/v3/instrumentation.js: Added. This class is used to gather runtime statistics of v3 UI. (It measures

the performance of the perf dashboard UI).

  • public/v3/main.js: Added. Bootstraps the app.

(main):
(fetchManifest):

  • public/v3/models: Added.
  • public/v3/models/analysis-task.js: Added.
  • public/v3/models/bug-tracker.js: Added.
  • public/v3/models/bug.js: Added.
  • public/v3/models/builder.js: Added.
  • public/v3/models/commit-log.js: Added.
  • public/v3/models/data-model.js: Added.

(DataModelObject): The base class for various data objects that correspond to database tables. It supplies static
hash map to find entries by id as well as other keys.
(LabeledObject): A subclass of DataModelObject with the capability to find an object via its name.

  • public/v3/models/measurement-cluster.js: Added.

(MeasurementCluster): Represents a single cluster or a chunk of data in a measurement set.

  • public/v3/models/measurement-set.js: Added.

(MeasurementSet): Represents a measurement set.
(MeasurementSet.findSet): Returns the singleton set given (metric, platform). We use singleton to avoid issuing
multiple HTTP requests for the same JSON when there are multiple TimeSeriesChart that show the same graph (e.g. on
charts page with overview and main charts).
(MeasurementSet.prototype.findClusters): Finds the list of clusters to fetch in a given time range.
(MeasurementSet.prototype.fetchBetween): Fetch clusters for a given time range and calls callback whenever new data
arrives. The number of callbacks depends on the how many clusters need to be newly fetched.
(MeasurementSet.prototype._fetchSecondaryClusters): Fetches non-primary (non-latest) clusters.
(MeasurementSet.prototype._fetch): Issues a HTTP request to fetch a cluster.
(MeasurementSet.prototype._didFetchJSON): Called when a cluster is fetched.
(MeasurementSet.prototype._failedToFetchJSON): Called when the fetching of a cluster has failed.
(MeasurementSet.prototype._invokeCallbacks): Invokes callbacks upon an approval of a new cluster.
(MeasurementSet.prototype._addFetchedCluster): Adds the newly fetched cluster in the order.
(MeasurementSet.prototype.fetchedTimeSeries): Returns a time series that contains data from all clusters that have
been fetched.
(TimeSeries.prototype.findById): Additions to TimeSeries defined in /v2/data.js.
(TimeSeries.prototype.dataBetweenPoints): Ditto.
(TimeSeries.prototype.firstPoint): Ditto.

  • public/v3/models/metric.js: Added.
  • public/v3/models/platform.js: Added.
  • public/v3/models/repository.js: Added.
  • public/v3/models/test.js: Added.
  • public/v3/pages: Added.
  • public/v3/pages/analysis-category-page.js: Added. The "Analysis" page that lists the analysis tasks.
  • public/v3/pages/analysis-category-toolbar.js: Added. The toolbar to filter analysis tasks based on its category

(unconfirmed, bisecting, identified, closed) and a keyword.

  • public/v3/pages/analysis-task-page.js: Added. Not implemented yet. It just has the hyperlink to the v2 UI.
  • public/v3/pages/chart-pane-status-view.js: Added.

(ChartPaneStatusView): A subclass of ChartStatusView used in the charts page. In addition to the current value,
comparison to baseline/target, it shows the list of repository revisions (e.g. WebKit revision, OS version).

  • public/v3/pages/chart-pane.js: Added.

(ChartPane): A component a pane in the charts page. Each pane has the overview chart and the main chart. The zooming
is synced across all panes in the charts page.

  • public/v3/pages/charts-page.js: Added. Charts page.
  • public/v3/pages/charts-toolbar.js: Added. The toolbar to set the number of days to show. This affects the overview

chart's domain in each pane.

  • public/v3/pages/create-analysis-task-page.js: Added.

(CreateAnalysisTaskPage): A page that gets shown momentarily while creating a new analysis task.

  • public/v3/pages/dashboard-page.js: Added. A dashboard page.
  • public/v3/pages/dashboard-toolbar.js: Added. Its toolbar with buttons to select the number of days to show.
  • public/v3/pages/domain-control-toolbar.js: Added. An abstract superclass of charts and dashboard toolbars.
  • public/v3/pages/heading.js: Added. A component for displaying the header and toolbar, if exists, on each page.
  • public/v3/pages/page-router.js: Added. This class is responsible for updating the URL hashes as well as opening

and updating each page when the hash changes (via back/forward navigation).

  • public/v3/pages/page-with-charts.js: Added. An abstract subclass of page used by dashboards and charts page.

Supplies helper functions for creating TimeSeriesChart options.

  • public/v3/pages/page-with-heading.js: Added. An abstract subclass of page that uses the heading component.
  • public/v3/pages/page.js: Added. The Page component.
  • public/v3/pages/toolbar.js: Added. An abstract toolbar component.
  • public/v3/remote.js: Added.

(getJSON): Fetches JSON from the remote server.
(getJSONWithStatus): Ditto. Rejects the response if the status is not "OK".
(PrivilegedAPI.sendRequest): Posts a HTTP request to a privileged API in /privileged-api/.
(PrivilegedAPI.requestCSRFToken): Creates a new CSRF token to request a privileged API post.

  • tools/bundle-v3-scripts.py: Added.

(main): Bundles js files together and minifies them by jsmin.py for the v3 UI. Without this script, we're forced to
download 44 JS files or making each JS file contain multiple classes.

Location:
trunk/Websites/perf.webkit.org
Files:
48 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Websites/perf.webkit.org/ChangeLog

    r194121 r194130  
     12015-12-15  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        Add v3 UI to perf dashboard
     4        https://bugs.webkit.org/show_bug.cgi?id=152311
     5
     6        Reviewed by Chris Dumez.
     7
     8        Add the third iteration of the perf dashboard UI. UI for viewing and modifying analysis tasks is coming soon.
     9        The v3 UI is focused on speed, and removes all third-party script dependencies including jQuery, d3, and Ember.
     10        Both the DOM-based UI and graphing are implemented manually.
     11
     12
     13        The entire app is structured using new component library implemented in components/base.js. Each component is
     14        an instance of a subclass of ComponentBase which owns a single DOM element. Each subclass may supply static
     15        methods named htmlTemplate and cssTemplate as the template for a component instance. ComponentBase automatically
     16        clones the templates inside the associated element (or its shadow root on the supported browsers). Each subclass
     17        must supply a method called "render()" which constructs and updates the DOM as needed.
     18
     19        There is a special component called Page, which represents an entire page. Each Page is opened by PageRouter's
     20        "route()" function. Each subclass of Page supplies "open()" for initialization and "updateFromSerializedState()"
     21        for a hash URL transition.
     22
     23
     24        The key feature of the v3 UI is the split of time series into chunks called clusters (see r194120). On an internal
     25        instance of the dashboard, the v2 UI downloads 27MB of data whereas the same page loads only 3MB of data in the v3.
     26        The key logic for fetching time series in chunks is implemented by MeasurementSet in /v3/models/measurement-set.js.
     27        We first fetch the cached primary cluster (the cluster that contains the newest data) at:
     28        /data/measurement-set-<platform-id>-<metric-id>.json
     29
     30        If that's outdated according to lastModified in manifest.json, then we immediately re-fetch the primary cluster at:
     31        /api/measurement-set/?platform=<platform-id>&metric=<metric-id>
     32
     33        Once the up-to-date primary cluster is fetched, we fetch all "secondary" clusters. For each cluster being fetched,
     34        including the primary, we invoke registered callbacks.
     35
     36
     37        In addition, the v3 UI reduces the initial page load time by loading a single bundled JS file generated by
     38        tools/bundle-v3-scripts.py. index.html has a fallback to load all 44 JS files individually during development.
     39
     40        * public/api/analysis-tasks.php:
     41        (fetch_and_push_bugs_to_tasks): Added the code to fetch start and end run times. This is necessary in V3 UI
     42        because no longer fetch the entire time series. See r194120 for the new measurement set JSON API.
     43        (format_task): Compute the category of an analysis task based on "result" value. This will be re-vamped once
     44        I add the UI for the analysis task page in v3.
     45
     46        * public/include/json-header.php:
     47        (require_format): CamelCase the name.
     48        (require_match_one_of_values): Ditto.
     49        (validate_arguments): Renamed from require_existence_of and used in measurement-set.php landed in r194120.
     50
     51        * public/v3: Added.
     52        * public/v3/components: Added.
     53
     54        * public/v3/components/base.js: Added.
     55        (ComponentBase): The base component class.
     56        (ComponentBase.prototype.element): Returns the DOM element associated with the DOM element.
     57        (ComponentBase.prototype.content): Returns the shadow root if one exists and the associated element otherwise.
     58        (ComponentBase.prototype.render): To be implemented by a subclass.
     59        (ComponentBase.prototype.renderReplace): A helper function to "render" DOM contents.
     60        (ComponentBase.prototype._constructShadowTree): Called inside the constructor to instantiate the templates.
     61        (ComponentBase.prototype._recursivelyReplaceUnknownElementsByComponents): Instantiates components referred by
     62        its element name inside the instantiated content.
     63        (ComponentBase.isElementInViewport): A helper function. Returns true if the element is in the viewport and it has
     64        non-zero width and height.
     65        (ComponentBase.defineElement): Defines a custom element that can be automatically instantiated from htmlTemplate.
     66        (ComponentBase.createElement): A helper function to create DOM tree to be used in "render()" method.
     67        (ComponentBase._addContentToElement): A helper for "createElement".
     68        (ComponentBase.createLink): A helper function to create a hyperlink or another clickable element (via callback).
     69        (ComponentBase.createActionHandler): A helper function to create an event listener that prevents the default action
     70        and stops the event propagation.
     71
     72        * public/v3/components/button-base.js: Added.
     73
     74        * public/v3/components/chart-status-view.js: Added.
     75        (ChartStatusView): A component that reports the current status of time-series-chart. It's subclasses by
     76        ChartPaneStatusView to provide additional information in the charts page's panes.
     77
     78        * public/v3/components/close-button.js: Added.
     79        (CloseButton):
     80        * public/v3/components/commit-log-viewer.js: Added.
     81        (CommitLogViewer): A component that lists commit revisions along with commit messages for a range of data points.
     82
     83        * public/v3/components/interactive-time-series-chart.js: Added.
     84        (InteractiveTimeSeriesChart): A subclass of InteractiveTimeSeriesChart with interactivity (selection & indicator).
     85        Selection and indicator are mutually exclusive.
     86
     87        * public/v3/components/pane-selector.js: Added.
     88        (PaneSelector): A component for selecting (platform, metric) pair to add in the charts page.
     89
     90        * public/v3/components/spinner-icon.js: Added.
     91
     92        * public/v3/components/time-series-chart.js: Added.
     93        (TimeSeriesChart): A canvas-based chart component without interactivity. It takes a source list and options as
     94        the constructor arguments. A source list is a list of measurement sets (measurement-set.js) with drawing options.
     95        This component fetches data via MeasurementSet.fetchBetween inside TimeSeriesChart.prototype.setDomain and
     96        progressively updates the charts as more data arrives. The canvas is updated on animation frame via rAF and all
     97        layout and rendering metrics are lazily computed in _layout. In addition, this component samples data before
     98        rendering the chart when there are more data points per pixel in _ensureSampledTimeSeries.
     99
     100        * public/v3/index.html: Added. Loads bundled-scripts.js if it exists, or individual script files otherwise.
     101
     102        * public/v3/instrumentation.js: Added. This class is used to gather runtime statistics of v3 UI. (It measures
     103        the performance of the perf dashboard UI).
     104
     105        * public/v3/main.js: Added. Bootstraps the app.
     106        (main):
     107        (fetchManifest):
     108
     109        * public/v3/models: Added.
     110        * public/v3/models/analysis-task.js: Added.
     111        * public/v3/models/bug-tracker.js: Added.
     112        * public/v3/models/bug.js: Added.
     113        * public/v3/models/builder.js: Added.
     114        * public/v3/models/commit-log.js: Added.
     115        * public/v3/models/data-model.js: Added.
     116        (DataModelObject): The base class for various data objects that correspond to database tables. It supplies static
     117        hash map to find entries by id as well as other keys.
     118        (LabeledObject): A subclass of DataModelObject with the capability to find an object via its name.
     119
     120        * public/v3/models/measurement-cluster.js: Added.
     121        (MeasurementCluster): Represents a single cluster or a chunk of data in a measurement set.
     122
     123        * public/v3/models/measurement-set.js: Added.
     124        (MeasurementSet): Represents a measurement set.
     125        (MeasurementSet.findSet): Returns the singleton set given (metric, platform). We use singleton to avoid issuing
     126        multiple HTTP requests for the same JSON when there are multiple TimeSeriesChart that show the same graph (e.g. on
     127        charts page with overview and main charts).
     128        (MeasurementSet.prototype.findClusters): Finds the list of clusters to fetch in a given time range.
     129        (MeasurementSet.prototype.fetchBetween): Fetch clusters for a given time range and calls callback whenever new data
     130        arrives. The number of callbacks depends on the how many clusters need to be newly fetched.
     131        (MeasurementSet.prototype._fetchSecondaryClusters): Fetches non-primary (non-latest) clusters.
     132        (MeasurementSet.prototype._fetch): Issues a HTTP request to fetch a cluster.
     133        (MeasurementSet.prototype._didFetchJSON): Called when a cluster is fetched.
     134        (MeasurementSet.prototype._failedToFetchJSON): Called when the fetching of a cluster has failed.
     135        (MeasurementSet.prototype._invokeCallbacks): Invokes callbacks upon an approval of a new cluster.
     136        (MeasurementSet.prototype._addFetchedCluster): Adds the newly fetched cluster in the order.
     137        (MeasurementSet.prototype.fetchedTimeSeries): Returns a time series that contains data from all clusters that have
     138        been fetched.
     139        (TimeSeries.prototype.findById): Additions to TimeSeries defined in /v2/data.js.
     140        (TimeSeries.prototype.dataBetweenPoints): Ditto.
     141        (TimeSeries.prototype.firstPoint): Ditto.
     142
     143        * public/v3/models/metric.js: Added.
     144        * public/v3/models/platform.js: Added.
     145        * public/v3/models/repository.js: Added.
     146        * public/v3/models/test.js: Added.
     147
     148        * public/v3/pages: Added.
     149        * public/v3/pages/analysis-category-page.js: Added. The "Analysis" page that lists the analysis tasks.
     150        * public/v3/pages/analysis-category-toolbar.js: Added. The toolbar to filter analysis tasks based on its category
     151        (unconfirmed, bisecting, identified, closed) and a keyword.
     152
     153        * public/v3/pages/analysis-task-page.js: Added. Not implemented yet. It just has the hyperlink to the v2  UI.
     154
     155        * public/v3/pages/chart-pane-status-view.js: Added.
     156        (ChartPaneStatusView): A subclass of ChartStatusView used in the charts page. In addition to the current value,
     157        comparison to baseline/target, it shows the list of repository revisions (e.g. WebKit revision, OS version).
     158
     159        * public/v3/pages/chart-pane.js: Added.
     160        (ChartPane): A component a pane in the charts page. Each pane has the overview chart and the main chart. The zooming
     161        is synced across all panes in the charts page.
     162
     163        * public/v3/pages/charts-page.js: Added. Charts page.
     164        * public/v3/pages/charts-toolbar.js: Added. The toolbar to set the number of days to show. This affects the overview
     165        chart's domain in each pane.
     166
     167        * public/v3/pages/create-analysis-task-page.js: Added.
     168        (CreateAnalysisTaskPage): A page that gets shown momentarily while creating a new analysis task.
     169
     170        * public/v3/pages/dashboard-page.js: Added. A dashboard page.
     171        * public/v3/pages/dashboard-toolbar.js: Added. Its toolbar with buttons to select the number of days to show.
     172        * public/v3/pages/domain-control-toolbar.js: Added. An abstract superclass of charts and dashboard toolbars.
     173
     174        * public/v3/pages/heading.js: Added. A component for displaying the header and toolbar, if exists, on each page.
     175        * public/v3/pages/page-router.js: Added. This class is responsible for updating the URL hashes as well as opening
     176        and updating each page when the hash changes (via back/forward navigation).
     177        * public/v3/pages/page-with-charts.js: Added. An abstract subclass of page used by dashboards and charts page.
     178        Supplies helper functions for creating TimeSeriesChart options.
     179        * public/v3/pages/page-with-heading.js: Added. An abstract subclass of page that uses the heading component.
     180        * public/v3/pages/page.js: Added. The Page component.
     181        * public/v3/pages/toolbar.js: Added. An abstract toolbar component.
     182
     183        * public/v3/remote.js: Added.
     184        (getJSON): Fetches JSON from the remote server.
     185        (getJSONWithStatus): Ditto. Rejects the response if the status is not "OK".
     186        (PrivilegedAPI.sendRequest): Posts a HTTP request to a privileged API in /privileged-api/.
     187        (PrivilegedAPI.requestCSRFToken): Creates a new CSRF token to request a privileged API post.
     188
     189        * tools/bundle-v3-scripts.py: Added.
     190        (main): Bundles js files together and minifies them by jsmin.py for the v3 UI. Without this script, we're forced to
     191        download 44 JS files or making each JS file contain multiple classes.
     192
     193        * tools/jsmin.py: Copied from WebInspector / JavaScriptCore code.
     194
    11952015-12-15  Ryosuke Niwa  <rniwa@webkit.org>
    2196
  • trunk/Websites/perf.webkit.org/public/api/analysis-tasks.php

    r183232 r194130  
    1111        exit_with_error('InvalidRequest');
    1212
    13     if (count($path) > 0 && $path[0]) {
    14         $task_id = intval($path[0]);
     13    $task_id = count($path) > 0 && $path[0] ? $path[0] : array_get($_GET, 'id');
     14
     15    if ($task_id) {
     16        $task_id = intval($task_id);
    1517        $task = $db->select_first_row('analysis_tasks', 'task', array('id' => $task_id));
    1618        if (!$task)
     
    7476    }
    7577
     78    $run_ids = array();
     79    $task_by_run = array();
     80    foreach ($tasks as &$task) {
     81        if ($task['startRun']) {
     82            array_push($run_ids, $task['startRun']);
     83            $task_by_run[$task['startRun']] = &$task;
     84        }
     85        if ($task['endRun']) {
     86            array_push($run_ids, $task['endRun']);
     87            $task_by_run[$task['endRun']] = &$task;
     88        }
     89    }
     90
     91    // FIXME: This query is quite expensive. We may need to store this directly in analysis_tasks table instead.
     92    $build_revision_times = $db->query_and_fetch_all('SELECT run_id, build_time, max(commit_time) AS revision_time
     93            FROM builds
     94                LEFT OUTER JOIN build_commits ON commit_build = build_id
     95                LEFT OUTER JOIN commits ON build_commit = commit_id, test_runs
     96            WHERE run_build = build_id AND run_id = ANY($1) GROUP BY build_id, run_id',
     97        array('{' . implode(', ', $run_ids) . '}'));
     98    foreach ($build_revision_times as &$row) {
     99        $time = $row['revision_time'] or $row['build_time'];
     100        $id = $row['run_id'];
     101        if ($task_by_run[$id]['startRun'] == $id)
     102            $task_by_run[$id]['startRunTime'] = Database::to_js_time($time);
     103        if ($task_by_run[$id]['endRun'] == $id)
     104            $task_by_run[$id]['endRunTime'] = Database::to_js_time($time);
     105    }
     106
    76107    return $bugs;
    77108}
     
    89120        'startRun' => $task_row['task_start_run'],
    90121        'endRun' => $task_row['task_end_run'],
     122        'category' => $task_row['task_result'] ? 'bisecting' : 'unconfirmed',
    91123        'result' => $task_row['task_result'],
    92124        'needed' => $task_row['task_needed'] ? Database::is_true($task_row['task_needed']) : null,
  • trunk/Websites/perf.webkit.org/public/include/json-header.php

    r183237 r194130  
    5454function require_format($name, $value, $pattern) {
    5555    if (!preg_match($pattern, $value))
    56         exit_with_error('Invalid' . $name, array('value' => $value));
     56        exit_with_error('Invalid' . camel_case_words_separated_by_underscore($name), array('value' => $value));
    5757}
    5858
    5959function require_match_one_of_values($name, $value, $valid_values) {
    6060    if (!in_array($value, $valid_values))
    61         exit_with_error('Invalid' . $name, array('value' => $value));
     61        exit_with_error('Invalid' . camel_case_words_separated_by_underscore($name), array('value' => $value));
     62}
     63
     64function validate_arguments($array, $list_of_arguments) {
     65    $result = array();
     66    foreach ($list_of_arguments as $name => $pattern) {
     67        $value = array_get($array, $name, '');
     68        if ($pattern == 'int') {
     69            require_format($name, $value, '/^\d+$/');
     70            $value = intval($value);
     71        } else if ($pattern == 'int?') {
     72            require_format($name, $value, '/^\d*$/');
     73            $value = $value ? intval($value) : null;
     74        } else
     75            require_format($name, $value, $pattern);
     76        $result[$name] = $value;
     77    }
     78    return $result;
    6279}
    6380
Note: See TracChangeset for help on using the changeset viewer.