mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Bug 976802 - add support for testing fake CameraParameters, r=dhylands
This commit is contained in:
parent
29fa261c10
commit
116c56d9ed
@ -147,15 +147,18 @@ GonkCameraParameters::Initialize()
|
||||
|
||||
rv = GetImpl(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, mExposureCompensationMin);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
NS_WARNING("Failed to initialize minimum exposure compensation");
|
||||
mExposureCompensationMin = 0;
|
||||
}
|
||||
rv = GetImpl(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, mExposureCompensationStep);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
NS_WARNING("Failed to initialize exposure compensation step size");
|
||||
mExposureCompensationStep = 0;
|
||||
}
|
||||
rv = GetListAsArray(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
// zoom is not supported
|
||||
mZoomRatios.Clear();
|
||||
}
|
||||
|
||||
mInitialized = true;
|
||||
@ -410,6 +413,11 @@ GonkCameraParameters::SetTranslated(uint32_t aKey, const double& aValue)
|
||||
|
||||
switch (aKey) {
|
||||
case CAMERA_PARAM_EXPOSURECOMPENSATION:
|
||||
if (mExposureCompensationStep == 0) {
|
||||
DOM_CAMERA_LOGE("Exposure compensation not supported, can't set %f\n", aValue);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from real value to a Gonk index, round
|
||||
* to the nearest step; index is 1-based.
|
||||
@ -422,34 +430,46 @@ GonkCameraParameters::SetTranslated(uint32_t aKey, const double& aValue)
|
||||
|
||||
case CAMERA_PARAM_ZOOM:
|
||||
{
|
||||
if (mZoomRatios.Length() == 0) {
|
||||
DOM_CAMERA_LOGE("Zoom not supported, can't set %fx\n", aValue);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from a real zoom multipler (e.g. 2.5x) to
|
||||
* the index of the nearest supported value.
|
||||
*/
|
||||
value = aValue * 100.0;
|
||||
|
||||
// mZoomRatios is sorted, so we can binary search it
|
||||
unsigned int bottom = 0;
|
||||
unsigned int top = mZoomRatios.Length() - 1;
|
||||
unsigned int middle;
|
||||
if (value < mZoomRatios[0]) {
|
||||
index = 0;
|
||||
} else if (value > mZoomRatios.LastElement()) {
|
||||
index = mZoomRatios.Length() - 1;
|
||||
} else {
|
||||
// mZoomRatios is sorted, so we can binary search it
|
||||
int bottom = 0;
|
||||
int top = mZoomRatios.Length() - 1;
|
||||
int middle;
|
||||
|
||||
while (bottom != top) {
|
||||
middle = (top + bottom) / 2;
|
||||
if (value == mZoomRatios[middle]) {
|
||||
// exact match
|
||||
break;
|
||||
}
|
||||
if (value > mZoomRatios[middle] && value < mZoomRatios[middle + 1]) {
|
||||
// the specified zoom value lies in this interval
|
||||
break;
|
||||
}
|
||||
if (value > mZoomRatios[middle]) {
|
||||
bottom = middle + 1;
|
||||
} else {
|
||||
top = middle - 1;
|
||||
while (top >= bottom) {
|
||||
middle = (top + bottom) / 2;
|
||||
if (value == mZoomRatios[middle]) {
|
||||
// exact match
|
||||
break;
|
||||
}
|
||||
if (value > mZoomRatios[middle] && value < mZoomRatios[middle + 1]) {
|
||||
// the specified zoom value lies in this interval
|
||||
break;
|
||||
}
|
||||
if (value > mZoomRatios[middle]) {
|
||||
bottom = middle + 1;
|
||||
} else {
|
||||
top = middle - 1;
|
||||
}
|
||||
}
|
||||
index = middle;
|
||||
}
|
||||
index = middle;
|
||||
DOM_CAMERA_LOGI("Zoom = %fx --> index = %d\n", aValue, index);
|
||||
}
|
||||
return SetImpl(CAMERA_PARAM_ZOOM, index);
|
||||
}
|
||||
@ -616,16 +636,19 @@ GonkCameraParameters::GetListAsArray(uint32_t aKey, nsTArray<T>& aArray)
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!p) {
|
||||
DOM_CAMERA_LOGW("Camera parameter %d not available (value is null)\n", aKey);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
if (*p == '\0') {
|
||||
DOM_CAMERA_LOGW("Camera parameter %d not available (value is empty string)\n", aKey);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
aArray.Clear();
|
||||
|
||||
// If there is no value available, just return the empty array.
|
||||
if (!p) {
|
||||
DOM_CAMERA_LOGI("Camera parameter %d not available (value is null)\n", aKey);
|
||||
return NS_OK;
|
||||
}
|
||||
if (*p == '\0') {
|
||||
DOM_CAMERA_LOGI("Camera parameter %d not available (value is empty string)\n", aKey);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const char* comma;
|
||||
|
||||
while (p) {
|
||||
|
@ -56,6 +56,33 @@ TestGonkCameraHardware::TestCase()
|
||||
return test;
|
||||
}
|
||||
|
||||
const nsCString
|
||||
TestGonkCameraHardware::GetExtraParameters()
|
||||
{
|
||||
/**
|
||||
* The contents of this pref are appended to the flattened string of
|
||||
* parameters stuffed into GonkCameraParameters by the camera library.
|
||||
* It consists of semicolon-delimited key=value pairs, e.g.
|
||||
*
|
||||
* focus-mode=auto;flash-mode=auto;preview-size=1024x768
|
||||
*
|
||||
* The unflattening process breaks this string up on semicolon boundaries
|
||||
* and sets an entry in a hashtable of strings with the token before
|
||||
* the equals sign as the key, and the token after as the value. Because
|
||||
* the string is parsed in order, key=value pairs occuring later in the
|
||||
* string will replace value pairs appearing earlier, making it easy to
|
||||
* inject fake, testable values into the parameters table.
|
||||
*
|
||||
* One constraint of this approach is that neither the key nor the value
|
||||
* may contain equals signs or semicolons. We don't enforce that here
|
||||
* so that we can also test correct handling of improperly-formatted values.
|
||||
*/
|
||||
const nsCString parameters = Preferences::GetCString("camera.control.test.hardware.gonk.parameters");
|
||||
DOM_CAMERA_LOGA("TestGonkCameraHardware : extra-parameters '%s'\n",
|
||||
parameters.get());
|
||||
return parameters;
|
||||
}
|
||||
|
||||
bool
|
||||
TestGonkCameraHardware::IsTestCaseInternal(const char* aTest, const char* aFile, int aLine)
|
||||
{
|
||||
@ -174,7 +201,14 @@ TestGonkCameraHardware::PullParameters(GonkCameraParameters& aParams)
|
||||
return static_cast<nsresult>(TestCaseError(UNKNOWN_ERROR));
|
||||
}
|
||||
|
||||
return GonkCameraHardware::PullParameters(aParams);
|
||||
String8 s = mCamera->getParameters();
|
||||
nsCString extra = GetExtraParameters();
|
||||
if (!extra.IsEmpty()) {
|
||||
s += ";";
|
||||
s += extra.get();
|
||||
}
|
||||
|
||||
return aParams.Unflatten(s);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -55,6 +55,7 @@ public:
|
||||
|
||||
protected:
|
||||
const nsCString TestCase();
|
||||
const nsCString GetExtraParameters();
|
||||
bool IsTestCaseInternal(const char* aTest, const char* aFile, int aLine);
|
||||
int TestCaseError(int aDefaultError);
|
||||
|
||||
|
@ -18,13 +18,41 @@ var CameraTest = (function() {
|
||||
* 'take-picture-process-failure' will simulate a failure of the
|
||||
* asynchronous picture-taking process, even if the initial API call
|
||||
* path seems to have succeeded.
|
||||
*
|
||||
* If 'camera.control.test.hardware.gonk.parameters' is set, it will cause
|
||||
* the contents of that string to be appended to the string of parameters
|
||||
* pulled from the Gonk camera library. This allows tests to inject fake
|
||||
* settings/capabilities for features not supported by the emulator. These
|
||||
* parameters are one or more semicolon-delimited key=value pairs, e.g. to
|
||||
* pretend the emulator supports zoom:
|
||||
*
|
||||
* zoom-ratios=100,150,200,300,400;max-zoom=4
|
||||
*
|
||||
* This means (of course) that neither the key not the value tokens can
|
||||
* contain either equals signs or semicolons. The test shim doesn't enforce
|
||||
* this so that we can test getting junk from the camera library as well.
|
||||
*/
|
||||
const PREF_TEST_ENABLED = "camera.control.test.enabled";
|
||||
const PREF_TEST_HARDWARE = "camera.control.test.hardware";
|
||||
const PREF_TEST_EXTRA_PARAMETERS = "camera.control.test.hardware.gonk.parameters";
|
||||
var oldTestEnabled;
|
||||
var oldTestHw;
|
||||
var testMode;
|
||||
|
||||
function testHardwareSetFakeParameters(parameters, callback) {
|
||||
SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_EXTRA_PARAMETERS, parameters]]}, function() {
|
||||
var setParams = SpecialPowers.getCharPref(PREF_TEST_EXTRA_PARAMETERS);
|
||||
ise(setParams, parameters, "Extra test parameters '" + setParams + "'");
|
||||
if (callback) {
|
||||
callback(setParams);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testHardwareClearFakeParameters(callback) {
|
||||
SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_EXTRA_PARAMETERS]]}, callback);
|
||||
}
|
||||
|
||||
function testHardwareSet(test, callback) {
|
||||
SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_HARDWARE, test]]}, function() {
|
||||
var setTest = SpecialPowers.getCharPref(PREF_TEST_HARDWARE);
|
||||
@ -58,6 +86,8 @@ var CameraTest = (function() {
|
||||
} catch(e) { }
|
||||
testMode = {
|
||||
set: testHardwareSet,
|
||||
setFakeParameters: testHardwareSetFakeParameters,
|
||||
clearFakeParameters: testHardwareClearFakeParameters,
|
||||
done: testHardwareDone
|
||||
};
|
||||
if (callback) {
|
||||
@ -68,32 +98,40 @@ var CameraTest = (function() {
|
||||
}
|
||||
|
||||
function testEnd(callback) {
|
||||
function allDone(cb) {
|
||||
function cb2() {
|
||||
SimpleTest.finish();
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
// A chain of clean-up functions....
|
||||
function allCleanedUp() {
|
||||
SimpleTest.finish();
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
function cleanUpTestEnabled() {
|
||||
var next = allCleanedUp;
|
||||
if (oldTestEnabled) {
|
||||
SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_ENABLED, oldTestEnabled]]}, cb2);
|
||||
SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_ENABLED, oldTestEnabled]]}, next);
|
||||
} else {
|
||||
SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_ENABLED]]}, cb2);
|
||||
SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_ENABLED]]}, next);
|
||||
}
|
||||
}
|
||||
function cleanUpTest() {
|
||||
var next = cleanUpTestEnabled;
|
||||
if (testMode) {
|
||||
testMode.done(next);
|
||||
testMode = null;
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
function cleanUpExtraParameters() {
|
||||
var next = cleanUpTest;
|
||||
if (testMode) {
|
||||
testMode.clearFakeParameters(next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
if (testMode) {
|
||||
testMode.done(function() {
|
||||
allDone(callback);
|
||||
});
|
||||
testMode = null;
|
||||
} else {
|
||||
allDone(function() {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
cleanUpExtraParameters();
|
||||
}
|
||||
|
||||
ise(SpecialPowers.sanityCheck(), "foo", "SpecialPowers passed sanity check");
|
||||
|
@ -7,3 +7,4 @@ support-files = camera_common.js
|
||||
[test_camera_hardware_init_failure.html]
|
||||
[test_camera_hardware_failures.html]
|
||||
[test_bug975472.html]
|
||||
[test_camera_fake_parameters.html]
|
||||
|
135
dom/camera/test/test_camera_fake_parameters.html
Normal file
135
dom/camera/test/test_camera_fake_parameters.html
Normal file
@ -0,0 +1,135 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for CameraParameters we need to fake</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=976802">Mozilla Bug 976802</a>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
}
|
||||
};
|
||||
|
||||
var cameraObj = null;
|
||||
|
||||
// Shorthand functions
|
||||
function end() {
|
||||
CameraTest.end();
|
||||
}
|
||||
function next() {
|
||||
CameraTest.next();
|
||||
}
|
||||
function run() {
|
||||
CameraTest.run();
|
||||
}
|
||||
|
||||
function onError(e) {
|
||||
ok(false, "Error" + JSON.stringify(e));
|
||||
}
|
||||
|
||||
// The array of tests
|
||||
var tests = [
|
||||
{
|
||||
key: "fake-zoom",
|
||||
prep: function setupFakeZoom(test) {
|
||||
test.setFakeParameters("zoom-ratios=100,150,200,300,400;max-zoom=4", function() {
|
||||
run();
|
||||
});
|
||||
},
|
||||
test: function testFakeZoom(cam, cap) {
|
||||
ok(cap.zoomRatios.length == 5, "zoom ratios length = " + cap.zoomRatios.length);
|
||||
|
||||
// test individual zoom ratios
|
||||
cap.zoomRatios.forEach(function(zoom, index) {
|
||||
cam.zoom = zoom;
|
||||
ok(cam.zoom === zoom,
|
||||
"zoom[" + index + "] = " + zoom + "x, cam.zoom = " + cam.zoom + "x");
|
||||
});
|
||||
|
||||
// test below-lower-bound zoom ratio
|
||||
var zoom = cap.zoomRatios[0] - 0.1;
|
||||
cam.zoom = zoom;
|
||||
ok(cam.zoom === cap.zoomRatios[0],
|
||||
zoom + "x zoom clamps to minimum: " +
|
||||
cap.zoomRatios[0] + "x, cam.zoom = " + cam.zoom + "x");
|
||||
|
||||
// test above-upper-bound zoom ratio
|
||||
zoom = cap.zoomRatios.slice(-1)[0] + 1.0;
|
||||
cam.zoom = zoom;
|
||||
ok(cam.zoom === cap.zoomRatios.slice(-1)[0],
|
||||
zoom + "x zoom clamps to maximum: " + cap.zoomRatios.slice(-1)[0] +
|
||||
"x, cam.zoom = " + cam.zoom + "x");
|
||||
|
||||
// test snapping to supported zoom ratio
|
||||
if (cap.zoomRatios.length > 1) {
|
||||
zoom = (cap.zoomRatios[0] + cap.zoomRatios[1]) / 2;
|
||||
cam.zoom = zoom;
|
||||
ok(cam.zoom === cap.zoomRatios[0],
|
||||
zoom + "x zoom rounded down to: " + cap.zoomRatios[0] +
|
||||
"x, cam.zoom = " + cam.zoom + "x");
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
var testGenerator = function() {
|
||||
for (var i = 0; i < tests.length; ++i ) {
|
||||
yield tests[i];
|
||||
}
|
||||
}();
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
document.getElementById('viewfinder').mozSrcObject = null;
|
||||
cameraObj.release();
|
||||
cameraObj = null;
|
||||
});
|
||||
|
||||
CameraTest.begin("hardware", function(test) {
|
||||
function onError(error) {
|
||||
ok(false, "getCamera() failed with: " + error);
|
||||
end();
|
||||
}
|
||||
|
||||
CameraTest.next = function() {
|
||||
try {
|
||||
var t = testGenerator.next();
|
||||
info("test: " + t.key);
|
||||
function onSuccess(camera, config) {
|
||||
cameraObj = camera;
|
||||
t.test(camera, camera.capabilities);
|
||||
}
|
||||
CameraTest.run = function() {
|
||||
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);
|
||||
};
|
||||
t.prep(test);
|
||||
} catch(e) {
|
||||
if (e instanceof StopIteration) {
|
||||
end();
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
next();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user