resources/perf.webkit.org/public/v3/components/test-group-revision-table.js (252 lines of code) (raw):

class TestGroupRevisionTable extends ComponentBase { constructor() { super('test-group-revision-table'); this._testGroup = null; this._analysisResults = null; this._renderTableLazily = new LazilyEvaluatedFunction(this._renderTable.bind(this)); } setTestGroup(testGroup) { this._testGroup = testGroup; this.enqueueToRender(); } setAnalysisResults(analysisResults) { this._analysisResults = analysisResults; this.enqueueToRender(); } render() { this._renderTableLazily.evaluate(this._testGroup, this._analysisResults); } _renderTable(testGroup, analysisResults) { if (!testGroup) return; const commitSets = testGroup.requestedCommitSets(); const requestedRepositorySet = new Set; const additionalRepositorySet = new Set; const patchedPepositorySet = new Set; for (const commitSet of commitSets) { for (const repository of commitSet.repositories()) { requestedRepositorySet.add(repository); if (commitSet.patchForRepository(repository)) patchedPepositorySet.add(repository); } } const rowEntries = []; let hasCustomRoots = false; let firstRequest = null; commitSets.forEach((commitSet, commitSetIndex) => { const setLabel = testGroup.labelForCommitSet(commitSet); const buildRequests = testGroup.requestsForCommitSet(commitSet); if (commitSet.customRoots().length) hasCustomRoots = true; if (!firstRequest) firstRequest = buildRequests[0]; buildRequests.forEach((request, i) => { const resultCommitSet = analysisResults ? analysisResults.commitSetForRequest(request) : null; if (resultCommitSet) { for (const repository of resultCommitSet.repositories()) { if (!requestedRepositorySet.has(repository)) additionalRepositorySet.add(repository); } } let label = (1 + request.order()).toString(); if (request.order() < 0) label = `Build ${request.order() - firstRequest.order() + 1}`; else if (firstRequest.isBuild()) label = `Test ${label}`; rowEntries.push({ groupHeader: !i ? setLabel : null, groupRowCount: buildRequests.length, label, commitSet: resultCommitSet || commitSet, requestedCommitSet: commitSet, customRoots: commitSet.customRoots(), // FIXME: resultCommitSet should also report roots that got installed. rowCountByRepository: new Map, repositoriesToSkip: new Set, customRootsRowCount: 1, request, }); }); }); this._mergeCellsWithSameCommitsAcrossRows(rowEntries); const requestedRepositoryList = Repository.sortByNamePreferringOnesWithURL([...requestedRepositorySet]); const additionalRepositoryList = Repository.sortByNamePreferringOnesWithURL([...additionalRepositorySet]); const element = ComponentBase.createElement; this.renderReplace(this.content('revision-table'), [ element('thead', [ element('th', 'Configuration'), requestedRepositoryList.map((repository) => element('th', repository.name())), hasCustomRoots ? element('th', 'Roots') : [], element('th', 'Order'), element('th', 'Status'), additionalRepositoryList.map((repository) => element('th', repository.name())), ]), element('tbody', rowEntries.map((entry) => { const request = entry.request; return element('tr', [ entry.groupHeader ? element('td', {rowspan: entry.groupRowCount}, entry.groupHeader) : [], requestedRepositoryList.map((repository) => this._buildCommitCell(entry, repository, patchedPepositorySet.has(repository))), hasCustomRoots ? this._buildCustomRootsCell(entry) : [], element('td', entry.label), this._buildDescriptionCell(request), additionalRepositoryList.map((repository) => this._buildCommitCell(entry, repository)), ]); }))]); } _buildDescriptionCell(request) { const link = ComponentBase.createLink; const element = ComponentBase.createElement; const showWarningIcon = request.hasFinished() && request.statusDescription(); const cellContent = []; if (showWarningIcon) { const warningIcon = new WarningIcon; warningIcon.setWarning(`Last status: ${request.statusDescription()}`); cellContent.push(element('div', {class: 'warning-icon-container'}, warningIcon)); } cellContent.push(request.statusUrl() ? link(request.statusLabel(), request.statusDescription(), request.statusUrl()) : request.statusLabel()); const hasInProgressReport = !request.hasFinished() && request.statusDescription(); if (hasInProgressReport) cellContent.push(element('span', {class: 'status-description'}, `${request.statusDescription()}`)); return element('td', {class: 'description-cell'}, cellContent); } _buildCommitCell(entry, repository, showRoot = false) { const element = ComponentBase.createElement; const link = ComponentBase.createLink; if (entry.repositoriesToSkip.has(repository)) return []; const commit = entry.commitSet.commitForRepository(repository); let content = []; if (commit) content = commit.url() ? [link(commit.label(), commit.url())] : [commit.label()]; const patch = entry.requestedCommitSet.patchForRepository(repository); if (patch) content.push(' with ', this._buildFileInfo(patch)); if (showRoot) { const root = entry.requestedCommitSet.rootForRepository(repository); if (root) content.push(' (', this._buildFileInfo(root, 'Build product') ,')'); } return element('td', {rowspan: entry.rowCountByRepository.get(repository)}, content); } _buildCustomRootsCell(entry) { const rowspan = entry.customRootsRowCount; if (!rowspan) return []; const element = ComponentBase.createElement; if (!entry.customRoots.length) return element('td', {class: 'roots', rowspan}); return element('td', {class: 'roots', rowspan}, element('ul', entry.customRoots.map((customRoot) => { return this._buildFileInfo(customRoot); }).map((content) => element('li', content)))); } _buildFileInfo(file, labelOverride = null) { const element = ComponentBase.createElement; const link = ComponentBase.createLink; let label = labelOverride || file.label(); if (file.deletedAt()) return [label, ' ', element('span', {class: 'purged'}, '(Purged)')]; return link(label, file.url()); } _mergeCellsWithSameCommitsAcrossRows(rowEntries) { for (let rowIndex = 0; rowIndex < rowEntries.length; rowIndex++) { const entry = rowEntries[rowIndex]; for (const repository of entry.commitSet.repositories()) { if (entry.repositoriesToSkip.has(repository)) continue; const commit = entry.commitSet.commitForRepository(repository); const patch = entry.requestedCommitSet.patchForRepository(repository); let rowCount = 1; for (let otherRowIndex = rowIndex + 1; otherRowIndex < rowEntries.length; otherRowIndex++) { const otherEntry = rowEntries[otherRowIndex]; const otherCommit = otherEntry.commitSet.commitForRepository(repository); const otherPatch = otherEntry.requestedCommitSet.patchForRepository(repository); if (commit != otherCommit || patch != otherPatch) break; otherEntry.repositoriesToSkip.add(repository); rowCount++; } entry.rowCountByRepository.set(repository, rowCount); } if (entry.customRootsRowCount) { let rowCount = 1; for (let otherRowIndex = rowIndex + 1; otherRowIndex < rowEntries.length; otherRowIndex++) { const otherEntry = rowEntries[otherRowIndex]; if (!CommitSet.areCustomRootsEqual(entry.customRoots, otherEntry.customRoots)) break; otherEntry.customRootsRowCount = 0; rowCount++; } entry.customRootsRowCount = rowCount; } } } static htmlTemplate() { return `<table id="revision-table"></table>`; } static cssTemplate() { return ` table { border-collapse: collapse; } th, td { text-align: center; padding: 0.2rem 0.8rem; } tbody th, tbody td { border-top: solid 1px #eee; border-bottom: solid 1px #eee; } th { font-weight: inherit; } .status-description { display: inline-block; max-width: 10rem; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } .status-description::before { content: ': '; } .description-cell { position: relative; } div.warning-icon-container { position: absolute; top: -0.2rem; left: 0; } .roots { max-width: 20rem; } .purged { color: #999; } .roots ul, .roots li { list-style: none; margin: 0; padding: 0; } .roots li { margin-top: 0.4rem; margin-bottom: 0.4rem; } `; } } ComponentBase.defineElement('test-group-revision-table', TestGroupRevisionTable);