Perf dashboard should show the results of A/B testing
[WebKit-https.git] / Websites / perf.webkit.org / public / api / runs.php
1 <?php
2
3 require('../include/json-header.php');
4
5 function fetch_runs_for_config_and_test_group($db, $config, $test_group_id) {
6     return $db->query_and_fetch_all('
7         SELECT test_runs.*, builds.*, array_agg((commit_repository, commit_revision, commit_time)) AS revisions
8             FROM builds
9                 LEFT OUTER JOIN build_commits ON commit_build = build_id
10                 LEFT OUTER JOIN commits ON build_commit = commit_id,
11                 test_runs, build_requests, analysis_test_groups
12             WHERE run_build = build_id AND run_config = $1 AND request_build = build_id AND request_group = $2
13             GROUP BY build_id, run_id', array($config['config_id'], $test_group_id));
14 }
15
16 function fetch_runs_for_config($db, $config) {
17     return $db->query_and_fetch_all('
18         SELECT test_runs.*, builds.*, array_agg((commit_repository, commit_revision, commit_time)) AS revisions
19             FROM builds
20                 LEFT OUTER JOIN build_commits ON commit_build = build_id
21                 LEFT OUTER JOIN commits ON build_commit = commit_id, test_runs
22             WHERE run_build = build_id AND run_config = $1 AND NOT EXISTS (SELECT * FROM build_requests WHERE request_build = build_id)
23             GROUP BY build_id, run_id', array($config['config_id']));
24 }
25
26 function main($path) {
27     if (count($path) != 1)
28         exit_with_error('InvalidRequest');
29
30     $parts = explode('-', $path[0]);
31     if (count($parts) != 2)
32         exit_with_error('InvalidRequest');
33
34     $db = new Database;
35     if (!$db->connect())
36         exit_with_error('DatabaseConnectionFailure');
37
38     $platform_id = intval($parts[0]);
39     $metric_id = intval($parts[1]);
40     $config_rows = $db->query_and_fetch_all('SELECT config_id, config_type, config_platform, config_metric
41         FROM test_configurations WHERE config_metric = $1 AND config_platform = $2', array($metric_id, $platform_id));
42     if (!$config_rows)
43         exit_with_error('ConfigurationNotFound');
44
45     $test_group_id = array_get($_GET, 'testGroup');
46     if ($test_group_id)
47         $test_group_id = intval($test_group_id);
48     else {
49         // FIXME: We should support revalication as well as caching results in the server side.
50         $maxage = config('jsonCacheMaxAge');
51         header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $maxage) . ' GMT');
52         header("Cache-Control: maxage=$maxage");
53     }
54
55     $generator = new RunsGenerator();
56
57     foreach ($config_rows as $config) {
58         if ($test_group_id)
59             $raw_runs = fetch_runs_for_config_and_test_group($db, $config, $test_group_id);
60         else
61             $raw_runs = fetch_runs_for_config($db, $config);
62         $generator->add_runs($config['config_type'], $raw_runs);
63     }
64
65     exit_with_success($generator->results());
66 }
67
68 class RunsGenerator {
69     function __construct() {
70         $this->results = array();
71     }
72
73     function &results() { return $this->results; }
74
75     function add_runs($name, $raw_runs) {
76         $formatted_runs = array();
77         if ($raw_runs) {
78             foreach ($raw_runs as $run)
79                 array_push($formatted_runs, self::format_run($run));
80         }
81         $this->results[$name] = $formatted_runs;
82         return $formatted_runs;
83     }
84
85     private static function format_run($run) {
86         return array(
87             'id' => intval($run['run_id']),
88             'mean' => floatval($run['run_mean_cache']),
89             'iterationCount' => intval($run['run_iteration_count_cache']),
90             'sum' => floatval($run['run_sum_cache']),
91             'squareSum' => floatval($run['run_square_sum_cache']),
92             'revisions' => self::parse_revisions_array($run['revisions']),
93             'build' => $run['build_id'],
94             'buildTime' => strtotime($run['build_time']) * 1000,
95             'buildNumber' => intval($run['build_number']),
96             'builder' => $run['build_builder']);
97     }
98
99     private static function parse_revisions_array($postgres_array) {
100         // e.g. {"(WebKit,131456,\"2012-10-16 14:53:00\")","(Chromium,162004,)"}
101         $outer_array = json_decode('[' . trim($postgres_array, '{}') . ']');
102         $revisions = array();
103         foreach ($outer_array as $item) {
104             $name_and_revision = explode(',', trim($item, '()'));
105             if (!$name_and_revision[0])
106                 continue;
107             $time = strtotime(trim($name_and_revision[2], '"')) * 1000;
108             $revisions[trim($name_and_revision[0], '"')] = array(trim($name_and_revision[1], '"'), $time);
109         }
110         return $revisions;
111     }
112 }
113
114 main(array_key_exists('PATH_INFO', $_SERVER) ? explode('/', trim($_SERVER['PATH_INFO'], '/')) : array());
115
116 ?>