Record code coverage in canvaskit tests, increase coverage

Fix a bug with paragraph text direction that an incorrect unit test wasn't detecting.

Change-Id: I73418ea8a90da097078d93ddf8692a55488f672f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/292366
Commit-Queue: Nathaniel Nifong <nifong@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
This commit is contained in:
Nathaniel Nifong 2020-05-29 11:06:19 -04:00 committed by Skia Commit-Bot
parent a727122007
commit 59d299ba3f
11 changed files with 3592 additions and 100 deletions

View File

@ -1,2 +1,3 @@
package-lock.json
fonts/*.cpp
fonts/*.cpp
coverage/*

View File

@ -4,7 +4,9 @@ To compile CanvasKit, you will first need to [install `emscripten`][1]. This
will set the environment `EMSDK` (among others) which is required for
compilation.
# Compile and Test Locally
[1]: https://emscripten.org/docs/getting_started/downloads.html
# Compile and Run Local Example
```
make release # make debug is much faster and has better error messages
@ -23,7 +25,67 @@ any of the "extras", one might run:
Such a stripped-down version is about half the size of the default release build.
[1]: https://emscripten.org/docs/getting_started/downloads.html
# Unit tests, performance tests, and coverage.
To run unit tests and compute test coverage on a debug gpu build
```
make debug
make test-continuous
```
This reads karma.conf.js, and opens a chrome browser and begins running all the test
in `test/` it will detect changes to the tests in that directory and automatically
run again, however it will automatically rebuild and reload canvaskit. Closing the
chrome window will just cause it to re-opened. Kill the karma process to stop continuous
monitoring for changes.
The tests are run with whichever build of canvaskit you last made. be sure to also
test with `release`, `debug_cpu`, and `release_cpu`. testing with release builds will
expose problems in closure compilation and usually forgotten externs.
## Coverage
Coverage will be automatically computed when running test-continuous locally. Note that
the results will only be useful when testing a debug build. Open
`coverage/<browser version>/index.html` For a summary and detailed line-by-line result.
## Measuring Performance
To measure the runtime of all benchmarks in `perf/`
```
make release
make perf
```
Performacnce benchmarks also use karma, with a different config `karma.bench.conf.js`.
It will run once and print results.
Typically, you'd want to run these at head, and with your CL to observe the effect of some
optimization.
## Adding tests
The tests in `tests/` and `perf/` are grouped into files by topic.
Within each file there are `describe` blocks further organizing the tests, and within those
`it()` functions which test particular behaviors. `describe` and `it` are jasmine methods
which can both be temporarily renamed `fdescribe` and `fit`. Which causes jasmine to only those.
We have also defined `gm` which is a method for defining a test which draws something to a canvas
that is shapshotted and reported to gold.skia.org, where you can compare it with the snapshot at
head.
## Testing from Gerrit
When submitting a CL in gerrit, click "choose tryjobs" and type canvaskit to filter them.
select all of them, which at the time of this writing is four jobs, for each combination
of perf/test gpu/cpu.
The performance results are reported to perf.skia.org
gold results are reported to gold.skia.org
Coverage is not measured while running tests this way.
# Infrastructure Playbook

View File

@ -115,7 +115,6 @@
CanvasKit = CK;
ParticlesAPI1(CanvasKit);
RTShaderAPI1(CanvasKit);
SurfaceAPI1(CanvasKit);
ColorSupport(CanvasKit);
});
@ -205,76 +204,6 @@
surface.requestAnimationFrame(drawFrame);
}
function SurfaceAPI1(CanvasKit) {
const surface = CanvasKit.MakeCanvasSurface('surfaces');
if (!surface) {
console.error('Could not make surface');
return;
}
console.log('SurfaceAPI1 top surface type = '+surface.reportBackendType() );
const context = CanvasKit.currentContext();
const canvas = surface.getCanvas();
//create a subsurface as a temporary workspace.
const subSurface = surface.makeSurface({
width: 50,
height: 50,
alphaType: CanvasKit.AlphaType.Premul,
colorType: CanvasKit.ColorType.RGBA_8888,
});
if (!subSurface) {
console.error('Could not make subsurface');
return;
}
console.log('SurfaceAPI1 subSurface type = '+subSurface.reportBackendType() );
// draw a small "scene"
const paint = new CanvasKit.SkPaint();
paint.setColor(CanvasKit.Color(139, 228, 135, 0.95)); // greenish
paint.setStyle(CanvasKit.PaintStyle.Fill);
paint.setAntiAlias(true);
const subCanvas = subSurface.getCanvas();
subCanvas.clear(CanvasKit.BLACK);
subCanvas.drawRect(CanvasKit.LTRBRect(5, 15, 45, 40), paint);
paint.setColor(CanvasKit.Color(214, 93, 244)); // purplish
for (let i = 0; i < 10; i++) {
const x = Math.random() * 50;
const y = Math.random() * 50;
subCanvas.drawOval(CanvasKit.XYWHRect(x, y, 6, 6), paint);
}
// Snap it off as an SkImage - this image will be in the form the
// parent surface prefers (e.g. Texture for GPU / Raster for CPU).
const img = subSurface.makeImageSnapshot();
// clean up the temporary surface
subSurface.delete();
paint.delete();
// Make it repeat a bunch with a shader
const pattern = img.makeShader(CanvasKit.TileMode.Repeat, CanvasKit.TileMode.Mirror);
const patternPaint = new CanvasKit.SkPaint();
patternPaint.setShader(pattern);
let i = 0;
function drawFrame() {
i++;
CanvasKit.setCurrentContext(context);
canvas.clear(CanvasKit.WHITE);
canvas.drawOval(CanvasKit.LTRBRect(i % 60, i % 60, 300 - (i% 60), 300 - (i % 60)), patternPaint);
surface.flush();
window.requestAnimationFrame(drawFrame);
}
window.requestAnimationFrame(drawFrame);
}
function ParagraphAPI1(CanvasKit, fontData) {
if (!CanvasKit || !fontData) {
return;

View File

@ -7,6 +7,7 @@
CanvasKit.MakeSWCanvasSurface = function(idOrElement) {
var canvas = idOrElement;
if (canvas.tagName !== 'CANVAS') {
// TODO(nifong): unit test
canvas = document.getElementById(idOrElement);
if (!canvas) {
throw 'Canvas with id ' + idOrElement + ' was not found';

View File

@ -235,30 +235,6 @@ function copy2dArray(arr, dest, ptr) {
return ptr;
}
// arr should be a non-jagged 3d JS array (TypedArrays can't be nested
// inside themselves.)
// dest is something like CanvasKit.HEAPF32
// ptr can be optionally provided if the memory was already allocated.
function copy3dArray(arr, dest, ptr) {
if (!arr || !arr.length || !arr[0].length) {
return nullptr;
}
if (!ptr) {
ptr = CanvasKit._malloc(arr.length * arr[0].length * arr[0][0].length * dest.BYTES_PER_ELEMENT);
}
var idx = 0;
var adjustedPtr = ptr / dest.BYTES_PER_ELEMENT;
for (var x = 0; x < arr.length; x++) {
for (var y = 0; y < arr[0].length; y++) {
for (var z = 0; z < arr[0][0].length; z++) {
dest[adjustedPtr + idx] = arr[x][y][z];
idx++;
}
}
}
return ptr;
}
var defaultPerspective = Float32Array.of(0, 0, 1);
// Copies the given DOMMatrix/Array/TypedArray to the CanvasKit heap and

View File

@ -76,6 +76,17 @@ module.exports = function(config) {
],
},
};
} else {
// Extra options that should only be applied locally
// Measure test coverage and write output to coverage/ directory
cfg.reporters.push('coverage');
cfg.preprocessors = {
// Measure test coverage of these source files
// Since this file is a combination of our code, and emscripten's glue,
// we'll never see 100% coverage, but this lets us measure improvements.
'canvaskit/bin/canvaskit.js': ['coverage'],
};
}
config.set(cfg);

3494
modules/canvaskit/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
"jasmine-core": "~3.1.0",
"karma": "~3.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage": "^2.0.2",
"karma-jasmine": "~1.1.2",
"requirejs": "~2.3.5"
},

View File

@ -15,7 +15,7 @@
var ret = [];
for (var i = 0; i < floatArray.length; i+=5) {
var r = CanvasKit.LTRBRect(floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3]);
if (floatArray[i+4] === 1) {
if (floatArray[i+4] === 0) {
r['direction'] = CanvasKit.TextDirection.RTL;
} else {
r['direction'] = CanvasKit.TextDirection.LTR;

View File

@ -33,6 +33,12 @@ describe('Core canvas behavior', () => {
paint.delete();
canvas.drawPicture(pic);
// test that file saving functionality throws no errors
// Unfortunately jasmine spy objects can't fake their type so we can't verify it downloads
// a nonzero sized file.
pic.saveAsFile('foo.skp');
pic.delete();
});
@ -593,6 +599,17 @@ describe('Core canvas behavior', () => {
expect(paint.getColor()).toEqual(Float32Array.of(3.3, 2.2, 1.1, 0.5));
});
gm('draw shadow', (canvas) => {
const lightRadius = 30;
const flags = 0;
const lightPos = [250,150,300];
const zPlaneParams = [0,0,1];
const path = starPath(CanvasKit);
canvas.drawShadow(path, zPlaneParams, lightPos, lightRadius,
CanvasKit.RED, CanvasKit.MAGENTA, flags);
})
describe('ColorSpace Support', () => {
it('Can create an SRGB 8888 surface', () => {
const colorSpace = CanvasKit.SkColorSpace.SRGB;

View File

@ -178,7 +178,7 @@ describe('Paragraph Behavior', function() {
expect(rects.length).toEqual(test.expectedNum);
for (const rect of rects) {
expect(rect.direction).toEqual(CanvasKit.TextDirection.LTR);
expect(rect.direction.value).toEqual(CanvasKit.TextDirection.LTR.value);
const p = new CanvasKit.SkPaint();
p.setColor(test.color);
p.setStyle(CanvasKit.PaintStyle.Stroke);