Files
palemoon27/dom/camera/test/camera_common.js
T
roytam1 86a3aa0b54 import changes from `dev' branch of rmottola/Arctic-Fox:
- missing part of  Bug 1165772: P1. (7311039be4)
- missing gstreamer stuff (54a80d69b2)
- Bug 1214208: Do not use MP3Decoder on B2G. r=alfredo (0a19e7946e)
- Bug 1194014 - Remove redundant includes. r=jya (ccc3753113)
- Bug 1039639 - Add support for Flac on Firefox OS. r=cajbir (7d76197e07)
- Bug 875573 - Add video/x-m4v mime type. r=kentuckyfriedtakahe (6ec8af93e6)
- cleanup (6fb3d5dd26)
- Bug 1180621 - [FxOS] Enable VP9 codec for the Android version after KK. r=sotaro (58f7c2b657)
- Bug 1187247: [MSE] P2. Enable WebM in MediaSource. r=jya (2df0ee1f7a)
- Bug 1187247: [MSE] P1. Continue parsing MediaSegment if buffer starts with SimpleBlock/Block. r=kinetik (574475ed6f)
- Bug 1217170: P1. Rename functions to explicitly reflect what they are doing. r=kentuckyfriedtakahe (70c81a8179)
- Bug 1070216 - Split DOMMediaStream::InitStreamCommon into three. r=roc (1bda71cc88)
- Bug 1215582 - Rename Blacklist to Block list in GStreamerFormatReader. r=gerald (4f08077f5e)
- Bug 1170958 - Destroy track-locked MediaInputPorts when the track ends. r=roc (ff3922a2d6)
- Bug 1070216 - constify DOMMediaStream::Get[Audio/Video]Tracks(). r=roc (ba09f6f191)
- Bug 1070216 - Guard against adding a track owned by one MSG to a stream owned by another. r=padenot (a80deb8b30)
- Bug 1070216 - Implement MediaStream constructors. r=smaug,jib,padenot (3403ef2599)
- Bug 1070216 - Guard against a null MediaInputPort in DOMMediaStream::FindPlaybackDOMTrack(). r=roc (453a9ffbc1)
- Bug 1212783 - Expose TrackPort in DOMMediaStream.h r=roc (fb61c79ae7)
- Bug 1219711 - Ensure MediaStreamTrack.enabled propagates across peer connections. r=jesup (d9d1e54dae)
- Bug 1129051 - Fix double free in Camera Control Listener. Fix webrtc memory leak. r=aosmond (3e9b3bccfd)
- Bug 1152260 - Generate focused event for drivers that do not notify us when using continuous auto focus. r=mikeh (6c7bd42fdc)
- Bug 1175656 - Implement generation of recording posters in Gecko. r=dhylands,bz (51b2c66dc7)
- Bug 1187364 - Part 1. Add ability for camera to pause/resume recording. r=dhylands,bz (c54c735e37)
- Bug 1187364 - Part 2. Ensure that recording is resumed with a key frame. r=mchiang (c1c6048982)
- Bug 1187364 - Part 3. Fix missing end comment in WebIDL. r=me,bz (7faf106cc1)
- Bug 1212783 - Add a MediaStreamTrack to DOMCameraControl. r=aosmond (91e11efd3a)
- Bug 1124338 - Fix possible camera cached parameters invalidation from underlying driver modification. r=aosmond (dea67dc155)
- Bug 1196330 - Do not restart preview if configuration is unchanged. r=dhylands (097644f5d9)
- Bug 1215372 - Filter empty camera face detected events at gonk layer. r=dhylands (733efe50eb)
- Bug 1179726 - Prefer lower resolutions than 4kuhd as the default video recording profile. r=dhylands (27c71273dc)
- Bug 1222122 - Add picture size to verified parameters when reconfiguring the camera. r=dhylands (8c1fac6a4a)
- Bug 1141267 - register CameraThread with profiler, r=aosmond (299592a024)
- Bug 1008483 - removes the RW lock in CameraControlImpl and replaces it with a standard mutex. r=aosmond (45936cb90d)
- Bug 1008483 - Part 2. Readd missing nsPrintfCString.h include which has broken some local builds. r=me (9dd84b0f19)
- Bug 1191731 - Update poster API to allow application control over when poster is saved. r=bz, r=dhylands (73f9e7e0f4)
- Bug 1155648 - Fix documentation for DOMMediaStream::OnTracksAvailable. r=jesup (702828c304)
- Bug 1217170: [MSE] P2. Enable WebM/MSE on systems with no MP4/H264 support. r=kentuckyfriedtakahe (0b814b0708)
- Bug 1213177: Enable WebM on machines where H264 HW decoding is disabled. r=kentuckyfriedtakahe (e64da2ea24)
- add back some sps telemetry (52c2c64f5b)
- missing bit of Bug 1195073: [MSE/webm] P1 (9c45e82c3d)
- Bug 1150305 - sourcebuffer.buffered returns the same object if not changed. r=roc, r=bz, r=jya (6005d56c0c)
- Bug 1215447 - move flag setting from SeekStarted() to Seek(). r=roc. (a646b744c1)
- Bug 1119936 - Audio from FM Radio or Music app ceases to play when switching between front/back camera. r=roc (1a60aa7d69)
- Bug 1186806 - Part 1: Replace nsBaseHashtable::EnumerateRead() with iterators in HTMLFormControlsCollection. r=khuey (ccb8cb180a)
- Bug 1186806 - Part 2: Use NS_IMPL_CYCLE_COLLECTION_TRAVERSE instead of manual traversal in HTMLFormElement. r=khuey (57e6eabf1b)
- Bug 1186806 - Part 3: Replace nsBaseHashtable::EnumerateRead() with iterators in HTMLMediaElement. r=khuey (243ef6e83b)
- Bug 1186806 - Part 4: Replace nsBaseHashtable::EnumerateRead() with iterators in HTMLPropertiesCollection. r=khuey (499bdef85f)
- Bug 1163958 - Reduce the allocation in MediaStreamGraph - patch 3 CLOSED TREE (a557661df1)
- Bug 1219330 - Prevent the creation of TextureClient after shutdown. r=mattwoodrow (a6c047d54f)
- Bug 1205559: Make TextureChild/TextureClient thread-safe. r=nical (307c089631)
- missing bit of 1219330 (0e351ea419)
- nsRefPtr -> RefPtr (07ba248e69)
- Bug 1215023. Part 1 - make MediaDecoder::mOwner a const member. We will check mShuttingDown before calling functions of mOwner. r=kinetik. (da7f201815)
- Bug 1215023. Part 2 - remove null check of mOwner. We check mShuttingDown to know whether it is valid to call functions of mOwner. r=kinetik. (8d28a04bbe)
- Bug 1220558. Part 2 - remove unused members. r=jya. (d3a9ed8c68)
- Bug 1223599 - Remove the throttling argument from AbstractMediaDecoder::NotifyDataArrived(). r=jya. (320323ff1d)
- Bug 1194606 - Make MediaDecoderStateMachine capable of requesting different kind (decoded/raw) of media data. r=jya (1e2b6a5c44)
- Bug 1197075: P3. Decode frames ahead of MDSM requesting them. r=edwin This makes the media.*-decode-ahead pref performs more according to its name. We decode audio and video in advance so a MediaDataPromise can be resolved almost instantly. Default is 2. (b3f56447c4)
- Bug 1189964 - Fix bustage. r=bustage CLOSED TREE (afaa49b4b5)
- Bug 1212149 - e10s support for opening notification settings. r=wchen (f0e7778fb6)
- Bug 1215644 - Use child process volume service cache for available and storage status requests. r=dhylands (dfd49f2ef3)
- bug 1215552 - nsHttpConnectionMgr::PostEvent shouldnt manually ref count r=hurley (5e2f1886e6)
- Bug 1219392 - Capitalize mozilla::unused to avoid conflicts. r=froydnj (0c8bb7f15a)
- bug 1217834 - buzzfeed packet loss r=dragana (e9a60b605f)
- Bug 1168033 - Add a comment to nsHttpConnectionMgr.cpp explaining the assignment of attemptedOptimisticPipeline. r=mcmanus (2451996350)
- bug 1189645 - remove spdy telem r=hurley (cda90abbdb)
- Bug 1148268 - fixed misspelling attribute mActorDestoryed. r=dhyland. (3615d68765)
- Bug 1216031 - Make MediaDecoder::mVideoFrameContainer const. r=kinetik. (a3feb9d6bc)
- missing bits of  Bug 1165515 - Part 13-2 (009e32281f)
- Bug 1131473 - crash in -[NativeMenuItemTarget menuItemHit:]. r=spohl (ea2da6441c)
- Bug 1216416 - Fix -Wimplicit-fallthrough warnings in widget/cocoa. r=spohl (faaa390b20)
- Bug 1181977 - Firefox app menu contains only "Quit" in certain edgecases. r=spohl (0b9d912961)
2022-12-06 13:48:22 +08:00

468 lines
15 KiB
JavaScript

function isDefinedObj(obj) {
return typeof(obj) !== 'undefined' && obj != null;
}
function isDefined(obj) {
return typeof(obj) !== 'undefined';
}
/* This is a simple test suite class removing the need to
write a lot of boilerplate for camera tests. It can
manage the platform configurations for testing, any
cleanup required, and common actions such as fetching
the camera or waiting for the preview to be completed.
To create the suite:
var suite = new CameraTestSuite();
To add a test case to the suite:
suite.test('test-name', function() {
function startAutoFocus(p) {
return suite.camera.autoFocus();
}
return suite.getCamera()
.then(startAutoFocus, suite.rejectGetCamera);
});
Finally, to execute the test cases:
suite.setup()
.then(suite.run);
Behind the scenes, suite configured the native camera
to use the JS hardware, setup that hardware such that
the getCamera would succeed, got a camera control
reference and saved it to suite.camera, and after the
tests were finished, it reset any modified state,
released the camera object, and concluded the mochitest
appropriately.
*/
function CameraTestSuite() {
SimpleTest.waitForExplicitFinish();
this._window = window;
this._document = document;
this.viewfinder = document.getElementById('viewfinder');
this._tests = [];
this.hwType = '';
/* Ensure that the this pointer is bound to all functions so that
they may be used as promise resolve/reject handlers without any
special effort, permitting code like this:
getCamera().catch(suite.rejectGetCamera);
instead of:
getCamera().catch(suite.rejectGetCamera.bind(suite));
*/
this.setup = this._setup.bind(this);
this.teardown = this._teardown.bind(this);
this.test = this._test.bind(this);
this.run = this._run.bind(this);
this.waitPreviewStarted = this._waitPreviewStarted.bind(this);
this.waitParameterPush = this._waitParameterPush.bind(this);
this.initJsHw = this._initJsHw.bind(this);
this.getCamera = this._getCamera.bind(this);
this.setLowMemoryPlatform = this._setLowMemoryPlatform.bind(this);
this.logError = this._logError.bind(this);
this.expectedError = this._expectedError.bind(this);
this.expectedRejectGetCamera = this._expectedRejectGetCamera.bind(this);
this.expectedRejectConfigure = this._expectedRejectConfigure.bind(this);
this.expectedRejectAutoFocus = this._expectedRejectAutoFocus.bind(this);
this.expectedRejectTakePicture = this._expectedRejectTakePicture.bind(this);
this.expectedRejectStartRecording = this._expectedRejectStartRecording.bind(this);
this.expectedRejectStopRecording = this._expectedRejectStopRecording.bind(this);
this.rejectGetCamera = this._rejectGetCamera.bind(this);
this.rejectConfigure = this._rejectConfigure.bind(this);
this.rejectRelease = this._rejectRelease.bind(this);
this.rejectAutoFocus = this._rejectAutoFocus.bind(this);
this.rejectTakePicture = this._rejectTakePicture.bind(this);
this.rejectStartRecording = this._rejectStartRecording.bind(this);
this.rejectStopRecording = this._rejectStopRecording.bind(this);
this.rejectPauseRecording = this._rejectPauseRecording.bind(this);
this.rejectResumeRecording = this._rejectResumeRecording.bind(this);
this.rejectPreviewStarted = this._rejectPreviewStarted.bind(this);
var self = this;
this._window.addEventListener('beforeunload', function() {
if (isDefinedObj(self.viewfinder)) {
self.viewfinder.srcObject = null;
}
self.hw = null;
if (isDefinedObj(self.camera)) {
ok(false, 'window unload triggered camera release instead of test completion');
self.camera.release();
self.camera = null;
}
});
}
CameraTestSuite.prototype = {
camera: null,
hw: null,
_lowMemSet: false,
_reloading: false,
_setupPermission: function(permission) {
if (!SpecialPowers.hasPermission(permission, document)) {
info("requesting " + permission + " permission");
SpecialPowers.addPermission(permission, true, document);
this._reloading = true;
}
},
/* Returns a promise which is resolved when the test suite is ready
to be executing individual test cases. One may provide the expected
hardware type here if desired; the default is to use the JS test
hardware. Use '' for the native emulated camera hardware. */
_setup: function(hwType) {
/* Depending on how we run the mochitest, we may not have the necessary
permissions yet. If we do need to request them, then we have to reload
the window to ensure the reconfiguration propogated properly. */
this._setupPermission("camera");
this._setupPermission("device-storage:videos");
this._setupPermission("device-storage:videos-create");
this._setupPermission("device-storage:videos-write");
if (this._reloading) {
window.location.reload();
return Promise.reject();
}
info("has necessary permissions");
if (!isDefined(hwType)) {
hwType = 'hardware';
}
this._hwType = hwType;
return new Promise(function(resolve, reject) {
SpecialPowers.pushPrefEnv({'set': [['device.storage.prompt.testing', true]]}, function() {
SpecialPowers.pushPrefEnv({'set': [['camera.control.test.permission', true]]}, function() {
SpecialPowers.pushPrefEnv({'set': [['camera.control.test.enabled', hwType]]}, function() {
resolve();
});
});
});
});
},
/* Returns a promise which is resolved when all of the SpecialPowers
parameters that were set while testing are flushed. This includes
camera.control.test.enabled and camera.control.test.is_low_memory. */
_teardown: function() {
return new Promise(function(resolve, reject) {
SpecialPowers.flushPrefEnv(function() {
resolve();
});
});
},
/* Returns a promise which is resolved when the set low memory
parameter is set. If no value is given, it defaults to true.
This is intended to be used inside a test case at the beginning
of its promise chain to configure the platform as desired. */
_setLowMemoryPlatform: function(val) {
if (typeof(val) === 'undefined') {
val = true;
}
if (this._lowMemSet === val) {
return Promise.resolve();
}
var self = this;
return new Promise(function(resolve, reject) {
SpecialPowers.pushPrefEnv({'set': [['camera.control.test.is_low_memory', val]]}, function() {
self._lowMemSet = val;
resolve();
});
}).catch(function(e) {
return self.logError('set low memory ' + val + ' failed', e);
});
},
/* Add a test case to the test suite to be executed later. */
_test: function(aName, aCb) {
this._tests.push({
name: aName,
cb: aCb
});
},
/* Execute all test cases (after setup is called). */
_run: function() {
if (this._reloading) {
return;
}
var test = this._tests.shift();
var self = this;
if (test) {
info(test.name + ' started');
function runNextTest() {
self.run();
}
function resetLowMem() {
return self.setLowMemoryPlatform(false);
}
function postTest(pass) {
ok(pass, test.name + ' finished');
var camera = self.camera;
self.viewfinder.srcObject = null;
self.camera = null;
if (!isDefinedObj(camera)) {
return Promise.resolve();
}
function handler(e) {
ok(typeof(e) === 'undefined', 'camera released');
return Promise.resolve();
}
return camera.release().then(handler).catch(handler);
}
this.initJsHw();
var testPromise;
try {
testPromise = test.cb();
if (!isDefinedObj(testPromise)) {
testPromise = Promise.resolve();
}
} catch(e) {
ok(false, 'caught exception while running test: ' + e);
testPromise = Promise.reject(e);
}
testPromise
.then(function(p) {
return postTest(true);
}, function(e) {
self.logError('unhandled error', e);
return postTest(false);
})
.then(resetLowMem, resetLowMem)
.then(runNextTest, runNextTest);
} else {
ok(true, 'all tests completed');
var finish = SimpleTest.finish.bind(SimpleTest);
this.teardown().then(finish, finish);
}
},
/* If the JS hardware is in use, get (and possibly initialize)
the service XPCOM object. The native Gonk layers are able
to get it via the same mechanism. Save a reference to it
so that the test case may manipulate it as it sees fit in
this.hw. Minimal setup is done for the test hardware such
that the camera is able to be brought up without issue.
This function has no effect if the JS hardware is not used. */
_initJsHw: function() {
if (this._hwType === 'hardware') {
this.hw = SpecialPowers.Cc['@mozilla.org/cameratesthardware;1']
.getService(SpecialPowers.Ci.nsICameraTestHardware);
this.hw.reset(this._window);
/* Minimum parameters required to get camera started */
this.hw.params['preview-size'] = '320x240';
this.hw.params['preview-size-values'] = '320x240';
this.hw.params['picture-size-values'] = '320x240';
} else {
this.hw = null;
}
},
/* Returns a promise which resolves when the camera has
been successfully opened with the given name and
configuration. If no name is given, it uses the first
camera in the list from the camera manager. */
_getCamera: function(name, config) {
var cameraManager = navigator.mozCameras;
if (!isDefined(name)) {
name = cameraManager.getListOfCameras()[0];
}
var self = this;
return cameraManager.getCamera(name, config).then(
function(p) {
ok(isDefinedObj(p) && isDefinedObj(p.camera), 'got camera');
self.camera = p.camera;
/* Ensure a followup promise can verify config by
returning the same parameter again. */
return Promise.resolve(p);
}
);
},
/* Returns a promise which resolves when the camera has
successfully started the preview and is bound to the
given viewfinder object. Note that this requires that
a video element be present with the ID 'viewfinder'. */
_waitPreviewStarted: function() {
var self = this;
return new Promise(function(resolve, reject) {
function onPreviewStateChange(e) {
try {
if (e.newState === 'started') {
ok(true, 'viewfinder is ready and playing');
self.camera.removeEventListener('previewstatechange', onPreviewStateChange);
resolve();
}
} catch(e) {
reject(e);
}
}
if (!isDefinedObj(self.viewfinder)) {
reject(new Error('no viewfinder object'));
return;
}
self.viewfinder.srcObject = self.camera;
self.viewfinder.play();
self.camera.addEventListener('previewstatechange', onPreviewStateChange);
});
},
/* Returns a promise which resolves when the camera hardware
has received a push parameters request. This is useful
when setting camera parameters from the application and
you want confirmation when the operation is complete if
there is no asynchronous notification provided. */
_waitParameterPush: function() {
var self = this;
return new Promise(function(resolve, reject) {
self.hw.attach({
'pushParameters': function() {
self._window.setTimeout(resolve);
}
});
});
},
/* When an error occurs in the promise chain, all of the relevant rejection
functions will be triggered. Most of the time however we only want the
first rejection to be handled and then let the failure trickle down the
chain to terminate the test. There is no way to exit a promise chain
early so the convention is to handle the error in the first reject and
then give an empty error for subsequent reject handlers so they know
it is not for them.
For example:
function rejectSomething(e) {
return suite.logError('something call failed');
}
getCamera()
.then(, suite.rejectGetCamera)
.then(something)
.then(, rejectSomething)
If the getCamera promise is rejected, suite.rejectGetCamera reports an
error, but rejectSomething remains silent. */
_logError: function(msg, e) {
if (isDefined(e)) {
ok(false, msg + ': ' + e);
}
// Make sure the error is undefined for later handlers
return Promise.reject();
},
/* The reject handlers below are intended to be used
when a test case does not expect a particular call
to fail but otherwise does not require any special
handling of that situation beyond failing the test
case and logging why.*/
_rejectGetCamera: function(e) {
return this.logError('get camera failed', e);
},
_rejectConfigure: function(e) {
return this.logError('set configuration failed', e);
},
_rejectRelease: function(e) {
return this.logError('release camera failed', e);
},
_rejectAutoFocus: function(e) {
return this.logError('auto focus failed', e);
},
_rejectTakePicture: function(e) {
return this.logError('take picture failed', e);
},
_rejectStartRecording: function(e) {
return this.logError('start recording failed', e);
},
_rejectStopRecording: function(e) {
return this.logError('stop recording failed', e);
},
_rejectPauseRecording: function(e) {
return this.logError('pause recording failed', e);
},
_rejectResumeRecording: function(e) {
return this.logError('resume recording failed', e);
},
_rejectPreviewStarted: function(e) {
return this.logError('preview start failed', e);
},
/* The success handlers below are intended to be used
when a test case does not expect a particular call
to succed but otherwise does not require any special
handling of that situation beyond failing the test
case and logging why.*/
_expectedError: function(msg) {
ok(false, msg);
/* Since the original promise was technically resolved
we actually want to pass up a rejection to try and
end the test case sooner */
return Promise.reject();
},
_expectedRejectGetCamera: function(p) {
/* Copy handle to ensure it gets released at the end
of the test case */
self.camera = p.camera;
return this.expectedError('expected get camera to fail');
},
_expectedRejectConfigure: function(p) {
return this.expectedError('expected set configuration to fail');
},
_expectedRejectAutoFocus: function(p) {
return this.expectedError('expected auto focus to fail');
},
_expectedRejectTakePicture: function(p) {
return this.expectedError('expected take picture to fail');
},
_expectedRejectStartRecording: function(p) {
return this.expectedError('expected start recording to fail');
},
_expectedRejectStopRecording: function(p) {
return this.expectedError('expected stop recording to fail');
},
};
is(SpecialPowers.sanityCheck(), "foo", "SpecialPowers passed sanity check");