Bug 976802 - add support for testing fake CameraParameters, r=dhylands

This commit is contained in:
Mike Habicher 2014-02-28 17:51:26 -05:00
parent 6908b035ed
commit 7e59fa5afc
6 changed files with 283 additions and 51 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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");

View File

@ -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]

View 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>