Bug 1052825 - Part 4: Add test in mobileconnection for unlocking puk via MMI. r=hsinyi
authorEdgar Chen <echen@mozilla.com>
Thu, 27 Nov 2014 15:12:42 +0800
changeset 219813 e55b62072a39b38b78c9601370caff8ea2ef2460
parent 219812 100e9676dae8aa603a4df2f9ae6cad5803686370
child 219814 3e9eae669e160363bd121ad59b68d836bbfa0a89
push id10419
push usercbook@mozilla.com
push dateTue, 16 Dec 2014 12:45:27 +0000
treeherderfx-team@ec87657146eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsinyi
bugs1052825
milestone37.0a1
Bug 1052825 - Part 4: Add test in mobileconnection for unlocking puk via MMI. r=hsinyi
dom/mobileconnection/tests/marionette/head.js
dom/mobileconnection/tests/marionette/manifest.ini
dom/mobileconnection/tests/marionette/test_mobile_mmi_unlock_puk.js
--- a/dom/mobileconnection/tests/marionette/head.js
+++ b/dom/mobileconnection/tests/marionette/head.js
@@ -4,16 +4,21 @@
 const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
 
 const SETTINGS_KEY_DATA_ENABLED = "ril.data.enabled";
 const SETTINGS_KEY_DATA_ROAMING_ENABLED = "ril.data.roaming_enabled";
 const SETTINGS_KEY_DATA_APN_SETTINGS = "ril.data.apnSettings";
 
 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";
+
 // Emulate Promise.jsm semantics.
 Promise.defer = function() { return new Deferred(); };
 function Deferred() {
   this.promise = new Promise(function(resolve, reject) {
     this.resolve = resolve;
     this.reject = reject;
   }.bind(this));
   Object.freeze(this);
@@ -296,43 +301,94 @@ function getMozMobileConnectionByService
   if (aServiceId !== undefined) {
     mobileConn =
       workingFrame.contentWindow.navigator.mozMobileConnections[aServiceId];
   }
   return mobileConn;
 }
 
 /**
+ * Get MozIccManager
+ *
+ * @return a MozIccManager
+ */
+function getMozIccManager() {
+  return workingFrame.contentWindow.navigator.mozIccManager;
+}
+
+/**
+ * Get MozIcc by IccId
+ *
+ * @param aIccId [optional]
+ *        Default: The first item of |mozIccManager.iccIds|.
+ *
+ * @return A MozIcc.
+ */
+function getMozIccByIccId(aIccId) {
+  let iccManager = getMozIccManager();
+
+  aIccId = aIccId || iccManager.iccIds[0];
+  if (!aIccId) {
+    ok(true, "iccManager.iccIds[0] is " + aIccId);
+    return null;
+  }
+
+  return iccManager.getIccById(aIccId);
+}
+
+/**
+ * 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) {
+  let deferred = Promise.defer();
+
+  aEventTarget.addEventListener(aEventName, function onevent(aEvent) {
+    if (!aMatchFun || aMatchFun(aEvent)) {
+      aEventTarget.removeEventListener(aEventName, onevent);
+      ok(true, "Event '" + aEventName + "' got.");
+      deferred.resolve(aEvent);
+    }
+  });
+
+  return deferred.promise;
+}
+
+/**
  * Wait for one named MobileConnection event.
  *
  * Resolve if that named event occurs.  Never reject.
  *
  * Fulfill params: the DOMEvent passed.
  *
  * @param aEventName
  *        A string event name.
  * @param aServiceId [optional]
  *        A numeric DSDS service id. Default: the one indicated in
  *        start*TestCommon() or 0 if not indicated.
+ * @param aMatchFun [optional]
+ *        A matching function returns true or false to filter the event.
  *
  * @return A deferred promise.
  */
-function waitForManagerEvent(aEventName, aServiceId) {
-  let deferred = Promise.defer();
-
+function waitForManagerEvent(aEventName, aServiceId, aMatchFun) {
   let mobileConn = getMozMobileConnectionByServiceId(aServiceId);
-
-  mobileConn.addEventListener(aEventName, function onevent(aEvent) {
-    mobileConn.removeEventListener(aEventName, onevent);
-
-    ok(true, "MobileConnection event '" + aEventName + "' got.");
-    deferred.resolve(aEvent);
-  });
-
-  return deferred.promise;
+  return waitForTargetEvent(mobileConn, aEventName, aMatchFun);
 }
 
 /**
  * Get available networks.
  *
  * Fulfill params:
  *   An array of MozMobileNetworkInfo.
  * Reject params:
@@ -673,36 +729,25 @@ function setRadioEnabled(aEnabled, aServ
  *        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 deferred = Promise.defer();
+  let promises = [];
 
-  let promises = [];
-  promises.push(waitForManagerEvent("radiostatechange", aServiceId));
-  promises.push(setRadioEnabled(aEnabled, aServiceId));
-  Promise.all(promises).then(function keepWaiting() {
+  promises.push(waitForManagerEvent("radiostatechange", aServiceId, function() {
     let mobileConn = getMozMobileConnectionByServiceId(aServiceId);
-    // To ignore some transient states, we only resolve that deferred promise
-    // when |radioState| equals to the expected one and never rejects.
-    let state = mobileConn.radioState;
-    aEnabled = aEnabled ? "enabled" : "disabled";
-    if (state == aEnabled) {
-      deferred.resolve();
-      return;
-    }
+    return mobileConn.radioState === aEnabled ? "enabled" : "disabled";
+  }));
+  promises.push(setRadioEnabled(aEnabled, aServiceId));
 
-    return waitForManagerEvent("radiostatechange", aServiceId).then(keepWaiting);
-  });
-
-  return deferred.promise;
+  return Promise.all(promises);
 }
 
 /**
  * Set CLIR (calling line id restriction).
  *
  * Fulfill params: (none)
  * Reject params:
  *   'RadioNotAvailable', 'RequestNotSupported', or 'GenericFailure'
@@ -1118,26 +1163,26 @@ function cleanUp() {
  * @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()
+  return 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();
     })
-    .then(cleanUp, function() {
-      ok(false, 'promise rejects during test.');
-      cleanUp();
-    });
 }
 
 /**
  * Common test routine helper for mobile connection tests.
  *
  * This function ensures global |mobileConnection| variable is available during
  * the process and performs clean-ups as well.
  *
--- a/dom/mobileconnection/tests/marionette/manifest.ini
+++ b/dom/mobileconnection/tests/marionette/manifest.ini
@@ -12,16 +12,17 @@ qemu = true
 [test_mobile_preferred_network_type.js]
 [test_mobile_preferred_network_type_radio_off.js]
 [test_mobile_data_connection.js]
 [test_mobile_data_location.js]
 [test_mobile_data_state.js]
 [test_mobile_mmi.js]
 [test_mobile_mmi_change_pin.js]
 [test_mobile_mmi_call_forwarding.js]
+[test_mobile_mmi_unlock_puk.js]
 [test_mobile_roaming_preference.js]
 [test_call_barring_get_option.js]
 [test_call_barring_set_error.js]
 [test_call_barring_change_password.js]
 [test_mobile_set_radio.js]
 [test_mobile_last_known_network.js]
 [test_mobile_icc_change.js]
 [test_mobile_connections_array_uninitialized.js]
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/tests/marionette/test_mobile_mmi_unlock_puk.js
@@ -0,0 +1,122 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = "head.js";
+
+function restartRadioAndWait(aCardState) {
+  let iccManager = getMozIccManager();
+  return setRadioEnabledAndWait(false).then(() => {
+    let promises = [];
+
+    promises.push(waitForTargetEvent(iccManager, "iccdetected")
+      .then((aEvent) => {
+        let icc = getMozIccByIccId(aEvent.iccId);
+        if (icc.cardState !== aCardState) {
+          return waitForTargetEvent(icc, "cardstatechange", function() {
+            return icc.cardState === aCardState;
+          });
+        }
+      }));
+    promises.push(setRadioEnabledAndWait(true));
+
+    return Promise.all(promises);
+  });
+}
+
+function passingWrongPinAndWait(aIcc) {
+  return aIcc.getCardLockRetryCount("pin").then((aResult) => {
+    let promises = [];
+    let retryCount = aResult.retryCount;
+
+    ok(true, "pin retryCount is " + retryCount);
+
+    promises.push(waitForTargetEvent(aIcc, "cardstatechange", function() {
+      return aIcc.cardState === "pukRequired";
+    }));
+
+    for (let i = 0; i < retryCount; i++) {
+      promises.push(aIcc.unlockCardLock({ lockType: "pin", pin: "1111" })
+        .then(() => {
+          ok(false, "unlocking pin should not success");
+        }, (aError) => {
+          ok(true, "pin retryCount = " + aError.retryCount);
+        }));
+    }
+
+    return Promise.all(promises);
+  });
+}
+
+function sendUnlockPukMmi(aPuk, aNewPin, aNewPinAgain) {
+  let MMI_CODE = "**05*" + aPuk + "*" + aNewPin + "*" + aNewPinAgain + "#";
+  log("Test " + MMI_CODE);
+
+  return sendMMI(MMI_CODE);
+}
+
+function testUnlockPukMmiError(aPuk, aNewPin, aNewPinAgain, aErrorName,
+                               aRetryCount = null) {
+  return sendUnlockPukMmi(aPuk, aNewPin, aNewPinAgain)
+    .then(() => {
+      ok(false, "unlocking puk should not success");
+    }, (aError) => {
+      is(aError.name, aErrorName, "Check name");
+      is(aError.message, "", "Check message");
+      is(aError.serviceCode, "scPuk", "Check service code");
+      is(aError.additionalInformation, aRetryCount,
+         "Check additional information");
+    });
+}
+
+function testUnlockPukAndWait(aIcc, aCardState) {
+  let promises = [];
+
+  promises.push(waitForTargetEvent(aIcc, "cardstatechange", function() {
+    return aIcc.cardState === aCardState;
+  }));
+  promises.push(sendUnlockPukMmi(DEFAULT_PUK, DEFAULT_PIN, DEFAULT_PIN));
+
+  return Promise.all(promises);
+}
+
+// Start test
+startTestCommon(function() {
+  let icc = getMozIccByIccId();
+  let retryCount;
+
+  // Enable PIN-lock.
+  return icc.setCardLock({lockType: "pin", enabled: true, pin: DEFAULT_PIN})
+    // Reset card state to "pinRequired" by restarting radio.
+    .then(() => restartRadioAndWait("pinRequired"))
+    .then(() => { icc = getMozIccByIccId(); })
+    // Switch card state to "pukRequired" by entering wrong pin.
+    .then(() => passingWrongPinAndWait(icc))
+
+    // Get current PUK-lock retry count.
+    .then(() => icc.getCardLockRetryCount("puk"))
+    .then((aResult) => {
+      retryCount = aResult.retryCount;
+      ok(true, "puk retryCount is " + retryCount);
+    })
+
+    // Test passing no puk.
+    .then(() => testUnlockPukMmiError("", "1111", "2222", "emMmiError"))
+    // Test passing no newPin.
+    .then(() => testUnlockPukMmiError("11111111", "", "", "emMmiError"))
+    // Test passing mismatched newPin.
+    .then(() => testUnlockPukMmiError("11111111", "1111", "2222",
+                                      "emMmiErrorMismatchPin"))
+    // Test passing invalid puk (> 8 digit).
+    .then(() => testUnlockPukMmiError("123456789", DEFAULT_PIN, DEFAULT_PIN,
+                                      "emMmiErrorInvalidPin"))
+    // Test passing incorrect puk.
+    .then(() => testUnlockPukMmiError("11111111", DEFAULT_PIN, DEFAULT_PIN,
+                                      "emMmiErrorBadPuk", retryCount - 1))
+
+    // Test unlock PUK-lock success.
+    .then(() => testUnlockPukAndWait(icc, "ready"))
+
+    // Restore pin state.
+    .then(() => icc.setCardLock({lockType: "pin", enabled: false, pin: DEFAULT_PIN}));
+});