mirror of
https://github.com/scratchfoundation/scratch-audio.git
synced 2024-12-22 05:53:43 -05:00
add Effect tests and mocks
This commit is contained in:
parent
6c8edf52b9
commit
e40e6e0b1b
7 changed files with 214 additions and 3 deletions
|
@ -24,8 +24,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"audio-context": "1.0.1",
|
"audio-context": "1.0.1",
|
||||||
"minilog": "^3.0.1",
|
"minilog": "^3.0.1",
|
||||||
"startaudiocontext": "1.2.1",
|
"startaudiocontext": "1.2.1"
|
||||||
"tap": "^12.0.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-core": "^6.24.1",
|
"babel-core": "^6.24.1",
|
||||||
|
@ -37,6 +36,7 @@
|
||||||
"json": "^9.0.6",
|
"json": "^9.0.6",
|
||||||
"travis-after-all": "^1.4.4",
|
"travis-after-all": "^1.4.4",
|
||||||
"webpack": "^4.8.0",
|
"webpack": "^4.8.0",
|
||||||
"webpack-cli": "^2.0.15"
|
"webpack-cli": "^2.0.15",
|
||||||
|
"tap": "^12.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
test/__mocks__/AudioContext.js
Normal file
20
test/__mocks__/AudioContext.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
const AudioNodeMock = require('./AudioNode');
|
||||||
|
const AudioParamMock = require('./AudioParam');
|
||||||
|
|
||||||
|
class AudioContextMock {
|
||||||
|
createGain () {
|
||||||
|
return new AudioNodeMock({
|
||||||
|
gain: new AudioParamMock({
|
||||||
|
default: 1,
|
||||||
|
min: -3,
|
||||||
|
max: 3
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createChannelMerger () {
|
||||||
|
return new AudioNodeMock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AudioContextMock;
|
12
test/__mocks__/AudioEngine.js
Normal file
12
test/__mocks__/AudioEngine.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
const AudioContextMock = require('./AudioContext');
|
||||||
|
const AudioTargetMock = require('./AudioTarget');
|
||||||
|
|
||||||
|
class AudioEngineMock extends AudioTargetMock {
|
||||||
|
constructor () {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.audioContext = new AudioContextMock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AudioEngineMock;
|
36
test/__mocks__/AudioNode.js
Normal file
36
test/__mocks__/AudioNode.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
class AudioNodeMock {
|
||||||
|
constructor (params) {
|
||||||
|
Object.assign(this, params);
|
||||||
|
|
||||||
|
this.connected = null;
|
||||||
|
this.connectedFrom = [];
|
||||||
|
this._testResult = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
connect (node) {
|
||||||
|
this.connected = node;
|
||||||
|
node.connectedFrom.push(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect () {
|
||||||
|
if (this.connected !== null) {
|
||||||
|
this.connectedFrom = this.connectedFrom.filter(connection => connection !== this);
|
||||||
|
}
|
||||||
|
this.connected = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_test (test, message, depth = 0) {
|
||||||
|
if (this.connected === null) {
|
||||||
|
this._testResult.push({test, message, depth});
|
||||||
|
} else {
|
||||||
|
this.connected._test(test, message, depth + 1);
|
||||||
|
this._testResult.push(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_result () {
|
||||||
|
return this._testResult.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AudioNodeMock;
|
7
test/__mocks__/AudioParam.js
Normal file
7
test/__mocks__/AudioParam.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class AudioParamMock {
|
||||||
|
setTargetAtTime (value /* , start, stop */) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AudioParamMock;
|
21
test/__mocks__/AudioTarget.js
Normal file
21
test/__mocks__/AudioTarget.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
const AudioNodeMock = require('./AudioNode');
|
||||||
|
|
||||||
|
class AudioTargetMock {
|
||||||
|
constructor () {
|
||||||
|
this.inputNode = new AudioNodeMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
connect (target) {
|
||||||
|
this.inputNode.connect(target.getInputNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
getInputNode () {
|
||||||
|
return this.inputNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSoundPlayers () {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AudioTargetMock;
|
115
test/effects/EffectShape.js
Normal file
115
test/effects/EffectShape.js
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
const tap = require('tap');
|
||||||
|
|
||||||
|
const PanEffect = require('../../src/effects/PanEffect');
|
||||||
|
const PitchEffect = require('../../src/effects/PitchEffect');
|
||||||
|
const VolumeEffect = require('../../src/effects/VolumeEffect');
|
||||||
|
|
||||||
|
const AudioEngine = require('../__mocks__/AudioEngine');
|
||||||
|
const AudioTarget = require('../__mocks__/AudioTarget');
|
||||||
|
|
||||||
|
const testEffect = (EffectClass, effectDepth) => {
|
||||||
|
tap.test(EffectClass.name, t1 => {
|
||||||
|
t1.plan(3);
|
||||||
|
|
||||||
|
t1.test('methods', t2 => {
|
||||||
|
t2.plan(7);
|
||||||
|
|
||||||
|
t2.ok(
|
||||||
|
'DEFAULT_VALUE' in EffectClass.prototype,
|
||||||
|
'has DEFAULT_VALUE'
|
||||||
|
);
|
||||||
|
t2.type(EffectClass.prototype.initialize, 'function', 'has initialize');
|
||||||
|
t2.type(EffectClass.prototype.set, 'function', 'has set');
|
||||||
|
t2.type(EffectClass.prototype.update, 'function', 'has update');
|
||||||
|
t2.type(EffectClass.prototype.clear, 'function', 'has clear');
|
||||||
|
t2.type(EffectClass.prototype.connect, 'function', 'has connect');
|
||||||
|
t2.type(EffectClass.prototype.dispose, 'function', 'has dispose');
|
||||||
|
|
||||||
|
t2.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t1.test('connect', t2 => {
|
||||||
|
t2.plan(6);
|
||||||
|
|
||||||
|
const engine = new AudioEngine();
|
||||||
|
const target = new AudioTarget();
|
||||||
|
let effect = new EffectClass(engine, target, null);
|
||||||
|
|
||||||
|
target.inputNode._test('stall', 'message does not move');
|
||||||
|
t2.equal(target.inputNode._result().depth, 0, 'message not sent');
|
||||||
|
t2.ok(!engine.inputNode._result(), 'message not received');
|
||||||
|
t2.ok(!engine.inputNode.connected, 'engine not connected');
|
||||||
|
|
||||||
|
effect.dispose();
|
||||||
|
effect = new EffectClass(engine, target, null);
|
||||||
|
const effect2 = new EffectClass(engine, target, effect);
|
||||||
|
effect2.connect(engine);
|
||||||
|
effect.connect(effect2);
|
||||||
|
|
||||||
|
target.inputNode._test('move', 'message does move');
|
||||||
|
t2.ok(!target.inputNode._result(), 'message sent');
|
||||||
|
t2.equal(engine.inputNode._result().depth, 1, 'message received');
|
||||||
|
t2.ok(engine.inputNode.connectedFrom.length, 'engine connected');
|
||||||
|
|
||||||
|
t2.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t1.test('lifecycle', t2 => {
|
||||||
|
t2.plan(18);
|
||||||
|
|
||||||
|
const engine = new AudioEngine();
|
||||||
|
const target = new AudioTarget();
|
||||||
|
let effect = new EffectClass(engine, target, null);
|
||||||
|
let effect2 = new EffectClass(engine, target, effect);
|
||||||
|
|
||||||
|
target.inputNode._test('stall', 'message does not move');
|
||||||
|
t2.equal(target.inputNode._result().depth, 0, 'message not sent');
|
||||||
|
t2.ok(!engine.inputNode._result(), 'message not received');
|
||||||
|
t2.ok(!engine.inputNode.connected, 'engine not connected');
|
||||||
|
|
||||||
|
effect2.connect(engine);
|
||||||
|
effect.connect(effect2);
|
||||||
|
|
||||||
|
target.inputNode._test('move', 'message does move');
|
||||||
|
t2.ok(!target.inputNode._result(), 'message sent');
|
||||||
|
t2.equal(engine.inputNode._result().depth, 1, 'message received');
|
||||||
|
t2.ok(engine.inputNode.connectedFrom.length, 'engine connected');
|
||||||
|
|
||||||
|
effect.set(effect.DEFAULT_VALUE - 1);
|
||||||
|
|
||||||
|
target.inputNode._test('move', 'message does move');
|
||||||
|
t2.ok(!target.inputNode._result(), 'message sent');
|
||||||
|
t2.equal(engine.inputNode._result().depth, 1 + effectDepth, 'message received');
|
||||||
|
t2.ok(engine.inputNode.connectedFrom.length, 'engine connected');
|
||||||
|
|
||||||
|
effect2.set(effect2.DEFAULT_VALUE - 1);
|
||||||
|
|
||||||
|
target.inputNode._test('move', 'message does move');
|
||||||
|
t2.ok(!target.inputNode._result(), 'message sent');
|
||||||
|
t2.equal(engine.inputNode._result().depth, 1 + (2 * effectDepth), 'message received');
|
||||||
|
t2.ok(engine.inputNode.connectedFrom.length, 'engine connected');
|
||||||
|
|
||||||
|
effect.clear();
|
||||||
|
|
||||||
|
target.inputNode._test('move', 'message does move');
|
||||||
|
t2.ok(!target.inputNode._result(), 'message sent');
|
||||||
|
t2.equal(engine.inputNode._result().depth, 1 + effectDepth, 'message received');
|
||||||
|
t2.ok(engine.inputNode.connectedFrom.length, 'engine connected');
|
||||||
|
|
||||||
|
effect2.clear();
|
||||||
|
|
||||||
|
target.inputNode._test('move', 'message does move');
|
||||||
|
t2.ok(!target.inputNode._result(), 'message sent');
|
||||||
|
t2.equal(engine.inputNode._result().depth, 1, 'message received');
|
||||||
|
t2.ok(engine.inputNode.connectedFrom.length, 'engine connected');
|
||||||
|
|
||||||
|
t2.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t1.end();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
testEffect(PanEffect, 3);
|
||||||
|
testEffect(PitchEffect, 0);
|
||||||
|
testEffect(VolumeEffect, 1);
|
Loading…
Reference in a new issue