Changeset 180468 in webkit
- Timestamp:
- Feb 20, 2015 5:28:34 PM (9 years ago)
- Location:
- trunk/Websites/perf.webkit.org
- Files:
-
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Websites/perf.webkit.org/ChangeLog
r180466 r180468 1 2015-02-20 Ryosuke Niwa <rniwa@webkit.org> 2 3 Loading the perf dashboard takes multiple seconds 4 https://bugs.webkit.org/show_bug.cgi?id=141860 5 6 Reviewed by Andreas Kling. 7 8 This patch introduces the caches of JSON files returned by /api/ in /data/ directory. It also records 9 the last time test_runs rows associated with the requested platforms and metrics are inserted, updated, 10 or removed in the caches as well as the manifest JSON files ("last modified time"). Because the manifest 11 is regenerated each time a new test result is reported, the front end can compare last modified time in 12 the manifest file with that in a /api/runs JSON cache to detect the stale-ness. 13 14 More concretely, the front end first optimistically fetches the JSON in /data/. If the cache doesn't exit 15 or the last modified time in the cache doesn't match with that in the manifest file, it would fetch it 16 again via /api/runs. In the case the cache did exist, we render the charts based on the cache meanwhile. 17 This dramatically reduces the perceived latency for the page load since charts are drawn immediately using 18 the cache and we would only re-render the charts as new up-to-date JSON comes in. 19 20 This patch also changes the format of runs JSONs by pushing the exiting properties into 'configurations' 21 and adding 'lastModified' and 'elapsedTime' at the top level. 22 23 * init-database.sql: Added config_runs_last_modified to test_configurations table as well as a trigger to 24 auto-update this column upon changes to test_runs table. 25 26 * public/admin/test-configurations.php: 27 (add_run): Regenerate the manifest file to invalidate the /api/runs JSON cache. 28 (delete_run): Ditto. 29 30 * public/api/runs.php: 31 (main): Fetch all columns of test_configurations table including config_runs_last_modified. Also generate 32 the cache in /data/ directory. 33 (RunsGenerator::__construct): Compute the last modified time for this (platform, metric) pair. 34 (RunsGenerator::results): Put the old content in 'configurations' property and include 'lastModified' and 35 'elapsedTime' properties. 'elapsedTime' is added for debugging purposes. 36 (RunsGenerator::add_runs): 37 (RunsGenerator::parse_revisions_array): 38 39 * public/include/db.php: 40 (CONFIG_DIR): Added. 41 (generate_data_file): Added based on ManifestGenerator::store. 42 (Database::to_js_time): Extracted from RunsGenerator::add_runs to share code. 43 44 * public/include/json-header.php: 45 (echo_success): Renamed from success_json. Return the serialized JSON instead of echo'ing it so that we can 46 generate caches in /api/runs/. 47 (exit_with_success): 48 49 * public/include/manifest.php: 50 (ManifestGenerator::generate): Added 'elapsedTime' property for the time taken to generate the manifest. 51 It seems like we're generating it in 200-300ms for now so that's good. 52 (ManifestGenerator::store): Uses generate_data_file. 53 (ManifestGenerator::platforms): Added 'lastModified' array to each platform entry. This array contains the 54 last modified time for each (platform, metric) pair. 55 56 * public/index.html: 57 (fetchTest): Updated per the format change in runs JSON. 58 59 * public/v2/app.js: 60 (App.Pane._fetch): Fetch the cached JSON first. Refetch the uncached version if instructed as such. 61 (App.Pane._updateChartData): Extracted from App.Pane._fetch. 62 (App.Pane._handleFetchErrors): Ditto. 63 64 * public/v2/data.js: 65 (RunsData.fetchRuns): Takes the fourth argument indicating whether we should fetch the cached version or not. 66 The cached JSON is located in /data/ with the same filename. When fetching a cached JSON results in 404, 67 fulfill the promise with null as the result instead of rejecting it. The only client of this function which 68 sets useCache to true is App.Manifest.fetchRunsWithPlatformAndMetric, and it handles this special case. 69 70 * public/v2/manifest.js: 71 (App.DateArrayTransform): Added. Handles the array of last modified dates in platform objects. 72 (App.Platform.lastModifiedTimeForMetric): Added. Returns the last modified date in the manifest JSON. 73 (App.Manifest.fetchRunsWithPlatformAndMetric): Takes "useCache" like RunsData.fetchRuns. Set shouldRefetch 74 to true if response is null (the cache didn't exit) or the cache is out-of-date. 75 (App.Manifest._formatFetchedData): Extracted from App.Manifest.fetchRunsWithPlatformAndMetric. 76 77 * run-tests.js: 78 (initializeDatabase): Avoid splitting function definitions in the middle. 79 80 * tests/api-report.js: Added tests to verify that reporting new test results updates the last modified time 81 in test_configurations. 82 1 83 2015-02-20 Ryosuke Niwa <rniwa@webkit.org> 2 84 -
trunk/Websites/perf.webkit.org/init-database.sql
r178234 r180468 36 36 CREATE TABLE repositories ( 37 37 repository_id serial PRIMARY KEY, 38 repository_parent integer REFERENCES repositories ON DELETE CASCADE, 38 39 repository_name varchar(64) NOT NULL, 39 40 repository_url varchar(1024), 40 repository_blame_url varchar(1024)); 41 repository_blame_url varchar(1024), 42 CONSTRAINT repository_name_must_be_unique UNIQUE(repository_parent, repository_name)); 41 43 42 44 CREATE TABLE bug_trackers ( … … 122 124 config_type test_configuration_type NOT NULL, 123 125 config_is_in_dashboard boolean NOT NULL DEFAULT FALSE, 126 config_runs_last_modified timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'), 124 127 CONSTRAINT configuration_must_be_unique UNIQUE(config_metric, config_platform, config_type)); 125 128 CREATE INDEX config_platform_index ON test_configurations(config_platform); … … 144 147 iteration_relative_time float, 145 148 PRIMARY KEY (iteration_run, iteration_order)); 149 150 CREATE OR REPLACE FUNCTION update_config_last_modified() RETURNS TRIGGER AS $update_config_last_modified$ 151 BEGIN 152 IF TG_OP != 'DELETE' THEN 153 UPDATE test_configurations SET config_runs_last_modified = (CURRENT_TIMESTAMP AT TIME ZONE 'UTC') WHERE config_id = NEW.run_config; 154 ELSE 155 UPDATE test_configurations SET config_runs_last_modified = (CURRENT_TIMESTAMP AT TIME ZONE 'UTC') WHERE config_id = OLD.run_config; 156 END IF; 157 RETURN NULL; 158 END; 159 $update_config_last_modified$ LANGUAGE plpgsql; 160 161 CREATE TRIGGER update_config_last_modified AFTER INSERT OR UPDATE OR DELETE ON test_runs 162 FOR EACH ROW EXECUTE PROCEDURE update_config_last_modified(); 146 163 147 164 CREATE TABLE reports ( -
trunk/Websites/perf.webkit.org/public/admin/test-configurations.php
r163688 r180468 30 30 $db->commit_transaction(); 31 31 notice("Added a baseline test run."); 32 33 regenerate_manifest(); 32 34 } 33 35 … … 66 68 67 69 $db->commit_transaction(); 70 71 regenerate_manifest(); 68 72 } 69 73 -
trunk/Websites/perf.webkit.org/public/api/runs.php
r180000 r180468 38 38 $platform_id = intval($parts[0]); 39 39 $metric_id = intval($parts[1]); 40 $config_rows = $db->query_and_fetch_all('SELECT config_id, config_type, config_platform, config_metric40 $config_rows = $db->query_and_fetch_all('SELECT * 41 41 FROM test_configurations WHERE config_metric = $1 AND config_platform = $2', array($metric_id, $platform_id)); 42 42 if (!$config_rows) … … 53 53 } 54 54 55 $generator = new RunsGenerator( );55 $generator = new RunsGenerator($config_rows); 56 56 57 57 foreach ($config_rows as $config) { … … 63 63 } 64 64 65 exit_with_success($generator->results()); 65 $content = success_json($generator->results()); 66 if (!$test_group_id) 67 generate_data_file("$platform_id-$metric_id.json", $content); 68 echo $content; 66 69 } 67 70 68 71 class RunsGenerator { 69 function __construct( ) {72 function __construct($config_rows) { 70 73 $this->results = array(); 74 $last_modified_times = array(); 75 foreach ($config_rows as $row) 76 array_push($last_modified_times, Database::to_js_time($row['config_runs_last_modified'])); 77 $this->last_modified = max($last_modified_times); 78 $this->start_time = microtime(true); 71 79 } 72 80 73 function &results() { return $this->results; } 81 function results() { 82 return array( 83 'configurations' => &$this->results, 84 'lastModified' => $this->last_modified, 85 'elapsedTime' => microtime(true) - $this->start_time); 86 } 74 87 75 88 function add_runs($name, $raw_runs) { … … 92 105 'revisions' => self::parse_revisions_array($run['revisions']), 93 106 'build' => $run['build_id'], 94 'buildTime' => strtotime($run['build_time']) * 1000,107 'buildTime' => Database::to_js_time($run['build_time']), 95 108 'buildNumber' => intval($run['build_number']), 96 109 'builder' => $run['build_builder']); … … 105 118 if (!$name_and_revision[0]) 106 119 continue; 107 $time = strtotime(trim($name_and_revision[2], '"')) * 1000;120 $time = Database::to_js_time(trim($name_and_revision[2], '"')); 108 121 $revisions[trim($name_and_revision[0], '"')] = array(trim($name_and_revision[1], '"'), $time); 109 122 } -
trunk/Websites/perf.webkit.org/public/include/db.php
r178234 r180468 28 28 $_config = NULL; 29 29 30 define('CONFIG_DIR', dirname(__FILE__) . '/../../'); 31 30 32 function config($key) { 31 33 global $_config; 32 34 if (!$_config) 33 $_config = json_decode(file_get_contents( dirname(__FILE__) . '/../../config.json'), true);35 $_config = json_decode(file_get_contents(CONFIG_DIR . 'config.json'), true); 34 36 return $_config[$key]; 37 } 38 39 function generate_data_file($filename, $content) { 40 if (!assert(ctype_alnum(str_replace(array('-', '_', '.'), '', $filename)))) 41 return FALSE; 42 return file_put_contents(CONFIG_DIR . config('dataDirectory') . '/' . $filename, $content); 35 43 } 36 44 … … 55 63 function is_true($value) { 56 64 return $value == 't'; 65 } 66 67 static function to_js_time($time_str) { 68 return strtotime($time_str) * 1000; 57 69 } 58 70 -
trunk/Websites/perf.webkit.org/public/include/json-header.php
r178234 r180468 14 14 } 15 15 16 function echo_success($details = array()) {16 function success_json($details = array()) { 17 17 $details['status'] = 'OK'; 18 18 merge_additional_details($details); 19 19 20 echojson_encode($details);20 return json_encode($details); 21 21 } 22 22 23 23 function exit_with_success($details = array()) { 24 echo _success($details);24 echo success_json($details); 25 25 exit(0); 26 26 } -
trunk/Websites/perf.webkit.org/public/include/manifest.php
r179764 r180468 13 13 14 14 function generate() { 15 $start_time = microtime(true); 16 15 17 $config_table = $this->db->fetch_table('test_configurations'); 16 18 $platform_table = $this->db->fetch_table('platforms'); … … 35 37 'dashboards' => config('dashboards'), 36 38 ); 37 return $this->manifest; 39 40 $this->manifest['elapsedTime'] = (microtime(true) - $start_time) * 1000; 41 42 return TRUE; 38 43 } 39 44 40 45 function store() { 41 return file_put_contents(self::MANIFEST_PATH, json_encode($this->manifest));46 return generate_data_file('manifest.json', json_encode($this->manifest)); 42 47 } 43 48 … … 78 83 continue; 79 84 85 $new_last_modified = array_get($config_row, 'config_runs_last_modified', 0); 86 if ($new_last_modified) 87 $new_last_modified = strtotime($config_row['config_runs_last_modified']) * 1000; 88 80 89 $platform = &array_ensure_item_has_array($platform_metrics, $config_row['config_platform']); 81 if (!in_array($config_row['config_metric'], $platform)) 82 array_push($platform, $config_row['config_metric']); 90 $metrics = &array_ensure_item_has_array($platform, 'metrics'); 91 $last_modified = &array_ensure_item_has_array($platform, 'last_modified'); 92 93 $metric_id = $config_row['config_metric']; 94 $index = array_search($metric_id, $metrics); 95 if ($index === FALSE) { 96 array_push($metrics, $metric_id); 97 array_push($last_modified, $new_last_modified); 98 } else 99 $last_modified[$index] = max($last_modified[$index], $new_last_modified); 83 100 } 84 101 } 102 $configurations = array(); 103 85 104 $platforms = array(); 86 105 if ($platform_table) { … … 89 108 continue; 90 109 $id = $platform_row['platform_id']; 91 if (array_key_exists($id, $platform_metrics)) 92 $platforms[$id] = array('name' => $platform_row['platform_name'], 'metrics' => $platform_metrics[$id]); 110 if (array_key_exists($id, $platform_metrics)) { 111 $platforms[$id] = array( 112 'name' => $platform_row['platform_name'], 113 'metrics' => $platform_metrics[$id]['metrics'], 114 'lastModified' => $platform_metrics[$id]['last_modified']); 115 } 93 116 } 94 117 } -
trunk/Websites/perf.webkit.org/public/index.html
r175006 r180468 841 841 } 842 842 843 $.getJSON('api/runs/' + filename, function (data) { 843 $.getJSON('api/runs/' + filename, function (response) { 844 var data = response.configurations; 844 845 callback(createRunAndResults(data.current), createRunAndResults(data.baseline), createRunAndResults(data.target)); 845 846 }); -
trunk/Websites/perf.webkit.org/public/v2/app.js
r180451 r180468 350 350 this.set('failure', metricId ? 'Invalid metric id:' + metricId : 'Metric id was not specified'); 351 351 else { 352 var self = this; 353 354 App.Manifest.fetchRunsWithPlatformAndMetric(this.get('store'), platformId, metricId).then(function (result) { 355 self.set('platform', result.platform); 356 self.set('metric', result.metric); 357 self.set('chartData', result.data); 358 self._updateMovingAverageAndEnvelope(); 359 }, function (result) { 360 if (!result || typeof(result) === "string") 361 self.set('failure', 'Failed to fetch the JSON with an error: ' + result); 362 else if (!result.platform) 363 self.set('failure', 'Could not find the platform "' + platformId + '"'); 364 else if (!result.metric) 365 self.set('failure', 'Could not find the metric "' + metricId + '"'); 366 else 367 self.set('failure', 'An internal error'); 368 }); 369 352 var store = this.get('store'); 353 var updateChartData = this._updateChartData.bind(this); 354 var handleErrors = this._handleFetchErrors.bind(this, platformId, metricId); 355 var useCache = true; 356 App.Manifest.fetchRunsWithPlatformAndMetric(store, platformId, metricId, null, useCache).then(function (result) { 357 updateChartData(result); 358 if (!result.shouldRefetch) 359 return; 360 361 useCache = false; 362 App.Manifest.fetchRunsWithPlatformAndMetric(store, platformId, metricId, null, useCache) 363 .then(updateChartData, handleErrors); 364 }, handleErrors); 370 365 this.fetchAnalyticRanges(); 371 366 } 372 367 }.observes('platformId', 'metricId').on('init'), 368 _updateChartData: function (result) 369 { 370 this.set('platform', result.platform); 371 this.set('metric', result.metric); 372 this.set('chartData', result.data); 373 this._updateMovingAverageAndEnvelope(); 374 }, 375 _handleFetchErrors: function (platformId, metricId, result) 376 { 377 console.log(platformId, metricId, result) 378 if (!result || typeof(result) === "string") 379 this.set('failure', 'Failed to fetch the JSON with an error: ' + result); 380 else if (!result.platform) 381 this.set('failure', 'Could not find the platform "' + platformId + '"'); 382 else if (!result.metric) 383 this.set('failure', 'Could not find the metric "' + metricId + '"'); 384 else 385 this.set('failure', 'An internal error'); 386 }, 373 387 fetchAnalyticRanges: function () 374 388 { -
trunk/Websites/perf.webkit.org/public/v2/data.js
r180365 r180468 326 326 // FIXME: We need to devise a way to fetch runs in multiple chunks so that 327 327 // we don't have to fetch the entire time series to just show the last 3 days. 328 RunsData.fetchRuns = function (platformId, metricId, testGroupId) 329 { 330 var filename = platformId + '-' + metricId + '.json'; 331 328 RunsData.fetchRuns = function (platformId, metricId, testGroupId, useCache) 329 { 330 var url = useCache ? '../data/' : '../api/runs/'; 331 332 url += platformId + '-' + metricId + '.json'; 332 333 if (testGroupId) 333 filename+= '?testGroup=' + testGroupId;334 url += '?testGroup=' + testGroupId; 334 335 335 336 return new Ember.RSVP.Promise(function (resolve, reject) { 336 $.getJSON( '../api/runs/' + filename, function (data) {337 if ( data.status != 'OK') {338 reject( data.status);337 $.getJSON(url, function (response) { 338 if (response.status != 'OK') { 339 reject(response.status); 339 340 return; 340 341 } 341 delete data.status; 342 342 delete response.status; 343 344 var data = response.configurations; 343 345 for (var config in data) 344 346 data[config] = new RunsData(data[config]); 345 346 resolve(data); 347 348 if (response.lastModified) 349 response.lastModified = new Date(response.lastModified); 350 351 resolve(response); 347 352 }).fail(function (xhr, status, error) { 348 reject(xhr.status + (error ? ', ' + error : '')); 353 if (xhr.status == 404 && useCache) 354 resolve(null); 355 else 356 reject(xhr.status + (error ? ', ' + error : '')); 349 357 }) 350 358 }); -
trunk/Websites/perf.webkit.org/public/v2/manifest.js
r180333 r180468 35 35 fullName: function () 36 36 { 37 return this.get('path').join(' \u220b ') /* & ni; */37 return this.get('path').join(' \u220b ') /* ∈ */ 38 38 + ' : ' + this.get('label'); 39 39 }.property('path', 'label'), … … 55 55 }); 56 56 57 App.DateArrayTransform = DS.Transform.extend({ 58 deserialize: function (serialized) 59 { 60 return serialized.map(function (time) { return new Date(time); }); 61 } 62 }); 63 57 64 App.Platform = App.NameLabelModel.extend({ 58 65 _metricSet: null, 59 66 _testSet: null, 60 67 metrics: DS.hasMany('metric'), 68 lastModified: DS.attr('dateArray'), 61 69 containsMetric: function (metric) 62 70 { … … 64 72 this._metricSet = new Ember.Set(this.get('metrics')); 65 73 return this._metricSet.contains(metric); 74 }, 75 lastModifiedTimeForMetric: function (metric) 76 { 77 var index = this.get('metrics').indexOf(metric); 78 if (index < 0) 79 return null; 80 return this.get('lastModified').objectAt(index); 66 81 }, 67 82 containsTest: function (test) … … 280 295 this._defaultDashboardName = dashboards.length ? dashboards[0].get('name') : null; 281 296 }, 282 fetchRunsWithPlatformAndMetric: function (store, platformId, metricId, testGroupId) 283 { 297 fetchRunsWithPlatformAndMetric: function (store, platformId, metricId, testGroupId, useCache) 298 { 299 Ember.assert("Can't cache results for test groups", !(testGroupId && useCache)); 300 var self = this; 284 301 return Ember.RSVP.all([ 285 RunsData.fetchRuns(platformId, metricId, testGroupId ),302 RunsData.fetchRuns(platformId, metricId, testGroupId, useCache), 286 303 this.fetch(store), 287 304 ]).then(function (values) { 288 var r uns= values[0];305 var response = values[0]; 289 306 290 307 var platform = App.Manifest.platform(platformId); 291 308 var metric = App.Manifest.metric(metricId); 292 309 293 var suffix = metric.get('name').match('([A-z][a-z]+|FrameRate)$')[0];294 var unit = {295 'FrameRate': 'fps',296 'Runs': '/s',297 'Time': 'ms',298 'Malloc': 'bytes',299 'Heap': 'bytes',300 'Allocations': 'bytes'301 }[suffix];302 var smallerIsBetter = unit != 'fps' && unit != '/s'; // Assume smaller is better for unit-less metrics.303 304 var useSI = unit == 'bytes';305 var unitSuffix = unit ? ' ' + unit : '';306 var deltaFormatterWithoutSign = useSI ? d3.format('.2s') : d3.format('.2g');307 310 return { 308 311 platform: platform, 309 312 metric: metric, 310 data: { 311 current: runs.current.timeSeriesByCommitTime(), 312 baseline: runs.baseline ? runs.baseline.timeSeriesByCommitTime() : null, 313 target: runs.target ? runs.target.timeSeriesByCommitTime() : null, 314 unit: unit, 315 formatWithUnit: function (value) { return this.formatter(value) + unitSuffix; }, 316 formatWithDeltaAndUnit: function (value, delta) 317 { 318 return this.formatter(value) + (delta && !isNaN(delta) ? ' \u00b1 ' + deltaFormatterWithoutSign(delta) : '') + unitSuffix; 319 }, 320 formatter: useSI ? d3.format('.4s') : d3.format('.4g'), 321 deltaFormatter: useSI ? d3.format('+.2s') : d3.format('+.2g'), 322 smallerIsBetter: smallerIsBetter, 323 } 313 data: response ? self._formatFetchedData(metric.get('name'), response.configurations) : null, 314 shouldRefetch: !response || +response.lastModified < +platform.lastModifiedTimeForMetric(metric), 324 315 }; 325 316 }); 326 317 }, 318 _formatFetchedData: function (metricName, configurations) 319 { 320 var suffix = metricName.match('([A-z][a-z]+|FrameRate)$')[0]; 321 var unit = { 322 'FrameRate': 'fps', 323 'Runs': '/s', 324 'Time': 'ms', 325 'Malloc': 'bytes', 326 'Heap': 'bytes', 327 'Allocations': 'bytes' 328 }[suffix]; 329 330 var smallerIsBetter = unit != 'fps' && unit != '/s'; // Assume smaller is better for unit-less metrics. 331 332 var useSI = unit == 'bytes'; 333 var unitSuffix = unit ? ' ' + unit : ''; 334 var deltaFormatterWithoutSign = useSI ? d3.format('.2s') : d3.format('.2g'); 335 336 return { 337 current: configurations.current.timeSeriesByCommitTime(), 338 baseline: configurations.baseline ? configurations.baseline.timeSeriesByCommitTime() : null, 339 target: configurations.target ? configurations.target.timeSeriesByCommitTime() : null, 340 unit: unit, 341 formatWithUnit: function (value) { return this.formatter(value) + unitSuffix; }, 342 formatWithDeltaAndUnit: function (value, delta) 343 { 344 return this.formatter(value) + (delta && !isNaN(delta) ? ' \u00b1 ' + deltaFormatterWithoutSign(delta) : '') + unitSuffix; 345 }, 346 formatter: useSI ? d3.format('.4s') : d3.format('.4g'), 347 deltaFormatter: useSI ? d3.format('+.2s') : d3.format('+.2g'), 348 smallerIsBetter: smallerIsBetter, 349 }; 350 } 327 351 }).create(); -
trunk/Websites/perf.webkit.org/run-tests.js
r174459 r180468 200 200 var firstError; 201 201 var queue = new TaskQueue(); 202 commaSeparatedSqlStatements.split(/;\s* /).forEach(function (statement) {202 commaSeparatedSqlStatements.split(/;\s*(?=CREATE|DROP)/).forEach(function (statement) { 203 203 queue.addTask(function (error, callback) { 204 204 client.query(statement, function (error) { -
trunk/Websites/perf.webkit.org/tests/api-report.js
r177614 r180468 688 688 }); 689 689 }); 690 691 var reportsUpdatingDifferentTests = [ 692 [{ 693 "buildNumber": "123", 694 "buildTime": "2013-02-28T10:12:03", 695 "builderName": "someBuilder", 696 "builderPassword": "somePassword", 697 "platform": "Mountain Lion", 698 "tests": {"test1": {"metrics": {"Time": {"current": 3}}}} 699 }], 700 [{ 701 "buildNumber": "124", 702 "buildTime": "2013-02-28T11:31:21", 703 "builderName": "someBuilder", 704 "builderPassword": "somePassword", 705 "platform": "Mountain Lion", 706 "tests": {"test2": {"metrics": {"Time": {"current": 3}}}} 707 }], 708 [{ 709 "buildNumber": "125", 710 "buildTime": "2013-02-28T12:45:34", 711 "builderName": "someBuilder", 712 "builderPassword": "somePassword", 713 "platform": "Mountain Lion", 714 "tests": {"test1": {"metrics": {"Time": {"current": 3}}}} 715 }], 716 ]; 717 718 function fetchTestConfig(testName, metricName, callback) { 719 queryAndFetchAll('SELECT * FROM tests, test_metrics, test_configurations WHERE test_id = metric_test AND metric_id = config_metric' 720 + ' AND test_name = $1 AND metric_name = $2', [testName, metricName], function (runRows) { 721 assert.equal(runRows.length, 1); 722 callback(runRows[0]); 723 }); 724 } 725 726 it("should update the last modified date of test configurations with new runs", function () { 727 addBuilder(reportsUpdatingDifferentTests[0], function () { 728 postJSON('/api/report/', reportsUpdatingDifferentTests[0], function (response) { 729 assert.equal(response.statusCode, 200); 730 fetchTestConfig('test1', 'Time', function (originalConfig) { 731 postJSON('/api/report/', reportsUpdatingDifferentTests[2], function (response) { 732 assert.equal(response.statusCode, 200); 733 fetchTestConfig('test1', 'Time', function (config) { 734 assert.notEqual(+originalConfig['config_runs_last_modified'], +config['config_runs_last_modified']); 735 notifyDone(); 736 }); 737 }); 738 }); 739 }); 740 }); 741 }); 742 743 it("should update the last modified date of unrelated test configurations", function () { 744 addBuilder(reportsUpdatingDifferentTests[0], function () { 745 postJSON('/api/report/', reportsUpdatingDifferentTests[0], function (response) { 746 assert.equal(response.statusCode, 200); 747 fetchTestConfig('test1', 'Time', function (originalConfig) { 748 postJSON('/api/report/', reportsUpdatingDifferentTests[1], function (response) { 749 assert.equal(response.statusCode, 200); 750 fetchTestConfig('test1', 'Time', function (config) { 751 assert.equal(+originalConfig['config_runs_last_modified'], +config['config_runs_last_modified']); 752 notifyDone(); 753 }); 754 }); 755 }); 756 }); 757 }); 758 }); 690 759 });
Note: See TracChangeset
for help on using the changeset viewer.