Bug 996238 - Move more of the test code to promises. r=jib

This commit is contained in:
Martin Thomson 2015-04-01 11:21:06 -07:00
parent daceba7bd8
commit f268bbf98c
7 changed files with 97 additions and 114 deletions

View File

@ -13,6 +13,46 @@
return good;
}
function mkElement(type) {
// This makes an unattached element.
// It's not rendered to save the cycles that costs on b2g emulator
// and it gets dropped (and GC'd) when the test is done.
var e = document.createElement(type);
e.width = 32;
e.height = 24;
document.getElementById('display').appendChild(e);
return e;
}
// Runs checkFunc until it reports success.
// This is kludgy, but you have to wait for media to start flowing, and it
// can't be any old media, it has to include real data, for which we have no
// reliable signals to use as a trigger.
function periodicCheck(checkFunc) {
var resolve;
var done = false;
// This returns a function so that we create 10 closures in the loop, not
// one; and so that the timers don't all start straight away
var waitAndCheck = counter => () => {
if (done) {
return Promise.resolve();
}
return new Promise(r => setTimeout(r, 200 << counter))
.then(() => {
if (checkFunc()) {
done = true;
resolve();
}
});
};
var chain = Promise.resolve();
for (var i = 0; i < 10; ++i) {
chain = chain.then(waitAndCheck(i));
}
return new Promise(r => resolve = r);
}
function isSilence(audioData) {
var silence = true;
for (var i = 0; i < audioData.length; ++i) {
@ -23,97 +63,46 @@
return silence;
}
function periodicCheck(type, checkFunc, successMessage, done) {
var num = 0;
var timeout;
function periodic() {
if (checkFunc()) {
ok(true, type + ' is ' + successMessage);
done();
} else {
setupNext();
}
}
function setupNext() {
// exponential backoff on the timer
// on a very slow system (like the b2g emulator) a long timeout is
// necessary, but we want to run fast if we can
timeout = setTimeout(periodic, 200 << num);
num++;
}
function checkAudio(constraintApplied, stream) {
var audio = mkElement('audio');
audio.mozSrcObject = stream;
audio.play();
setupNext();
return function cancel() {
if (timeout) {
ok(false, type + ' (' + successMessage + ')' +
' failed after waiting full duration');
clearTimeout(timeout);
done();
}
};
}
function checkAudio(constraintApplied, stream, done) {
var context = new AudioContext();
var source = context.createMediaStreamSource(stream);
var analyser = context.createAnalyser();
source.connect(analyser);
analyser.connect(context.destination);
function testAudio() {
return periodicCheck(() => {
var sampleCount = analyser.frequencyBinCount;
info('got some audio samples: ' + sampleCount);
var bucket = new ArrayBuffer(sampleCount);
var view = new Uint8Array(bucket);
analyser.getByteTimeDomainData(view);
var buffer = new Uint8Array(sampleCount);
analyser.getByteTimeDomainData(buffer);
var silent = check(constraintApplied, isSilence(view), 'be silence for audio');
var silent = check(constraintApplied, isSilence(buffer),
'be silence for audio');
return sampleCount > 0 && silent;
}
function disconnect() {
}).then(() => {
source.disconnect();
analyser.disconnect();
done();
}
return periodicCheck('audio', testAudio,
(constraintApplied ? '' : 'not ') + 'silent', disconnect);
audio.pause();
ok(true, 'audio is ' + (constraintApplied ? '' : 'not ') + 'silent');
});
}
function mkElement(type) {
// this makes an unattached element
// it's not rendered to save the cycles that costs on b2g emulator
// and it gets droped (and GC'd) when the test is done
var e = document.createElement(type);
e.width = 32;
e.height = 24;
return e;
}
function checkVideo(constraintApplied, stream, done) {
function checkVideo(constraintApplied, stream) {
var video = mkElement('video');
video.mozSrcObject = stream;
var ready = false;
video.onplaying = function() {
ready = true;
}
video.play();
function tryToRenderToCanvas() {
if (!ready) {
info('waiting for video to start');
return false;
}
return periodicCheck(() => {
try {
// every try needs a new canvas, otherwise a taint from an earlier call
// will affect subsequent calls
var canvas = mkElement('canvas');
var ctx = canvas.getContext('2d');
// have to guard drawImage with the try as well, due to bug 879717
// if we get an error, this round fails, but that failure is usually
// just transitory
// Have to guard drawImage with the try as well, due to bug 879717. If
// we get an error, this round fails, but that failure is usually just
// transitory.
ctx.drawImage(video, 0, 0);
ctx.getImageData(0, 0, 1, 1);
return check(constraintApplied, false, 'throw on getImageData for video');
@ -121,10 +110,10 @@
return check(constraintApplied, e.name === 'SecurityError',
'get a security error: ' + e.name);
}
}
return periodicCheck('video', tryToRenderToCanvas,
(constraintApplied ? '' : 'not ') + 'protected', done);
}).then(() => {
video.pause();
ok(true, 'video is ' + (constraintApplied ? '' : 'not ') + 'protected');
});
}
global.audioIsSilence = checkAudio;

View File

@ -1 +0,0 @@
martin@Martins-MacBook-Pro.local.287

View File

@ -23,27 +23,30 @@ function identityPcTest(remoteOptions) {
fake: true,
peerIdentity: id1
}]);
test.setIdentityProvider(test.pcLocal, 'test1.example.com', 'idp.html');
test.setIdentityProvider(test.pcRemote, 'test2.example.com', 'idp.html');
test.pcLocal.setIdentityProvider('test1.example.com', 'idp.js');
test.pcRemote.setIdentityProvider('test2.example.com', 'idp.js');
test.chain.append([
function PEER_IDENTITY_IS_SET_CORRECTLY(test) {
// no need to wait to check identity in this case,
// setRemoteDescription should wait for the IdP to complete
function checkIdentity(pc, pfx, idp, name) {
is(pc.peerIdentity.idp, idp, pfx + "IdP is correct");
is(pc.peerIdentity.name, name + "@" + idp, pfx + "identity is correct");
return pc.peerIdentity.then(peerInfo => {
is(peerInfo.idp, idp, pfx + "IdP check");
is(peerInfo.name, name + "@" + idp, pfx + "identity check");
});
}
checkIdentity(test.pcLocal._pc, "local: ", "test2.example.com", "someone");
checkIdentity(test.pcRemote._pc, "remote: ", "test1.example.com", "someone");
return Promise.all([
checkIdentity(test.pcLocal._pc, "local: ", "test2.example.com", "someone"),
checkIdentity(test.pcRemote._pc, "remote: ", "test1.example.com", "someone")
]);
},
function REMOTE_STREAMS_ARE_RESTRICTED(test) {
var remoteStream = test.pcLocal._pc.getRemoteStreams()[0];
return Promise.all([
new Promise(done => audioIsSilence(true, remoteStream, done)),
new Promise(done => videoIsBlack(true, remoteStream, done))
audioIsSilence(true, remoteStream),
videoIsBlack(true, remoteStream)
]);
}
]);

View File

@ -15,9 +15,9 @@ createHTML({
});
function theTest() {
// override the remote media capture options to remove isolation
// for the remote party; the test verifies that the media it receives
// on the local side is isolated
// Override the remote media capture options to remove isolation for the
// remote party; the test verifies that the media it receives on the local
// side is isolated anyway.
identityPcTest({
audio: true,
video: true,

View File

@ -28,6 +28,8 @@ const signalingStateTransitions = {
"closed": []
}
var wait = (time) => new Promise(r => setTimeout(r, time));
/**
* This class provides a state checker for media elements which store
* a media stream to check for media attribute state and events fired.
@ -62,8 +64,8 @@ function MediaElementChecker(element) {
// If time has passed, then track that and remove the timeupdate event
// listener.
if(element.mozSrcObject && element.mozSrcObject.currentTime > 0 &&
element.currentTime > 0) {
if (element.mozSrcObject && element.mozSrcObject.currentTime > 0 &&
element.currentTime > 0) {
info('time passed for media element ' + elementId);
this.timePassed = true;
this.element.removeEventListener('timeupdate', timeUpdateCallback,

View File

@ -557,5 +557,3 @@ var addRenegotiationAnswerer = (chain, commands, checks) => {
});
addRenegotiation(chain, commands, checks);
};

View File

@ -7,41 +7,33 @@
<body>
<pre id="test">
<script type="application/javascript">
createHTML({ title: "Test getUserMedia peerIdentity Constraint", bug: "942367" });
createHTML({
title: "Test getUserMedia peerIdentity Constraint",
bug: "942367"
});
function theTest() {
function testPeerIdentityConstraint(withConstraint, done) {
function testPeerIdentityConstraint(withConstraint) {
var config = { audio: true, video: true, fake: true };
if (withConstraint) {
config.peerIdentity = 'user@example.com';
}
info('getting media with constraints: ' + JSON.stringify(config));
navigator.mediaDevices.getUserMedia(config).then(function(stream) {
var oneDone = false;
function checkDone() {
if (oneDone) {
done();
}
oneDone = true;
}
var cancelAudioCheck = audioIsSilence(withConstraint, stream, checkDone);
var cancelVideoCheck = videoIsBlack(withConstraint, stream, checkDone);
setTimeout(cancelAudioCheck, 3*60*1000);
setTimeout(cancelVideoCheck, 3*60*1000);
})
.catch(function(e) {
ok(false, 'gUM error: ' + e);
});
return getUserMedia(config)
.then(stream => Promise.all([
audioIsSilence(withConstraint, stream),
videoIsBlack(withConstraint, stream)
]));
};
// without constraint
testPeerIdentityConstraint(false, function() {
// with the constraint
testPeerIdentityConstraint(true, SimpleTest.finish.bind(SimpleTest));
});
// both without and with the constraint
testPeerIdentityConstraint(false)
.then(() => testPeerIdentityConstraint(true))
.catch(e => ok(false, 'error in test: ' + e))
.then(() => SimpleTest.finish())
.catch(e => ok(false, 'something is really messed up: ' + e));
}
runTest(theTest);
SimpleTest.waitForExplicitFinish();
</script>
</pre>