blob: 6f5aaa13ed9eed102f6bfbe384ba0c1097396c4b [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2019 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>chromium-coverage.js test</title>
<script src="../bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<script src="../bower_components/web-component-tester/browser.js"></script>
<test-fixture id="coverage-fixture">
<template>
<chromium-coverage></chromium-coverage>
</template>
</test-fixture>
<script type="module">
import './common-test-setup.js';
import {CoverageClient} from '../src/main/resources/static/coverage.js';
suite('<code coverage>', () => {
// Sample change info used for testing.
const sampleChangeInfo = {
host: 'chromium-review.googlesource.com',
project: 'chromium/src',
changeNum: 12345,
patchNum: 2,
};
// Sample coverage lines response from service; used for testing.
const sampleLinesResponse = {
data: {
files: [
{
path: 'base/test.cc',
lines: [
{
line: 10,
count: 10,
},
{
line: 11,
count: 0,
},
{
line: 12,
count: 0,
},
]
}
]
}
};
// Sample coverage ranges that match with the lines response.
const sampleCoverageRanges = {
'base/test.cc': [
{
side: 'right',
type: 'COVERED',
code_range: {
start_line: 10,
end_line: 10,
}
},
{
side: 'right',
type: 'NOT_COVERED',
code_range: {
start_line: 11,
end_line: 12,
}
},
]
};
// Sample coverage percentages response from service; used for testing.
const samplePercentagesResponse = {
data: {
files: [
{
path: 'base/test.cc',
absolute_coverage: {
covered: 3,
total: 10,
},
incremental_coverage: {
covered: 3,
total: 4,
},
}
]
}
};
// Sample coverage percentages that match with the percentages response.
const sampleCoveragePercentages = {
'base/test.cc': {
absolute: 30,
incremental: 75,
}
};
let coverageClient;
setup(() => {
coverageClient = new CoverageClient();
sinon.stub(window, 'fetch');
});
teardown(() => {
window.fetch.restore();
});
test('get normalized host', () => {
assert.equal('chromium-review.googlesource.com',
coverageClient.getNormalizedHost(
'chromium-review.googlesource.com'));
assert.equal('chromium-review.googlesource.com',
coverageClient.getNormalizedHost(
'canary-chromium-review.googlesource.com'));
});
test('parse project name from gerrit url', () => {
assert.equal('chromium/src',
coverageClient.parseProjectFromPathName(
'/c/chromium/src/+/1369646'));
assert.equal('chromium/src',
coverageClient.parseProjectFromPathName(
'/c/chromium/src/+/1369646'));
assert.equal('chromium/src',
coverageClient.parseProjectFromPathName(
'/c/chromium/src/+/1369646/3'));
assert.equal('chromium/src',
coverageClient.parseProjectFromPathName(
'/c/chromium/src/+/1369646/3/'));
assert.equal('chromium/src',
coverageClient.parseProjectFromPathName(
'/c/chromium/src/+/1369646/3/base/test.cc'));
assert.equal('chromium/src',
coverageClient.parseProjectFromPathName(
'/c/chromium/src/+/1369646/3/base/base/test.cc/'));
});
test('fetch coverage lines for chromium', async () => {
const response = new window.Response(JSON.stringify(sampleLinesResponse),
{ status: 200 });
window.fetch.returns(Promise.resolve(response));
const responseJson = await coverageClient.fetchCoverageJsonData(
sampleChangeInfo, 'lines');
assert.equal('https://findit-for-me.appspot.com/coverage/api/' +
'coverage-data?host=chromium-review.googlesource.com&' +
'project=chromium%2Fsrc&change=12345&patchset=2&' +
'type=lines&format=json&concise=1',
window.fetch.getCall(0).args[0]);
assert.deepEqual(sampleLinesResponse, responseJson);
});
test('fetch coverage percentanges for chromium', async () => {
const response = new window.Response(JSON.stringify(sampleLinesResponse),
{ status: 200 });
window.fetch.returns(Promise.resolve(response));
const responseJson = await coverageClient.fetchCoverageJsonData(
sampleChangeInfo, 'percentages');
assert.equal('https://findit-for-me.appspot.com/coverage/api/' +
'coverage-data?host=chromium-review.googlesource.com&' +
'project=chromium%2Fsrc&change=12345&patchset=2&' +
'type=percentages&format=json&concise=1',
window.fetch.getCall(0).args[0]);
assert.deepEqual(sampleLinesResponse, responseJson);
});
test('fetch coverage lines for libassistant', async () => {
const response = new window.Response(JSON.stringify(sampleLinesResponse),
{ status: 200 });
window.fetch.returns(Promise.resolve(response));
const responseJson = await coverageClient.fetchCoverageJsonData({
host: 'libassistant-internal-review.googlesource.com',
project: 'libassistant/internal',
changeNum: 12345,
patchNum: 2,
}, 'lines');
assert.equal('https://gob-coverage.googleplex.com/coverage/api/' +
'coverage-data?host=libassistant-internal-review.' +
'googlesource.com&project=libassistant%2Finternal&' +
'change=12345&patchset=2&type=lines&format=json&concise=1',
window.fetch.getCall(0).args[0]);
assert.deepEqual(sampleLinesResponse, responseJson);
});
test('fetch coverage lines for unknown host', async () => {
const response = new window.Response(JSON.stringify(sampleLinesResponse),
{ status: 200 });
window.fetch.returns(Promise.resolve(response));
const responseJson = await coverageClient.fetchCoverageJsonData({
host: 'unknown-review.googlesource.com',
project: 'unknown/src',
changeNum: 12345,
patchNum: 2,
}, 'lines');
assert.equal('https://findit-for-me.appspot.com/coverage/api/' +
'coverage-data?host=unknown-review.googlesource.com&' +
'project=unknown%2Fsrc&change=12345&patchset=2&' +
'type=lines&format=json&concise=1',
window.fetch.getCall(0).args[0]);
assert.deepEqual(sampleLinesResponse, responseJson);
});
test('parse coverage ranges', () => {
const coverageRanges =
coverageClient.convertResponseJsonToCoverageRanges(
sampleLinesResponse);
assert.deepEqual(sampleCoverageRanges, coverageRanges);
});
test('parse coverage percentages', () => {
const coveragePercentages =
coverageClient.convertResponseJsonToCoveragePercentages(
samplePercentagesResponse);
assert.deepEqual(sampleCoveragePercentages, coveragePercentages);
});
test('coverage data are cached', async () => {
sinon.stub(coverageClient, 'getNormalizedHost');
sinon.stub(coverageClient, 'parseProjectFromPathName');
sinon.stub(coverageClient, 'fetchCoverageJsonData');
coverageClient.getNormalizedHost.returns(sampleChangeInfo.host);
coverageClient.parseProjectFromPathName.returns(sampleChangeInfo.project);
coverageClient.coverageData.changeInfo = sampleChangeInfo;
coverageClient.coverageData.rangesPromise = new Promise(
function(resolve, reject) {
resolve(sampleCoverageRanges);
});
coverageClient.coverageData.percentagesPromise = new Promise(
function(resolve, reject) {
resolve(sampleCoveragePercentages);
});
const ranges = await coverageClient.provideCoverageRanges(
'12345', 'base/test.cc', 'PARENT', '2');
assert.equal(false, coverageClient.fetchCoverageJsonData.called);
assert.deepEqual(sampleCoverageRanges['base/test.cc'], ranges);
const percentages = await coverageClient.provideCoveragePercentages(
'12345', 'base/test.cc', '2');
assert.equal(false, coverageClient.fetchCoverageJsonData.called);
assert.deepEqual({absolute: 30, incremental: 75}, percentages);
coverageClient.getNormalizedHost.restore();
coverageClient.parseProjectFromPathName.restore();
coverageClient.fetchCoverageJsonData.restore();
});
test('coverage ranges are not cached', async () => {
sinon.stub(coverageClient, 'getNormalizedHost');
sinon.stub(coverageClient, 'parseProjectFromPathName');
sinon.stub(coverageClient, 'fetchCoverageJsonData');
coverageClient.getNormalizedHost.returns(sampleChangeInfo.host);
coverageClient.parseProjectFromPathName.returns(sampleChangeInfo.project);
coverageClient.fetchCoverageJsonData.withArgs(
sampleChangeInfo, 'lines').returns(sampleLinesResponse);
const ranges = await coverageClient.provideCoverageRanges(
'12345', 'base/test.cc', 'PARENT', '2')
assert.deepEqual(sampleChangeInfo,
coverageClient.coverageData.changeInfo);
assert.deepEqual(sampleCoverageRanges['base/test.cc'], ranges);
coverageClient.getNormalizedHost.restore();
coverageClient.parseProjectFromPathName.restore();
coverageClient.fetchCoverageJsonData.restore();
});
test('coverage percentages are not cached', async () => {
sinon.stub(coverageClient, 'getNormalizedHost');
sinon.stub(coverageClient, 'parseProjectFromPathName');
sinon.stub(coverageClient, 'fetchCoverageJsonData');
coverageClient.getNormalizedHost.returns(sampleChangeInfo.host);
coverageClient.parseProjectFromPathName.returns(sampleChangeInfo.project);
coverageClient.fetchCoverageJsonData.withArgs(
sampleChangeInfo, 'percentages').returns(samplePercentagesResponse);
const percentages = await coverageClient.provideCoveragePercentages(
'12345', 'base/test.cc', '2')
assert.deepEqual(sampleChangeInfo,
coverageClient.coverageData.changeInfo);
assert.deepEqual({absolute: 30, incremental: 75}, percentages);
coverageClient.getNormalizedHost.restore();
coverageClient.parseProjectFromPathName.restore();
coverageClient.fetchCoverageJsonData.restore();
});
test('incremental percentage is not available', async () => {
const response = {
data: {
files: [
{
path: 'base/test.cc',
absolute_coverage: {
covered: 3,
total: 10,
},
incremental_coverage: null,
}
]
}
};
sinon.stub(coverageClient, 'getNormalizedHost');
sinon.stub(coverageClient, 'parseProjectFromPathName');
sinon.stub(coverageClient, 'fetchCoverageJsonData');
coverageClient.getNormalizedHost.returns(sampleChangeInfo.host);
coverageClient.parseProjectFromPathName.returns(sampleChangeInfo.project);
coverageClient.fetchCoverageJsonData.withArgs(
sampleChangeInfo, 'percentages').returns(response);
const percentages = await coverageClient.provideCoveragePercentages(
'12345', 'base/test.cc', '2')
assert.deepEqual(sampleChangeInfo,
coverageClient.coverageData.changeInfo);
assert.deepEqual({absolute: 30, incremental: null}, percentages);
coverageClient.getNormalizedHost.restore();
coverageClient.parseProjectFromPathName.restore();
coverageClient.fetchCoverageJsonData.restore();
});
test('prefetch coverage ranges', async () => {
sinon.stub(coverageClient, 'getNormalizedHost');
sinon.stub(coverageClient, 'fetchCoverageJsonData');
coverageClient.getNormalizedHost.returns(sampleChangeInfo.host);
coverageClient.fetchCoverageJsonData.withArgs(
sampleChangeInfo, 'lines').returns(sampleLinesResponse);
coverageClient.prefetchCoverageRanges(
{'project': 'chromium/src','_number': 12345}, {'_number': 2});
assert.deepEqual(sampleChangeInfo,
coverageClient.coverageData.changeInfo);
assert.deepEqual(sampleCoverageRanges,
await coverageClient.coverageData.rangesPromise);
coverageClient.getNormalizedHost.restore();
coverageClient.fetchCoverageJsonData.restore();
});
test('invalid patchset number', async () => {
sinon.stub(coverageClient, 'getNormalizedHost');
sinon.stub(coverageClient, 'parseProjectFromPathName');
sinon.stub(coverageClient, 'fetchCoverageJsonData');
coverageClient.getNormalizedHost.returns(sampleChangeInfo.host);
coverageClient.parseProjectFromPathName.returns(sampleChangeInfo.project);
await coverageClient.provideCoverageRanges(
'12345', 'base/test.cc', null, null);
assert.equal(false, coverageClient.fetchCoverageJsonData.called);
await coverageClient.provideCoveragePercentages(
'12345', 'base/test.cc', -1);
assert.equal(false, coverageClient.fetchCoverageJsonData.called);
coverageClient.getNormalizedHost.restore();
coverageClient.parseProjectFromPathName.restore();
coverageClient.fetchCoverageJsonData.restore();
});
// This test tests the scenario that if two requests were issued to fetch
// coverage ranges for different change/patchset, the eventual coverage
// ranges won't be overwritten incorrectly.
test('racing condition between multiple fetches', async () => {
let changeInfo1 = JSON.parse(JSON.stringify(sampleChangeInfo));
changeInfo1.patchNum = 1;
let changeInfo2 = JSON.parse(JSON.stringify(sampleChangeInfo));
changeInfo2.patchNum = 2;
let ranges1 = {
'base/test.cc': [
{
side: 'right',
type: 'NOT_COVERED',
code_range: {
start_line: 10,
end_line: 10,
}
}
]
};
const fetchPromise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(ranges1);
}, 100);
});
let ranges2 = {
'base/test.cc': [
{
side: 'right',
type: 'COVERED',
code_range: {
start_line: 20,
end_line: 20,
}
}
]
}
const fetchPromise2 = Promise.resolve(ranges2);
sinon.stub(coverageClient, 'getNormalizedHost');
sinon.stub(coverageClient, 'parseProjectFromPathName');
sinon.stub(coverageClient, 'fetchCoverageRanges');
sinon.stub(coverageClient, 'fetchCoveragePercentages');
coverageClient.getNormalizedHost.returns(sampleChangeInfo.host);
coverageClient.parseProjectFromPathName.returns(sampleChangeInfo.project);
coverageClient.fetchCoverageRanges.withArgs(changeInfo1).returns(
fetchPromise1)
coverageClient.fetchCoverageRanges.withArgs(changeInfo2).returns(
fetchPromise2)
coverageClient.fetchCoveragePercentages.returns(new Promise(
function(resolve, reject) {
resolve(sampleCoveragePercentages);
}));
coverageClient.prefetchCoverageRanges({
'project': changeInfo1.project,
'_number': changeInfo1.changeNum
}, {
'_number': changeInfo1.patchNum
});
coverageClient.provideCoverageRanges(
changeInfo2.changeNum, 'base/test.cc', 'PARENT',
changeInfo2.patchNum);
// Even though the first request finishes after the second request, it
// shouldn't overwrite the coverage ranges promise because the user has
// moved on to a different change/patchset.
await fetchPromise1, fetchPromise2;
assert.deepEqual(ranges2,
await coverageClient.coverageData.rangesPromise);
coverageClient.getNormalizedHost.restore();
coverageClient.parseProjectFromPathName.restore();
coverageClient.fetchCoverageRanges.restore();
coverageClient.fetchCoveragePercentages.restore();
})
test('show percentage columns', async () => {
sinon.stub(coverageClient, 'parseProjectFromPathName').returns(
'chromium/src');
const configPromise = new Promise((resolve, reject) => {
resolve({ enabled: true });
});
coverageClient.plugin = {
getPluginName: sinon.stub().returns('chromium-coverage'),
restApi: sinon.stub().returns({
get: sinon.stub().returns(configPromise),
}),
};
assert.equal(true, await coverageClient.showPercentageColumns());
assert.equal('/projects/chromium%2Fsrc/chromium-coverage~config',
coverageClient.plugin.restApi().get.getCall(0).args[0]);
coverageClient.parseProjectFromPathName.restore();
coverageClient.plugin = null;
});
});
</script>