gecko-dev/dom/icc/tests/marionette/head.js

460 строки
13 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
const PREF_KEY_RIL_DEBUGGING_ENABLED = "ril.debugging.enabled";
// The pin code hard coded in emulator is "0000".
const DEFAULT_PIN = "0000";
// The puk code hard coded in emulator is "12345678".
const DEFAULT_PUK = "12345678";
const WHT = 0xFFFFFFFF;
const BLK = 0x000000FF;
const RED = 0xFF0000FF;
const GRN = 0x00FF00FF;
const BLU = 0x0000FFFF;
const TSP = 0;
// Basic Image, see record number 1 in EFimg.
const BASIC_ICON = {
width: 8,
height: 8,
codingScheme: "basic",
pixels: [WHT, WHT, WHT, WHT, WHT, WHT, WHT, WHT,
BLK, BLK, BLK, BLK, BLK, BLK, WHT, WHT,
WHT, BLK, WHT, BLK, BLK, WHT, BLK, WHT,
WHT, BLK, BLK, WHT, WHT, BLK, BLK, WHT,
WHT, BLK, BLK, WHT, WHT, BLK, BLK, WHT,
WHT, BLK, WHT, BLK, BLK, WHT, BLK, WHT,
WHT, WHT, BLK, BLK, BLK, BLK, WHT, WHT,
WHT, WHT, WHT, WHT, WHT, WHT, WHT, WHT]
};
// Color Image, see record number 3 in EFimg.
const COLOR_ICON = {
width: 8,
height: 8,
codingScheme: "color",
pixels: [BLU, BLU, BLU, BLU, BLU, BLU, BLU, BLU,
BLU, RED, RED, RED, RED, RED, RED, BLU,
BLU, RED, GRN, GRN, GRN, RED, RED, BLU,
BLU, RED, RED, GRN, GRN, RED, RED, BLU,
BLU, RED, RED, GRN, GRN, RED, RED, BLU,
BLU, RED, RED, GRN, GRN, GRN, RED, BLU,
BLU, RED, RED, RED, RED, RED, RED, BLU,
BLU, BLU, BLU, BLU, BLU, BLU, BLU, BLU]
};
// Color Image with Transparency, see record number 5 in EFimg.
const COLOR_TRANSPARENCY_ICON = {
width: 8,
height: 8,
codingScheme: "color-transparency",
pixels: [TSP, TSP, TSP, TSP, TSP, TSP, TSP, TSP,
TSP, RED, RED, RED, RED, RED, RED, TSP,
TSP, RED, GRN, GRN, GRN, RED, RED, TSP,
TSP, RED, RED, GRN, GRN, RED, RED, TSP,
TSP, RED, RED, GRN, GRN, RED, RED, TSP,
TSP, RED, RED, GRN, GRN, GRN, RED, TSP,
TSP, RED, RED, RED, RED, RED, RED, TSP,
TSP, TSP, TSP, TSP, TSP, TSP, TSP, TSP]
};
/**
* Helper function for checking stk icon.
*/
function isIcons(aIcons, aExpectedIcons) {
is(aIcons.length, aExpectedIcons.length, "icons.length");
for (let i = 0; i < aIcons.length; i++) {
let icon = aIcons[i];
let expectedIcon = aExpectedIcons[i];
is(icon.width, expectedIcon.width, "icon.width");
is(icon.height, expectedIcon.height, "icon.height");
is(icon.codingScheme, expectedIcon.codingScheme, "icon.codingScheme");
is(icon.pixels.length, expectedIcon.pixels.length);
for (let j = 0; j < icon.pixels.length; j++) {
is(icon.pixels[j], expectedIcon.pixels[j], "icon.pixels[" + j + "]");
}
}
}
/**
* Helper function for checking stk text.
*/
function isStkText(aStkText, aExpectedStkText) {
is(aStkText.text, aExpectedStkText.text, "stkText.text");
if (aExpectedStkText.icons) {
is(aStkText.iconSelfExplanatory, aExpectedStkText.iconSelfExplanatory,
"stkText.iconSelfExplanatory");
isIcons(aStkText.icons, aExpectedStkText.icons);
}
}
let _pendingEmulatorCmdCount = 0;
/**
* Send emulator command with safe guard.
*
* We should only call |finish()| after all emulator command transactions
* end, so here comes with the pending counter. Resolve when the emulator
* gives positive response, and reject otherwise.
*
* Fulfill params:
* result -- an array of emulator response lines.
* Reject params:
* result -- an array of emulator response lines.
*
* @param aCommand
* A string command to be passed to emulator through its telnet console.
*
* @return A deferred promise.
*/
function runEmulatorCmdSafe(aCommand) {
return new Promise(function(aResolve, aReject) {
++_pendingEmulatorCmdCount;
runEmulatorCmd(aCommand, function(aResult) {
--_pendingEmulatorCmdCount;
ok(true, "Emulator response: " + JSON.stringify(aResult));
if (Array.isArray(aResult) &&
aResult[aResult.length - 1] === "OK") {
aResolve(aResult);
} else {
aReject(aResult);
}
});
});
}
/**
* Send stk proactive pdu.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @param aPdu
*
* @return A deferred promise.
*/
function sendEmulatorStkPdu(aPdu) {
let cmd = "stk pdu " + aPdu;
return runEmulatorCmdSafe(cmd);
}
let workingFrame;
let iccManager;
/**
* Push required permissions and test if
* |navigator.mozIccManager| exists. Resolve if it does,
* reject otherwise.
*
* Fulfill params:
* iccManager -- an reference to navigator.mozIccManager.
*
* Reject params: (none)
*
* @param aAdditonalPermissions [optional]
* An array of permission strings other than "mobileconnection" to be
* pushed. Default: empty string.
*
* @return A deferred promise.
*/
function ensureIccManager(aAdditionalPermissions) {
return new Promise(function(aResolve, aReject) {
aAdditionalPermissions = aAdditionalPermissions || [];
if (aAdditionalPermissions.indexOf("mobileconnection") < 0) {
aAdditionalPermissions.push("mobileconnection");
}
let permissions = [];
for (let perm of aAdditionalPermissions) {
permissions.push({ "type": perm, "allow": 1, "context": document });
}
SpecialPowers.pushPermissions(permissions, function() {
ok(true, "permissions pushed: " + JSON.stringify(permissions));
// Permission changes can't change existing Navigator.prototype
// objects, so grab our objects from a new Navigator.
workingFrame = document.createElement("iframe");
workingFrame.addEventListener("load", function load() {
workingFrame.removeEventListener("load", load);
iccManager = workingFrame.contentWindow.navigator.mozIccManager;
if (iccManager) {
ok(true, "navigator.mozIccManager is instance of " + iccManager.constructor);
} else {
ok(true, "navigator.mozIccManager is undefined");
}
if (iccManager instanceof MozIccManager) {
aResolve(iccManager);
} else {
aReject();
}
});
document.body.appendChild(workingFrame);
});
});
}
/**
* Get MozIcc by IccId
*
* @param aIccId [optional]
* Default: The first item of |aIccManager.iccIds|.
*
* @return A MozIcc.
*/
function getMozIcc(aIccId) {
aIccId = aIccId || iccManager.iccIds[0];
if (!aIccId) {
ok(true, "iccManager.iccIds[0] is undefined");
return null;
}
return iccManager.getIccById(aIccId);
}
/**
* Get MozMobileConnection by ServiceId
*
* @param aServiceId [optional]
* A numeric DSDS service id. Default: 0 if not indicated.
*
* @return A MozMobileConnection.
*/
function getMozMobileConnectionByServiceId(aServiceId) {
aServiceId = aServiceId || 0;
return workingFrame.contentWindow.navigator.mozMobileConnections[aServiceId];
}
/**
* Set radio enabling state.
*
* Resolve no matter the request succeeds or fails. Never reject.
*
* Fulfill params: (none)
*
* @param aEnabled
* A boolean state.
* @param aServiceId [optional]
* A numeric DSDS service id.
*
* @return A deferred promise.
*/
function setRadioEnabled(aEnabled, aServiceId) {
return getMozMobileConnectionByServiceId(aServiceId).setRadioEnabled(aEnabled)
.then(() => {
ok(true, "setRadioEnabled " + aEnabled + " on " + aServiceId + " success.");
}, (aError) => {
ok(false, "setRadioEnabled " + aEnabled + " on " + aServiceId + " " +
aError.name);
});
}
/**
* Wait for one named event.
*
* Resolve if that named event occurs. Never reject.
*
* Fulfill params: the DOMEvent passed.
*
* @param aEventTarget
* An EventTarget object.
* @param aEventName
* A string event name.
* @param aMatchFun [optional]
* A matching function returns true or false to filter the event.
*
* @return A deferred promise.
*/
function waitForTargetEvent(aEventTarget, aEventName, aMatchFun) {
return new Promise(function(aResolve, aReject) {
aEventTarget.addEventListener(aEventName, function onevent(aEvent) {
if (!aMatchFun || aMatchFun(aEvent)) {
aEventTarget.removeEventListener(aEventName, onevent);
ok(true, "Event '" + aEventName + "' got.");
aResolve(aEvent);
}
});
});
}
/**
* Wait for one named system message.
*
* Resolve if that named message is received. Never reject.
*
* Fulfill params: the message passed.
*
* @param aEventName
* A string message name.
* @param aMatchFun [optional]
* A matching function returns true or false to filter the message. If no
* matching function passed the promise is resolved after receiving the
* first message.
*
* @return A deferred promise.
*/
function waitForSystemMessage(aMessageName, aMatchFun) {
let target = workingFrame.contentWindow.navigator;
return new Promise(function(aResolve, aReject) {
target.mozSetMessageHandler(aMessageName, function(aMessage) {
if (!aMatchFun || aMatchFun(aMessage)) {
target.mozSetMessageHandler(aMessageName, null);
ok(true, "System message '" + aMessageName + "' got.");
aResolve(aMessage);
}
});
});
}
/**
* Set radio enabling state and wait for "radiostatechange" event.
*
* Resolve if radio state changed to the expected one. Never reject.
*
* Fulfill params: (none)
*
* @param aEnabled
* A boolean state.
* @param aServiceId [optional]
* A numeric DSDS service id. Default: the one indicated in
* start*TestCommon() or 0 if not indicated.
*
* @return A deferred promise.
*/
function setRadioEnabledAndWait(aEnabled, aServiceId) {
let mobileConn = getMozMobileConnectionByServiceId(aServiceId);
let promises = [];
promises.push(waitForTargetEvent(mobileConn, "radiostatechange", function() {
// To ignore some transient states, we only resolve that deferred promise
// when |radioState| equals to the expected one.
return mobileConn.radioState === (aEnabled ? "enabled" : "disabled");
}));
promises.push(setRadioEnabled(aEnabled, aServiceId));
return Promise.all(promises);
}
/**
* Restart radio and wait card state changes to expected one.
*
* Resolve if card state changed to the expected one. Never reject.
*
* Fulfill params: (none)
*
* @param aCardState
* Expected card state.
*
* @return A deferred promise.
*/
function restartRadioAndWait(aCardState) {
return setRadioEnabledAndWait(false).then(() => {
let promises = [];
promises.push(waitForTargetEvent(iccManager, "iccdetected")
.then((aEvent) => {
let icc = getMozIcc(aEvent.iccId);
if (icc.cardState !== aCardState) {
return waitForTargetEvent(icc, "cardstatechange", function() {
return icc.cardState === aCardState;
});
}
}));
promises.push(setRadioEnabledAndWait(true));
return Promise.all(promises);
});
}
/**
* Enable/Disable PIN-lock.
*
* Fulfill params: (none)
* Reject params:
* An object contains error name and remaining retry count.
* @see IccCardLockError
*
* @param aIcc
* A MozIcc object.
* @param aEnabled
* A boolean state.
*
* @return A deferred promise.
*/
function setPinLockEnabled(aIcc, aEnabled) {
let options = {
lockType: "pin",
enabled: aEnabled,
pin: DEFAULT_PIN
};
return aIcc.setCardLock(options);
}
/**
* Wait for pending emulator transactions and call |finish()|.
*/
function cleanUp() {
// Use ok here so that we have at least one test run.
ok(true, ":: CLEANING UP ::");
waitFor(finish, function() {
return _pendingEmulatorCmdCount === 0;
});
}
/**
* Basic test routine helper for icc tests.
*
* This helper does nothing but clean-ups.
*
* @param aTestCaseMain
* A function that takes no parameter.
*/
function startTestBase(aTestCaseMain) {
// Turn on debugging pref.
let debugPref = SpecialPowers.getBoolPref(PREF_KEY_RIL_DEBUGGING_ENABLED);
SpecialPowers.setBoolPref(PREF_KEY_RIL_DEBUGGING_ENABLED, true);
Promise.resolve()
.then(aTestCaseMain)
.catch((aError) => {
ok(false, "promise rejects during test: " + aError);
})
.then(() => {
// Restore debugging pref.
SpecialPowers.setBoolPref(PREF_KEY_RIL_DEBUGGING_ENABLED, debugPref);
cleanUp();
});
}
/**
* Common test routine helper for icc tests.
*
* This function ensures global variable |iccManager| and |icc| is available
* during the process and performs clean-ups as well.
*
* @param aTestCaseMain
* A function that takes one parameter -- icc.
* @param aAdditonalPermissions [optional]
* An array of permission strings other than "mobileconnection" to be
* pushed. Default: empty string..
*/
function startTestCommon(aTestCaseMain, aAdditionalPermissions) {
startTestBase(function() {
return ensureIccManager(aAdditionalPermissions)
.then(aTestCaseMain);
});
}