Compare commits
21 Commits
revert-597
...
greenkeepe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
097daeec28 | ||
|
|
d73aeb1ac1 | ||
|
|
298200f2ee | ||
|
|
b9411bf51e | ||
|
|
3513cd77ae | ||
|
|
d8c9f339f7 | ||
|
|
aaffc77b23 | ||
|
|
d867e62c74 | ||
|
|
a7bec3a958 | ||
|
|
0ce50b6cd6 | ||
|
|
562e535a13 | ||
|
|
42d3f6c012 | ||
|
|
9c01f364d4 | ||
|
|
5d085f678f | ||
|
|
7df536f492 | ||
|
|
a41bcafac7 | ||
|
|
5566a600ba | ||
|
|
34072d2f53 | ||
|
|
fdd02a6bd6 | ||
|
|
ec141ff76d | ||
|
|
3f68e18b2e |
@@ -1,14 +1,10 @@
|
||||
language: node_js
|
||||
dist: trusty
|
||||
addons:
|
||||
chrome: stable
|
||||
node_js:
|
||||
- 8
|
||||
- 10
|
||||
- node
|
||||
env:
|
||||
- NODE_ENV=production
|
||||
before_install:
|
||||
- google-chrome-stable --headless --no-sandbox --remote-debugging-port=9222 &
|
||||
install:
|
||||
- npm --production=false install
|
||||
- npm --production=false update
|
||||
@@ -16,6 +12,7 @@ sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- ~/.cache/ms-playwright
|
||||
jobs:
|
||||
include:
|
||||
- stage: test
|
||||
@@ -24,7 +21,7 @@ jobs:
|
||||
- npm run docs
|
||||
- npm run tap
|
||||
- stage: deploy
|
||||
node_js: 8
|
||||
node_js: 10
|
||||
script: npm run build
|
||||
before_deploy:
|
||||
- VPKG=$($(npm bin)/json -f package.json version)
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-polyfill": "^6.22.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"chromeless": "^1.5.1",
|
||||
"copy-webpack-plugin": "^4.5.1",
|
||||
"docdash": "^0.4.0",
|
||||
"eslint": "^4.6.1",
|
||||
@@ -37,7 +36,8 @@
|
||||
"gh-pages": "^1.0.0",
|
||||
"jsdoc": "^3.5.5",
|
||||
"json": "^9.0.4",
|
||||
"scratch-vm": "0.2.0-prerelease.20191227164934",
|
||||
"playwright-chromium": "^1.0.1",
|
||||
"scratch-vm": "0.2.0-prerelease.20200519195348",
|
||||
"tap": "^11.0.0",
|
||||
"travis-after-all": "^1.4.4",
|
||||
"uglifyjs-webpack-plugin": "^1.2.5",
|
||||
|
||||
@@ -679,9 +679,10 @@ class Drawable {
|
||||
* @param {twgl.v3} vec The scratch space [x,y] vector
|
||||
* @param {Drawable} drawable The drawable to sample the texture from
|
||||
* @param {Uint8ClampedArray} dst The "color4b" representation of the texture at point.
|
||||
* @param {number} [effectMask] A bitmask for which effects to use. Optional.
|
||||
* @returns {Uint8ClampedArray} The dst object filled with the color4b
|
||||
*/
|
||||
static sampleColor4b (vec, drawable, dst) {
|
||||
static sampleColor4b (vec, drawable, dst, effectMask) {
|
||||
const localPosition = getLocalPosition(drawable, vec);
|
||||
if (localPosition[0] < 0 || localPosition[1] < 0 ||
|
||||
localPosition[0] > 1 || localPosition[1] > 1) {
|
||||
@@ -698,7 +699,7 @@ class Drawable {
|
||||
// : drawable.skin._silhouette.colorAtLinear(localPosition, dst);
|
||||
|
||||
if (drawable.enabledEffects === 0) return textColor;
|
||||
return EffectTransform.transformColor(drawable, textColor);
|
||||
return EffectTransform.transformColor(drawable, textColor, effectMask);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,16 +118,17 @@ class EffectTransform {
|
||||
* Ghost and Color and Brightness effects.
|
||||
* @param {Drawable} drawable The drawable to get uniforms from.
|
||||
* @param {Uint8ClampedArray} inOutColor The color to transform.
|
||||
* @param {number} [effectMask] A bitmask for which effects to use. Optional.
|
||||
* @returns {Uint8ClampedArray} dst filled with the transformed color
|
||||
*/
|
||||
static transformColor (drawable, inOutColor) {
|
||||
|
||||
static transformColor (drawable, inOutColor, effectMask) {
|
||||
// If the color is fully transparent, don't bother attempting any transformations.
|
||||
if (inOutColor[3] === 0) {
|
||||
return inOutColor;
|
||||
}
|
||||
|
||||
const effects = drawable.enabledEffects;
|
||||
let effects = drawable.enabledEffects;
|
||||
if (typeof effectMask === 'number') effects &= effectMask;
|
||||
const uniforms = drawable.getUniforms();
|
||||
|
||||
const enableColor = (effects & ShaderManager.EFFECT_INFO.color.mask) !== 0;
|
||||
|
||||
@@ -763,6 +763,9 @@ class RenderWebGL extends EventEmitter {
|
||||
const color = __touchingColor;
|
||||
const hasMask = Boolean(mask3b);
|
||||
|
||||
// Masked drawable ignores ghost effect
|
||||
const effectMask = ~ShaderManager.EFFECT_INFO.ghost.mask;
|
||||
|
||||
// Scratch Space - +y is top
|
||||
for (let y = bounds.bottom; y <= bounds.top; y++) {
|
||||
if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) {
|
||||
@@ -773,7 +776,7 @@ class RenderWebGL extends EventEmitter {
|
||||
point[0] = x;
|
||||
// if we use a mask, check our sample color...
|
||||
if (hasMask ?
|
||||
maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) :
|
||||
maskMatches(Drawable.sampleColor4b(point, drawable, color, effectMask), mask3b) :
|
||||
drawable.isTouching(point)) {
|
||||
RenderWebGL.sampleColor3b(point, candidates, color);
|
||||
if (debugCanvasContext) {
|
||||
|
||||
54
test/helper/page-util.js
Normal file
54
test/helper/page-util.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/* global window, VirtualMachine, ScratchStorage, ScratchSVGRenderer */
|
||||
/* eslint-env browser */
|
||||
|
||||
// Wait for all SVG skins to be loaded.
|
||||
// TODO: this is extremely janky and should be removed once vm.loadProject waits for SVG skins to load
|
||||
// https://github.com/LLK/scratch-render/issues/563
|
||||
window.waitForSVGSkinLoad = renderer => new Promise(resolve => {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let interval;
|
||||
|
||||
const waitInner = () => {
|
||||
let numSVGSkins = 0;
|
||||
let numLoadedSVGSkins = 0;
|
||||
for (const skin of renderer._allSkins) {
|
||||
if (skin.constructor.name !== 'SVGSkin') continue;
|
||||
numSVGSkins++;
|
||||
if (skin._svgRenderer.loaded) numLoadedSVGSkins++;
|
||||
}
|
||||
|
||||
if (numSVGSkins === numLoadedSVGSkins) {
|
||||
clearInterval(interval);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
|
||||
interval = setInterval(waitInner, 1);
|
||||
});
|
||||
|
||||
window.loadFileInputIntoVM = (fileInput, vm, render) => {
|
||||
const reader = new FileReader();
|
||||
return new Promise(resolve => {
|
||||
reader.onload = () => {
|
||||
vm.start();
|
||||
vm.loadProject(reader.result)
|
||||
.then(() => window.waitForSVGSkinLoad(render))
|
||||
.then(() => {
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(fileInput.files[0]);
|
||||
});
|
||||
};
|
||||
|
||||
window.initVM = render => {
|
||||
const vm = new VirtualMachine();
|
||||
const storage = new ScratchStorage();
|
||||
|
||||
vm.attachStorage(storage);
|
||||
vm.attachRenderer(render);
|
||||
vm.attachV2SVGAdapter(new ScratchSVGRenderer.SVGRenderer());
|
||||
vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
|
||||
|
||||
return vm;
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
<script src="../../node_modules/scratch-vm/dist/web/scratch-vm.js"></script>
|
||||
<script src="../../node_modules/scratch-storage/dist/web/scratch-storage.js"></script>
|
||||
<script src="../../node_modules/scratch-svg-renderer/dist/web/scratch-svg-renderer.js"></script>
|
||||
<script src="../helper/page-util.js"></script>
|
||||
<!-- note: this uses the BUILT version of scratch-render! make sure to npm run build -->
|
||||
<script src="../../dist/web/scratch-render.js"></script>
|
||||
|
||||
@@ -17,38 +18,18 @@
|
||||
window.devicePixelRatio = 1;
|
||||
const gpuCanvas = document.getElementById('test');
|
||||
var render = new ScratchRender(gpuCanvas);
|
||||
var vm = new VirtualMachine();
|
||||
var storage = new ScratchStorage();
|
||||
var vm = initVM(render);
|
||||
|
||||
vm.attachStorage(storage);
|
||||
vm.attachRenderer(render);
|
||||
vm.attachV2SVGAdapter(new ScratchSVGRenderer.SVGRenderer());
|
||||
vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
|
||||
|
||||
document.getElementById('file').addEventListener('click', e => {
|
||||
document.body.removeChild(document.getElementById('loaded'));
|
||||
});
|
||||
|
||||
document.getElementById('file').addEventListener('change', e => {
|
||||
const reader = new FileReader();
|
||||
const thisFileInput = e.target;
|
||||
reader.onload = () => {
|
||||
vm.start();
|
||||
vm.loadProject(reader.result)
|
||||
.then(() => {
|
||||
// we add a `#loaded` div to our document, the integration suite
|
||||
// waits for that element to show up to assume the vm is ready
|
||||
// to play!
|
||||
const div = document.createElement('div');
|
||||
div.id='loaded';
|
||||
document.body.appendChild(div);
|
||||
vm.greenFlag();
|
||||
setTimeout(() => {
|
||||
renderCpu();
|
||||
}, 1000);
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(thisFileInput.files[0]);
|
||||
const fileInput = document.getElementById('file');
|
||||
const loadFile = loadFileInputIntoVM.bind(null, fileInput, vm, render);
|
||||
fileInput.addEventListener('change', e => {
|
||||
loadFile()
|
||||
.then(() => {
|
||||
vm.greenFlag();
|
||||
setTimeout(() => {
|
||||
renderCpu();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
const cpuCanvas = document.getElementById('cpu');
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<script src="../../node_modules/scratch-vm/dist/web/scratch-vm.js"></script>
|
||||
<script src="../../node_modules/scratch-storage/dist/web/scratch-storage.js"></script>
|
||||
<script src="../../node_modules/scratch-svg-renderer/dist/web/scratch-svg-renderer.js"></script>
|
||||
<script src="../helper/page-util.js"></script>
|
||||
<!-- note: this uses the BUILT version of scratch-render! make sure to npm run build -->
|
||||
<script src="../../dist/web/scratch-render.js"></script>
|
||||
|
||||
@@ -15,39 +16,13 @@
|
||||
|
||||
var canvas = document.getElementById('test');
|
||||
var render = new ScratchRender(canvas);
|
||||
var vm = new VirtualMachine();
|
||||
var storage = new ScratchStorage();
|
||||
var vm = initVM(render);
|
||||
var mockMouse = data => vm.runtime.postIOData('mouse', {
|
||||
canvasWidth: canvas.width,
|
||||
canvasHeight: canvas.height,
|
||||
...data,
|
||||
});
|
||||
|
||||
vm.attachStorage(storage);
|
||||
vm.attachRenderer(render);
|
||||
vm.attachV2SVGAdapter(new ScratchSVGRenderer.SVGRenderer());
|
||||
vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
|
||||
|
||||
document.getElementById('file').addEventListener('click', e => {
|
||||
document.body.removeChild(document.getElementById('loaded'));
|
||||
});
|
||||
|
||||
document.getElementById('file').addEventListener('change', e => {
|
||||
const reader = new FileReader();
|
||||
const thisFileInput = e.target;
|
||||
reader.onload = () => {
|
||||
vm.start();
|
||||
vm.loadProject(reader.result)
|
||||
.then(() => {
|
||||
// we add a `#loaded` div to our document, the integration suite
|
||||
// waits for that element to show up to assume the vm is ready
|
||||
// to play!
|
||||
const div = document.createElement('div');
|
||||
div.id='loaded';
|
||||
document.body.appendChild(div);
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(thisFileInput.files[0]);
|
||||
});
|
||||
const loadFile = loadFileInputIntoVM.bind(null, document.getElementById('file'), vm, render);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -1,29 +1,34 @@
|
||||
/* global vm, render, Promise */
|
||||
const {Chromeless} = require('chromeless');
|
||||
const {chromium} = require('playwright-chromium');
|
||||
const test = require('tap').test;
|
||||
const path = require('path');
|
||||
const chromeless = new Chromeless();
|
||||
|
||||
const indexHTML = path.resolve(__dirname, 'index.html');
|
||||
const testDir = (...args) => path.resolve(__dirname, 'pick-tests', ...args);
|
||||
|
||||
const runFile = (file, action, script) =>
|
||||
const runFile = async (file, action, page, script) => {
|
||||
// start each test by going to the index.html, and loading the scratch file
|
||||
chromeless.goto(`file://${indexHTML}`)
|
||||
.setFileInput('#file', testDir(file))
|
||||
// the index.html handler for file input will add a #loaded element when it
|
||||
// finishes.
|
||||
.wait('#loaded')
|
||||
.evaluate(`function () {return (${script})(${action});}`)
|
||||
;
|
||||
await page.goto(`file://${indexHTML}`);
|
||||
const fileInput = await page.$('#file');
|
||||
await fileInput.setInputFiles(testDir(file));
|
||||
|
||||
await page.evaluate(() =>
|
||||
// `loadFile` is defined on the page itself.
|
||||
// eslint-disable-next-line no-undef
|
||||
loadFile()
|
||||
);
|
||||
return page.evaluate(`(function () {return (${script})(${action});})()`);
|
||||
};
|
||||
|
||||
// immediately invoked async function to let us wait for each test to finish before starting the next.
|
||||
(async () => {
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage();
|
||||
|
||||
const testOperation = async function (name, action, expect) {
|
||||
await test(name, async t => {
|
||||
|
||||
const results = await runFile('test-mouse-touch.sb2', action, boundAction => {
|
||||
const results = await runFile('test-mouse-touch.sb2', action, page, boundAction => {
|
||||
vm.greenFlag();
|
||||
const sendResults = [];
|
||||
|
||||
@@ -97,5 +102,10 @@ const runFile = (file, action, script) =>
|
||||
}
|
||||
|
||||
// close the browser window we used
|
||||
await chromeless.end();
|
||||
})();
|
||||
await browser.close();
|
||||
})().catch(err => {
|
||||
// Handle promise rejections by exiting with a nonzero code to ensure that tests don't erroneously pass
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -1,55 +1,15 @@
|
||||
/* global vm, Promise */
|
||||
const {Chromeless} = require('chromeless');
|
||||
const {chromium} = require('playwright-chromium');
|
||||
const test = require('tap').test;
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const chromeless = new Chromeless();
|
||||
|
||||
const allGpuModes = ['ForceCPU', 'ForceGPU', 'Automatic'];
|
||||
|
||||
const indexHTML = path.resolve(__dirname, 'index.html');
|
||||
const testDir = (...args) => path.resolve(__dirname, 'scratch-tests', ...args);
|
||||
|
||||
const testFile = file => test(file, async t => {
|
||||
// start each test by going to the index.html, and loading the scratch file
|
||||
const says = await chromeless.goto(`file://${indexHTML}`)
|
||||
.setFileInput('#file', testDir(file))
|
||||
// the index.html handler for file input will add a #loaded element when it
|
||||
// finishes.
|
||||
.wait('#loaded')
|
||||
.evaluate(() => {
|
||||
// This function is run INSIDE the integration chrome browser via some
|
||||
// injection and .toString() magic. We can return some "simple data"
|
||||
// back across as a promise, so we will just log all the says that happen
|
||||
// for parsing after.
|
||||
|
||||
// this becomes the `says` in the outer scope
|
||||
const messages = [];
|
||||
const TIMEOUT = 5000;
|
||||
|
||||
vm.runtime.on('SAY', (_, __, message) => {
|
||||
messages.push(message);
|
||||
});
|
||||
|
||||
vm.greenFlag();
|
||||
const startTime = Date.now();
|
||||
|
||||
return Promise.resolve()
|
||||
.then(async () => {
|
||||
// waiting for all threads to complete, then we return
|
||||
while (vm.runtime.threads.some(thread => vm.runtime.isActiveThread(thread))) {
|
||||
if ((Date.now() - startTime) >= TIMEOUT) {
|
||||
// if we push the message after end, the failure from tap is not very useful:
|
||||
// "not ok test after end() was called"
|
||||
messages.unshift(`fail Threads still running after ${TIMEOUT}ms`);
|
||||
break;
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
}
|
||||
|
||||
return messages;
|
||||
});
|
||||
});
|
||||
|
||||
const checkOneGpuMode = (t, says) => {
|
||||
// Map string messages to tap reporting methods. This will be used
|
||||
// with events from scratch's runtime emitted on block instructions.
|
||||
let didPlan = false;
|
||||
@@ -99,17 +59,78 @@ const testFile = file => test(file, async t => {
|
||||
t.fail('did not say "end"');
|
||||
t.end();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const testFile = async (file, page) => {
|
||||
// start each test by going to the index.html, and loading the scratch file
|
||||
await page.goto(`file://${indexHTML}`);
|
||||
const fileInput = await page.$('#file');
|
||||
await fileInput.setInputFiles(testDir(file));
|
||||
await page.evaluate(() =>
|
||||
// `loadFile` is defined on the page itself.
|
||||
// eslint-disable-next-line no-undef
|
||||
loadFile()
|
||||
);
|
||||
const says = await page.evaluate(async useGpuModes => {
|
||||
// This function is run INSIDE the integration chrome browser via some
|
||||
// injection and .toString() magic. We can return some "simple data"
|
||||
// back across as a promise, so we will just log all the says that happen
|
||||
// for parsing after.
|
||||
|
||||
// this becomes the `says` in the outer scope
|
||||
const allMessages = {};
|
||||
const TIMEOUT = 5000;
|
||||
|
||||
vm.runtime.on('SAY', (_, __, message) => {
|
||||
const messages = allMessages[vm.renderer._useGpuMode];
|
||||
messages.push(message);
|
||||
});
|
||||
|
||||
for (const useGpuMode of useGpuModes) {
|
||||
const messages = allMessages[useGpuMode] = [];
|
||||
|
||||
vm.renderer.setUseGpuMode(useGpuMode);
|
||||
vm.greenFlag();
|
||||
const startTime = Date.now();
|
||||
|
||||
// wait for all threads to complete before moving on to the next mode
|
||||
while (vm.runtime.threads.some(thread => vm.runtime.isActiveThread(thread))) {
|
||||
if ((Date.now() - startTime) >= TIMEOUT) {
|
||||
// if we push the message after end, the failure from tap is not very useful:
|
||||
// "not ok test after end() was called"
|
||||
messages.unshift(`fail Threads still running after ${TIMEOUT}ms`);
|
||||
break;
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
}
|
||||
}
|
||||
|
||||
return allMessages;
|
||||
}, allGpuModes);
|
||||
|
||||
for (const gpuMode of allGpuModes) {
|
||||
test(`File: ${file}, GPU Mode: ${gpuMode}`, t => checkOneGpuMode(t, says[gpuMode]));
|
||||
}
|
||||
};
|
||||
|
||||
// immediately invoked async function to let us wait for each test to finish before starting the next.
|
||||
(async () => {
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage();
|
||||
|
||||
const files = fs.readdirSync(testDir())
|
||||
.filter(uri => uri.endsWith('.sb2') || uri.endsWith('.sb3'));
|
||||
|
||||
for (const file of files) {
|
||||
await testFile(file);
|
||||
await testFile(file, page);
|
||||
}
|
||||
|
||||
// close the browser window we used
|
||||
await chromeless.end();
|
||||
})();
|
||||
await browser.close();
|
||||
})().catch(err => {
|
||||
// Handle promise rejections by exiting with a nonzero code to ensure that tests don't erroneously pass
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user