blob: b006382595487ebdc39fe73b75eb2e6422b27af9 [file] [log] [blame]
App.Model = DS.Model;
App.NameLabelModel = DS.Model.extend({
name: DS.attr('string'),
label: function ()
{
return this.get('name');
}.property('name'),
});
App.Test = App.NameLabelModel.extend({
url: DS.attr('string'),
parent: DS.belongsTo('test', {inverse: 'childTests'}),
childTests: DS.hasMany('test', {inverse: 'parent'}),
metrics: DS.hasMany('metrics'),
});
App.Metric = App.NameLabelModel.extend({
test: DS.belongsTo('test'),
aggregator: DS.attr('string'),
label: function ()
{
return this.get('name') + (this.get('aggregator') ? ' : ' + this.get('aggregator') : '');
}.property('name', 'aggregator'),
path: function ()
{
var path = [];
var parent = this.get('test');
while (parent) {
path.push(parent.get('name'));
parent = parent.get('parent');
}
return path.reverse();
}.property('name', 'test'),
fullName: function ()
{
return this.get('path').join(' \u220b ') /* ∋ */
+ ' : ' + this.get('label');
}.property('path', 'label'),
childMetrics: function ()
{
var test = this.get('test');
var childMetrics = [];
test.get('childTests').forEach(function (childTest) {
childTest.get('metrics').forEach(function (metric) {
childMetrics.push(metric);
});
});
return childMetrics;
}.property('test.childTests'),
});
App.Builder = App.NameLabelModel.extend({
buildUrl: DS.attr('string'),
urlFromBuildNumber: function (buildNumber)
{
var template = this.get('buildUrl');
return template ? template.replace(/\$builderName/g, this.get('name')).replace(/\$buildNumber/g, buildNumber) : null;
}
});
App.BugTracker = App.NameLabelModel.extend({
bugUrl: DS.attr('string'),
newBugUrl: DS.attr('string'),
repositories: DS.hasMany('repository'),
urlFromBugNumber: function (bugNumber) {
var template = this.get('bugUrl');
return template && bugNumber ? template.replace(/\$number/g, bugNumber) : null;
}
});
App.DateArrayTransform = DS.Transform.extend({
deserialize: function (serialized)
{
return serialized.map(function (time) { return new Date(time); });
}
});
App.Platform = App.NameLabelModel.extend({
_metricSet: null,
_testSet: null,
metrics: DS.hasMany('metric'),
lastModified: DS.attr('dateArray'),
containsMetric: function (metric)
{
if (!this._metricSet)
this._metricSet = new Ember.Set(this.get('metrics'));
return this._metricSet.contains(metric);
},
lastModifiedTimeForMetric: function (metric)
{
var index = this.get('metrics').indexOf(metric);
if (index < 0)
return null;
return this.get('lastModified').objectAt(index);
},
containsTest: function (test)
{
if (!this._testSet) {
var set = new Ember.Set();
this.get('metrics').forEach(function (metric) {
for (var test = metric.get('test'); test; test = test.get('parent'))
set.push(test);
});
this._testSet = set;
}
return this._testSet.contains(test);
},
});
App.Repository = App.NameLabelModel.extend({
url: DS.attr('string'),
blameUrl: DS.attr('string'),
hasReportedCommits: DS.attr('boolean'),
urlForRevision: function (currentRevision)
{
return (this.get('url') || '').replace(/\$1/g, currentRevision);
},
urlForRevisionRange: function (from, to)
{
return (this.get('blameUrl') || '').replace(/\$1/g, from).replace(/\$2/g, to);
},
});
App.Dashboard = App.NameLabelModel.extend({
serialized: DS.attr('string'),
table: function ()
{
var json = this.get('serialized');
try {
var parsed = JSON.parse(json);
} catch (error) {
console.log("Failed to parse the grid:", error, json);
return [];
}
if (!parsed)
return [];
return this._normalizeTable(parsed);
}.property('serialized'),
rows: function ()
{
return this.get('table').slice(1);
}.property('table'),
headerColumns: function ()
{
var table = this.get('table');
if (!table || !table.length)
return [];
return table[0].map(function (name, index) {
return {label:name, index: index};
});
}.property('table'),
_normalizeTable: function (table)
{
var maxColumnCount = Math.max(table.map(function (column) { return column.length; }));
for (var i = 1; i < table.length; i++) {
var row = table[i];
for (var j = 1; j < row.length; j++) {
if (row[j] && !(row[j] instanceof Array)) {
console.log('Unrecognized (platform, metric) pair at column ' + i + ' row ' + j + ':' + row[j]);
row[j] = [];
}
}
}
return table;
},
});
App.MetricSerializer = App.PlatformSerializer = DS.RESTSerializer.extend({
normalizePayload: function (payload)
{
var results = {
platforms: this._normalizeIdMap(payload['all']),
builders: this._normalizeIdMap(payload['builders']),
tests: this._normalizeIdMap(payload['tests']).map(function (test) {
test['parent'] = test['parentId'];
return test;
}),
metrics: this._normalizeIdMap(payload['metrics']),
repositories: this._normalizeIdMap(payload['repositories']),
bugTrackers: this._normalizeIdMap(payload['bugTrackers']),
dashboards: [],
};
for (var testId in payload['tests']) {
var test = payload['tests'][testId];
var parent = payload['tests'][test['parent']];
if (!parent)
continue;
if (!parent['childTests'])
parent['childTests'] = [];
parent['childTests'].push(testId);
}
for (var metricId in payload['metrics']) {
var metric = payload['metrics'][metricId];
var test = payload['tests'][metric['test']];
if (!test['metrics'])
test['metrics'] = [];
test['metrics'].push(metricId);
}
var id = 1;
var dashboardsInPayload = payload['dashboards'];
for (var dashboardName in dashboardsInPayload) {
results['dashboards'].push({
id: id,
name: dashboardName,
serialized: JSON.stringify(dashboardsInPayload[dashboardName])
});
id++;
}
var siteTitle = payload['siteTitle'];
if (siteTitle) {
App.Manifest.set('siteTitle', siteTitle);
document.title = siteTitle;
}
return results;
},
_normalizeIdMap: function (idMap)
{
var results = [];
for (var id in idMap) {
var definition = idMap[id];
definition['id'] = id;
results.push(definition);
}
return results;
}
});
App.PlatformAdapter = DS.RESTAdapter.extend({
buildURL: function (type, id)
{
return '../data/manifest.json';
},
});
App.MetricAdapter = DS.RESTAdapter.extend({
buildURL: function (type, id)
{
return '../data/manifest.json';
},
});
App.Manifest = Ember.Controller.extend({
platforms: null,
topLevelTests: null,
repositories: [],
repositoriesWithReportedCommits: [],
bugTrackers: [],
_platformById: {},
_metricById: {},
_builderById: {},
_repositoryById: {},
_dashboardByName: {},
_defaultDashboardName: null,
_fetchPromise: null,
fetch: function (store)
{
if (!this._fetchPromise)
this._fetchPromise = store.findAll('platform').then(this._fetchedManifest.bind(this, store));
return this._fetchPromise;
},
isFetched: function () { return !!this.get('platforms'); }.property('platforms'),
platform: function (id) { return this._platformById[id]; },
metric: function (id) { return this._metricById[id]; },
builder: function (id) { return this._builderById[id]; },
repository: function (id) { return this._repositoryById[id]; },
dashboardByName: function (name) { return this._dashboardByName[name]; },
defaultDashboardName: function () { return this._defaultDashboardName; },
_fetchedManifest: function (store)
{
var startTime = Date.now();
var self = this;
var topLevelTests = [];
store.all('test').forEach(function (test) {
var parent = test.get('parent');
if (!parent)
topLevelTests.push(test);
});
this.set('topLevelTests', topLevelTests);
store.all('metric').forEach(function (metric) {
self._metricById[metric.get('id')] = metric;
});
var platforms = store.all('platform');
platforms.forEach(function (platform) {
self._platformById[platform.get('id')] = platform;
});
this.set('platforms', platforms);
store.all('builder').forEach(function (builder) {
self._builderById[builder.get('id')] = builder;
});
var repositories = store.all('repository');
repositories.forEach(function (repository) {
self._repositoryById[repository.get('id')] = repository;
});
this.set('repositories', repositories.sortBy('id'));
this.set('repositoriesWithReportedCommits',
repositories.filter(function (repository) { return repository.get('hasReportedCommits'); }));
this.set('bugTrackers', store.all('bugTracker').sortBy('name'));
var dashboards = store.all('dashboard').toArray();
this.set('dashboards', dashboards);
dashboards.forEach(function (dashboard) { self._dashboardByName[dashboard.get('name')] = dashboard; });
this._defaultDashboardName = dashboards.length ? dashboards[0].get('name') : null;
},
fetchRunsWithPlatformAndMetric: function (store, platformId, metricId, testGroupId, useCache)
{
Ember.assert("Can't cache results for test groups", !(testGroupId && useCache));
var self = this;
return Ember.RSVP.all([
RunsData.fetchRuns(platformId, metricId, testGroupId, useCache),
this.fetch(store),
]).then(function (values) {
var response = values[0];
var platform = App.Manifest.platform(platformId);
var metric = App.Manifest.metric(metricId);
return {
platform: platform,
metric: metric,
data: response ? self._formatFetchedData(metric.get('name'), response.configurations) : null,
shouldRefetch: !response || +response.lastModified < +platform.lastModifiedTimeForMetric(metric),
};
});
},
_makeFormatter: function (unit, sigFig, alwaysShowSign)
{
var isMiliseconds = false;
if (unit == 'ms') {
isMiliseconds = true;
unit = 's';
}
var divisor = unit == 'B' ? 1024 : 1000;
var suffix = ['\u03BC', 'm', '', 'K', 'M', 'G', 'T', 'P', 'E'];
var threshold = sigFig >= 3 ? divisor : (divisor / 10);
return function (value) {
var i;
var sign = value >= 0 ? (alwaysShowSign ? '+' : '') : '-';
value = Math.abs(value);
for (i = isMiliseconds ? 1 : 2; value < 1 && i > 0; i--)
value *= divisor;
for (; value >= threshold; i++)
value /= divisor;
return sign + value.toPrecision(Math.max(2, sigFig)) + ' ' + suffix[i] + (unit || '');
}
},
_formatFetchedData: function (metricName, configurations)
{
var unit = RunsData.unitFromMetricName(metricName);
var smallerIsBetter = RunsData.isSmallerBetter(unit);
var deltaFormatterWithoutSign = this._makeFormatter(unit, 2, false);
var currentTimeSeries = configurations.current.timeSeriesByCommitTime(false);
var baselineTimeSeries = configurations.baseline ? configurations.baseline.timeSeriesByCommitTime(false, true) : null;
var targetTimeSeries = configurations.target ? configurations.target.timeSeriesByCommitTime(false, true) : null;
var unfilteredCurrentTimeSeries = configurations.current.timeSeriesByCommitTime(true);
var unfilteredBaselineTimeSeries = configurations.baseline ? configurations.baseline.timeSeriesByCommitTime(true, true) : null;
var unfilteredTargetTimeSeries = configurations.target ? configurations.target.timeSeriesByCommitTime(true, true) : null;
return {
current: currentTimeSeries,
baseline: baselineTimeSeries,
target: targetTimeSeries,
unfilteredCurrentTimeSeries: unfilteredCurrentTimeSeries,
unfilteredBaselineTimeSeries: unfilteredBaselineTimeSeries,
unfilteredTargetTimeSeries: unfilteredTargetTimeSeries,
formatWithDeltaAndUnit: function (value, delta)
{
return this.formatter(value) + (delta && !isNaN(delta) ? ' \u00b1 ' + deltaFormatterWithoutSign(delta) : '');
},
formatter: this._makeFormatter(unit, 4, false),
deltaFormatter: this._makeFormatter(unit, 2, true),
smallerIsBetter: smallerIsBetter,
showOutlier: function (show)
{
this.current = show ? unfilteredCurrentTimeSeries : currentTimeSeries;
this.baseline = show ? unfilteredBaselineTimeSeries : baselineTimeSeries;
this.target = show ? unfilteredTargetTimeSeries : targetTimeSeries;
},
};
}
}).create();