Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 3 | |
michael@0 | 4 | let Promise = SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise; |
michael@0 | 5 | let telephony; |
michael@0 | 6 | let conference; |
michael@0 | 7 | |
michael@0 | 8 | /** |
michael@0 | 9 | * Emulator helper. |
michael@0 | 10 | */ |
michael@0 | 11 | let emulator = (function() { |
michael@0 | 12 | let pendingCmdCount = 0; |
michael@0 | 13 | let originalRunEmulatorCmd = runEmulatorCmd; |
michael@0 | 14 | |
michael@0 | 15 | // Overwritten it so people could not call this function directly. |
michael@0 | 16 | runEmulatorCmd = function() { |
michael@0 | 17 | throw "Use emulator.run(cmd, callback) instead of runEmulatorCmd"; |
michael@0 | 18 | }; |
michael@0 | 19 | |
michael@0 | 20 | function run(cmd, callback) { |
michael@0 | 21 | pendingCmdCount++; |
michael@0 | 22 | originalRunEmulatorCmd(cmd, function(result) { |
michael@0 | 23 | pendingCmdCount--; |
michael@0 | 24 | if (callback && typeof callback === "function") { |
michael@0 | 25 | callback(result); |
michael@0 | 26 | } |
michael@0 | 27 | }); |
michael@0 | 28 | } |
michael@0 | 29 | |
michael@0 | 30 | /** |
michael@0 | 31 | * @return Promise |
michael@0 | 32 | */ |
michael@0 | 33 | function waitFinish() { |
michael@0 | 34 | let deferred = Promise.defer(); |
michael@0 | 35 | |
michael@0 | 36 | waitFor(function() { |
michael@0 | 37 | deferred.resolve(); |
michael@0 | 38 | }, function() { |
michael@0 | 39 | return pendingCmdCount === 0; |
michael@0 | 40 | }); |
michael@0 | 41 | |
michael@0 | 42 | return deferred.promise; |
michael@0 | 43 | } |
michael@0 | 44 | |
michael@0 | 45 | return { |
michael@0 | 46 | run: run, |
michael@0 | 47 | waitFinish: waitFinish |
michael@0 | 48 | }; |
michael@0 | 49 | }()); |
michael@0 | 50 | |
michael@0 | 51 | // Delay 1s before each telephony.dial() |
michael@0 | 52 | // The workaround here should be removed after bug 1005816. |
michael@0 | 53 | |
michael@0 | 54 | let originalDial; |
michael@0 | 55 | |
michael@0 | 56 | function delayTelephonyDial() { |
michael@0 | 57 | originalDial = telephony.dial; |
michael@0 | 58 | telephony.dial = function(number, serviceId) { |
michael@0 | 59 | let deferred = Promise.defer(); |
michael@0 | 60 | |
michael@0 | 61 | let startTime = Date.now(); |
michael@0 | 62 | waitFor(function() { |
michael@0 | 63 | originalDial.call(telephony, number, serviceId).then(call => { |
michael@0 | 64 | deferred.resolve(call); |
michael@0 | 65 | }, cause => { |
michael@0 | 66 | deferred.reject(cause); |
michael@0 | 67 | }); |
michael@0 | 68 | }, function() { |
michael@0 | 69 | duration = Date.now() - startTime; |
michael@0 | 70 | return (duration >= 1000); |
michael@0 | 71 | }); |
michael@0 | 72 | |
michael@0 | 73 | return deferred.promise; |
michael@0 | 74 | }; |
michael@0 | 75 | } |
michael@0 | 76 | |
michael@0 | 77 | function restoreTelephonyDial() { |
michael@0 | 78 | telephony.dial = originalDial; |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | /** |
michael@0 | 82 | * Telephony related helper functions. |
michael@0 | 83 | */ |
michael@0 | 84 | (function() { |
michael@0 | 85 | /** |
michael@0 | 86 | * @return Promise |
michael@0 | 87 | */ |
michael@0 | 88 | function clearCalls() { |
michael@0 | 89 | let deferred = Promise.defer(); |
michael@0 | 90 | |
michael@0 | 91 | log("Clear existing calls."); |
michael@0 | 92 | emulator.run("gsm clear", function(result) { |
michael@0 | 93 | if (result[0] == "OK") { |
michael@0 | 94 | waitFor(function() { |
michael@0 | 95 | deferred.resolve(); |
michael@0 | 96 | }, function() { |
michael@0 | 97 | return telephony.calls.length === 0; |
michael@0 | 98 | }); |
michael@0 | 99 | } else { |
michael@0 | 100 | log("Failed to clear existing calls."); |
michael@0 | 101 | deferred.reject(); |
michael@0 | 102 | } |
michael@0 | 103 | }); |
michael@0 | 104 | |
michael@0 | 105 | return deferred.promise; |
michael@0 | 106 | } |
michael@0 | 107 | |
michael@0 | 108 | /** |
michael@0 | 109 | * Provide a string with format of the emulator call list result. |
michael@0 | 110 | * |
michael@0 | 111 | * @param prefix |
michael@0 | 112 | * Possible values are "outbound" and "inbound". |
michael@0 | 113 | * @param number |
michael@0 | 114 | * Call number. |
michael@0 | 115 | * @return A string with format of the emulator call list result. |
michael@0 | 116 | */ |
michael@0 | 117 | function callStrPool(prefix, number) { |
michael@0 | 118 | let padding = " : "; |
michael@0 | 119 | let numberInfo = prefix + number + padding.substr(number.length); |
michael@0 | 120 | |
michael@0 | 121 | let info = {}; |
michael@0 | 122 | let states = ['ringing', 'incoming', 'active', 'held']; |
michael@0 | 123 | for (let state of states) { |
michael@0 | 124 | info[state] = numberInfo + state; |
michael@0 | 125 | } |
michael@0 | 126 | |
michael@0 | 127 | return info; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | /** |
michael@0 | 131 | * Provide a corresponding string of an outgoing call. The string is with |
michael@0 | 132 | * format of the emulator call list result. |
michael@0 | 133 | * |
michael@0 | 134 | * @param number |
michael@0 | 135 | * Number of an outgoing call. |
michael@0 | 136 | * @return A string with format of the emulator call list result. |
michael@0 | 137 | */ |
michael@0 | 138 | function outCallStrPool(number) { |
michael@0 | 139 | return callStrPool("outbound to ", number); |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | /** |
michael@0 | 143 | * Provide a corresponding string of an incoming call. The string is with |
michael@0 | 144 | * format of the emulator call list result. |
michael@0 | 145 | * |
michael@0 | 146 | * @param number |
michael@0 | 147 | * Number of an incoming call. |
michael@0 | 148 | * @return A string with format of the emulator call list result. |
michael@0 | 149 | */ |
michael@0 | 150 | function inCallStrPool(number) { |
michael@0 | 151 | return callStrPool("inbound from ", number); |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | /** |
michael@0 | 155 | * Check utility functions. |
michael@0 | 156 | */ |
michael@0 | 157 | |
michael@0 | 158 | function checkInitialState() { |
michael@0 | 159 | log("Verify initial state."); |
michael@0 | 160 | ok(telephony.calls, 'telephony.call'); |
michael@0 | 161 | checkTelephonyActiveAndCalls(null, []); |
michael@0 | 162 | ok(conference.calls, 'conference.calls'); |
michael@0 | 163 | checkConferenceStateAndCalls('', []); |
michael@0 | 164 | } |
michael@0 | 165 | |
michael@0 | 166 | /** |
michael@0 | 167 | * Convenient helper to compare a TelephonyCall and a received call event. |
michael@0 | 168 | */ |
michael@0 | 169 | function checkEventCallState(event, call, state) { |
michael@0 | 170 | is(call, event.call, "event.call"); |
michael@0 | 171 | is(call.state, state, "call state"); |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | /** |
michael@0 | 175 | * Convenient helper to check mozTelephony.active and mozTelephony.calls. |
michael@0 | 176 | */ |
michael@0 | 177 | function checkTelephonyActiveAndCalls(active, calls) { |
michael@0 | 178 | is(telephony.active, active, "telephony.active"); |
michael@0 | 179 | is(telephony.calls.length, calls.length, "telephony.calls"); |
michael@0 | 180 | for (let i = 0; i < calls.length; ++i) { |
michael@0 | 181 | is(telephony.calls[i], calls[i]); |
michael@0 | 182 | } |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | /** |
michael@0 | 186 | * Convenient helper to check mozTelephony.conferenceGroup.state and |
michael@0 | 187 | * .conferenceGroup.calls. |
michael@0 | 188 | */ |
michael@0 | 189 | function checkConferenceStateAndCalls(state, calls) { |
michael@0 | 190 | is(conference.state, state, "conference.state"); |
michael@0 | 191 | is(conference.calls.length, calls.length, "conference.calls"); |
michael@0 | 192 | for (let i = 0; i < calls.length; i++) { |
michael@0 | 193 | is(conference.calls[i], calls[i]); |
michael@0 | 194 | } |
michael@0 | 195 | } |
michael@0 | 196 | |
michael@0 | 197 | /** |
michael@0 | 198 | * Convenient helper to handle *.oncallschanged event. |
michael@0 | 199 | * |
michael@0 | 200 | * @param container |
michael@0 | 201 | * Representation of "mozTelephony" or "mozTelephony.conferenceGroup." |
michael@0 | 202 | * @param containerName |
michael@0 | 203 | * Name of container. Could be an arbitrary string, used for debug |
michael@0 | 204 | * messages only. |
michael@0 | 205 | * @param expectedCalls |
michael@0 | 206 | * An array of calls. |
michael@0 | 207 | * @param callback |
michael@0 | 208 | * A callback function. |
michael@0 | 209 | */ |
michael@0 | 210 | function check_oncallschanged(container, containerName, expectedCalls, |
michael@0 | 211 | callback) { |
michael@0 | 212 | container.oncallschanged = function(event) { |
michael@0 | 213 | log("Received 'callschanged' event for the " + containerName); |
michael@0 | 214 | if (event.call) { |
michael@0 | 215 | let index = expectedCalls.indexOf(event.call); |
michael@0 | 216 | ok(index != -1); |
michael@0 | 217 | expectedCalls.splice(index, 1); |
michael@0 | 218 | |
michael@0 | 219 | if (expectedCalls.length === 0) { |
michael@0 | 220 | container.oncallschanged = null; |
michael@0 | 221 | callback(); |
michael@0 | 222 | } |
michael@0 | 223 | } |
michael@0 | 224 | }; |
michael@0 | 225 | } |
michael@0 | 226 | |
michael@0 | 227 | /** |
michael@0 | 228 | * Convenient helper to handle *.ongroupchange event. |
michael@0 | 229 | * |
michael@0 | 230 | * @param call |
michael@0 | 231 | * A TelephonyCall object. |
michael@0 | 232 | * @param callName |
michael@0 | 233 | * Name of a call. Could be an arbitrary string, used for debug messages |
michael@0 | 234 | * only. |
michael@0 | 235 | * @param group |
michael@0 | 236 | * Representation of mozTelephony.conferenceGroup. |
michael@0 | 237 | * @param callback |
michael@0 | 238 | * A callback function. |
michael@0 | 239 | */ |
michael@0 | 240 | function check_ongroupchange(call, callName, group, callback) { |
michael@0 | 241 | call.ongroupchange = function(event) { |
michael@0 | 242 | log("Received 'groupchange' event for the " + callName); |
michael@0 | 243 | call.ongroupchange = null; |
michael@0 | 244 | |
michael@0 | 245 | is(call.group, group); |
michael@0 | 246 | callback(); |
michael@0 | 247 | }; |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | /** |
michael@0 | 251 | * Convenient helper to handle *.onstatechange event. |
michael@0 | 252 | * |
michael@0 | 253 | * @param container |
michael@0 | 254 | * Representation of a TelephonyCall or mozTelephony.conferenceGroup. |
michael@0 | 255 | * @param containerName |
michael@0 | 256 | * Name of container. Could be an arbitrary string, used for debug messages |
michael@0 | 257 | * only. |
michael@0 | 258 | * @param state |
michael@0 | 259 | * A string. |
michael@0 | 260 | * @param callback |
michael@0 | 261 | * A callback function. |
michael@0 | 262 | */ |
michael@0 | 263 | function check_onstatechange(container, containerName, state, callback) { |
michael@0 | 264 | container.onstatechange = function(event) { |
michael@0 | 265 | log("Received 'statechange' event for the " + containerName); |
michael@0 | 266 | container.onstatechange = null; |
michael@0 | 267 | |
michael@0 | 268 | is(container.state, state); |
michael@0 | 269 | callback(); |
michael@0 | 270 | }; |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | /** |
michael@0 | 274 | * Convenient helper to check the sequence of call state and event handlers. |
michael@0 | 275 | * |
michael@0 | 276 | * @param state |
michael@0 | 277 | * A string of the expected call state. |
michael@0 | 278 | * @param previousEvent |
michael@0 | 279 | * A string of the event that should come before the expected state. |
michael@0 | 280 | */ |
michael@0 | 281 | function StateEventChecker(state, previousEvent) { |
michael@0 | 282 | let event = 'on' + state; |
michael@0 | 283 | |
michael@0 | 284 | return function(call, callName, callback) { |
michael@0 | 285 | call[event] = function() { |
michael@0 | 286 | log("Received '" + state + "' event for the " + callName); |
michael@0 | 287 | call[event] = null; |
michael@0 | 288 | |
michael@0 | 289 | if (previousEvent) { |
michael@0 | 290 | // We always clear the event handler when the event is received. |
michael@0 | 291 | // Therefore, if the corresponding handler is not existed, the expected |
michael@0 | 292 | // previous event has been already received. |
michael@0 | 293 | ok(!call[previousEvent]); |
michael@0 | 294 | } |
michael@0 | 295 | is(call.state, state); |
michael@0 | 296 | callback(); |
michael@0 | 297 | }; |
michael@0 | 298 | }; |
michael@0 | 299 | } |
michael@0 | 300 | |
michael@0 | 301 | /** |
michael@0 | 302 | * Convenient helper to check the call list existing in the emulator. |
michael@0 | 303 | * |
michael@0 | 304 | * @param expectedCallList |
michael@0 | 305 | * An array of call info with the format of "callStrPool()[state]". |
michael@0 | 306 | * @return A deferred promise. |
michael@0 | 307 | */ |
michael@0 | 308 | function checkEmulatorCallList(expectedCallList) { |
michael@0 | 309 | let deferred = Promise.defer(); |
michael@0 | 310 | |
michael@0 | 311 | emulator.run("gsm list", function(result) { |
michael@0 | 312 | log("Call list is now: " + result); |
michael@0 | 313 | for (let i = 0; i < expectedCallList.length; ++i) { |
michael@0 | 314 | is(result[i], expectedCallList[i], "emulator calllist"); |
michael@0 | 315 | } |
michael@0 | 316 | is(result[expectedCallList.length], "OK", "emulator calllist"); |
michael@0 | 317 | deferred.resolve(); |
michael@0 | 318 | }); |
michael@0 | 319 | |
michael@0 | 320 | return deferred.promise; |
michael@0 | 321 | } |
michael@0 | 322 | |
michael@0 | 323 | /** |
michael@0 | 324 | * Super convenient helper to check calls and state of mozTelephony and |
michael@0 | 325 | * mozTelephony.conferenceGroup. |
michael@0 | 326 | * |
michael@0 | 327 | * @param active |
michael@0 | 328 | * A TelephonyCall object. Should be the expected active call. |
michael@0 | 329 | * @param calls |
michael@0 | 330 | * An array of TelephonyCall objects. Should be the expected list of |
michael@0 | 331 | * mozTelephony.calls. |
michael@0 | 332 | * @param conferenceState |
michael@0 | 333 | * A string. Should be the expected conference state. |
michael@0 | 334 | * @param conferenceCalls |
michael@0 | 335 | * An array of TelephonyCall objects. Should be the expected list of |
michael@0 | 336 | * mozTelephony.conferenceGroup.calls. |
michael@0 | 337 | */ |
michael@0 | 338 | function checkState(active, calls, conferenceState, conferenceCalls) { |
michael@0 | 339 | checkTelephonyActiveAndCalls(active, calls); |
michael@0 | 340 | checkConferenceStateAndCalls(conferenceState, conferenceCalls); |
michael@0 | 341 | } |
michael@0 | 342 | |
michael@0 | 343 | /** |
michael@0 | 344 | * Super convenient helper to check calls and state of mozTelephony and |
michael@0 | 345 | * mozTelephony.conferenceGroup as well as the calls existing in the emulator. |
michael@0 | 346 | * |
michael@0 | 347 | * @param active |
michael@0 | 348 | * A TelephonyCall object. Should be the expected active call. |
michael@0 | 349 | * @param calls |
michael@0 | 350 | * An array of TelephonyCall objects. Should be the expected list of |
michael@0 | 351 | * mozTelephony.calls. |
michael@0 | 352 | * @param conferenceState |
michael@0 | 353 | * A string. Should be the expected conference state. |
michael@0 | 354 | * @param conferenceCalls |
michael@0 | 355 | * An array of TelephonyCall objects. Should be the expected list of |
michael@0 | 356 | * mozTelephony.conferenceGroup.calls. |
michael@0 | 357 | * @param callList |
michael@0 | 358 | * An array of call info with the format of "callStrPool()[state]". |
michael@0 | 359 | * @return A deferred promise. |
michael@0 | 360 | */ |
michael@0 | 361 | function checkAll(active, calls, conferenceState, conferenceCalls, callList) { |
michael@0 | 362 | checkState(active, calls, conferenceState, conferenceCalls); |
michael@0 | 363 | return checkEmulatorCallList(callList); |
michael@0 | 364 | } |
michael@0 | 365 | |
michael@0 | 366 | /** |
michael@0 | 367 | * Request utility functions. |
michael@0 | 368 | */ |
michael@0 | 369 | |
michael@0 | 370 | /** |
michael@0 | 371 | * Make sure there's no pending event before we jump to the next action. |
michael@0 | 372 | * |
michael@0 | 373 | * @param received |
michael@0 | 374 | * A string of the received event. |
michael@0 | 375 | * @param pending |
michael@0 | 376 | * An array of the pending events. |
michael@0 | 377 | * @param nextAction |
michael@0 | 378 | * A callback function that is called when there's no pending event. |
michael@0 | 379 | */ |
michael@0 | 380 | function receivedPending(received, pending, nextAction) { |
michael@0 | 381 | let index = pending.indexOf(received); |
michael@0 | 382 | if (index != -1) { |
michael@0 | 383 | pending.splice(index, 1); |
michael@0 | 384 | } |
michael@0 | 385 | if (pending.length === 0) { |
michael@0 | 386 | nextAction(); |
michael@0 | 387 | } |
michael@0 | 388 | } |
michael@0 | 389 | |
michael@0 | 390 | /** |
michael@0 | 391 | * Make an outgoing call. |
michael@0 | 392 | * |
michael@0 | 393 | * @param number |
michael@0 | 394 | * A string. |
michael@0 | 395 | * @param serviceId [optional] |
michael@0 | 396 | * Identification of a service. 0 is set as default. |
michael@0 | 397 | * @return A deferred promise. |
michael@0 | 398 | */ |
michael@0 | 399 | function dial(number, serviceId) { |
michael@0 | 400 | serviceId = typeof serviceId !== "undefined" ? serviceId : 0; |
michael@0 | 401 | log("Make an outgoing call: " + number + ", serviceId: " + serviceId); |
michael@0 | 402 | |
michael@0 | 403 | let deferred = Promise.defer(); |
michael@0 | 404 | |
michael@0 | 405 | telephony.dial(number, serviceId).then(call => { |
michael@0 | 406 | ok(call); |
michael@0 | 407 | is(call.number, number); |
michael@0 | 408 | is(call.state, "dialing"); |
michael@0 | 409 | is(call.serviceId, serviceId); |
michael@0 | 410 | |
michael@0 | 411 | call.onalerting = function onalerting(event) { |
michael@0 | 412 | call.onalerting = null; |
michael@0 | 413 | log("Received 'onalerting' call event."); |
michael@0 | 414 | checkEventCallState(event, call, "alerting"); |
michael@0 | 415 | deferred.resolve(call); |
michael@0 | 416 | }; |
michael@0 | 417 | }, cause => { |
michael@0 | 418 | deferred.reject(cause); |
michael@0 | 419 | }); |
michael@0 | 420 | |
michael@0 | 421 | return deferred.promise; |
michael@0 | 422 | } |
michael@0 | 423 | |
michael@0 | 424 | /** |
michael@0 | 425 | * Answer an incoming call. |
michael@0 | 426 | * |
michael@0 | 427 | * @param call |
michael@0 | 428 | * An incoming TelephonyCall object. |
michael@0 | 429 | * @param conferenceStateChangeCallback [optional] |
michael@0 | 430 | * A callback function which is called if answering an incoming call |
michael@0 | 431 | * triggers conference state change. |
michael@0 | 432 | * @return A deferred promise. |
michael@0 | 433 | */ |
michael@0 | 434 | function answer(call, conferenceStateChangeCallback) { |
michael@0 | 435 | log("Answering the incoming call."); |
michael@0 | 436 | |
michael@0 | 437 | let deferred = Promise.defer(); |
michael@0 | 438 | let done = function() { |
michael@0 | 439 | deferred.resolve(call); |
michael@0 | 440 | }; |
michael@0 | 441 | |
michael@0 | 442 | let pending = ["call.onconnected"]; |
michael@0 | 443 | let receive = function(name) { |
michael@0 | 444 | receivedPending(name, pending, done); |
michael@0 | 445 | }; |
michael@0 | 446 | |
michael@0 | 447 | // When there's already a connected conference call, answering a new incoming |
michael@0 | 448 | // call triggers conference state change. We should wait for |
michael@0 | 449 | // |conference.onstatechange| before checking the state of the conference call. |
michael@0 | 450 | if (conference.state === "connected") { |
michael@0 | 451 | pending.push("conference.onstatechange"); |
michael@0 | 452 | check_onstatechange(conference, "conference", "held", function() { |
michael@0 | 453 | if (typeof conferenceStateChangeCallback === "function") { |
michael@0 | 454 | conferenceStateChangeCallback(); |
michael@0 | 455 | } |
michael@0 | 456 | receive("conference.onstatechange"); |
michael@0 | 457 | }); |
michael@0 | 458 | } |
michael@0 | 459 | |
michael@0 | 460 | call.onconnecting = function onconnectingIn(event) { |
michael@0 | 461 | log("Received 'connecting' call event for incoming call."); |
michael@0 | 462 | call.onconnecting = null; |
michael@0 | 463 | checkEventCallState(event, call, "connecting"); |
michael@0 | 464 | }; |
michael@0 | 465 | |
michael@0 | 466 | call.onconnected = function onconnectedIn(event) { |
michael@0 | 467 | log("Received 'connected' call event for incoming call."); |
michael@0 | 468 | call.onconnected = null; |
michael@0 | 469 | checkEventCallState(event, call, "connected"); |
michael@0 | 470 | ok(!call.onconnecting); |
michael@0 | 471 | receive("call.onconnected"); |
michael@0 | 472 | }; |
michael@0 | 473 | call.answer(); |
michael@0 | 474 | |
michael@0 | 475 | return deferred.promise; |
michael@0 | 476 | } |
michael@0 | 477 | |
michael@0 | 478 | /** |
michael@0 | 479 | * Hold a call. |
michael@0 | 480 | * |
michael@0 | 481 | * @param call |
michael@0 | 482 | * A TelephonyCall object. |
michael@0 | 483 | * @return A deferred promise. |
michael@0 | 484 | */ |
michael@0 | 485 | function hold(call) { |
michael@0 | 486 | log("Putting the call on hold."); |
michael@0 | 487 | |
michael@0 | 488 | let deferred = Promise.defer(); |
michael@0 | 489 | |
michael@0 | 490 | let gotHolding = false; |
michael@0 | 491 | call.onholding = function onholding(event) { |
michael@0 | 492 | log("Received 'holding' call event"); |
michael@0 | 493 | call.onholding = null; |
michael@0 | 494 | checkEventCallState(event, call, "holding"); |
michael@0 | 495 | gotHolding = true; |
michael@0 | 496 | }; |
michael@0 | 497 | |
michael@0 | 498 | call.onheld = function onheld(event) { |
michael@0 | 499 | log("Received 'held' call event"); |
michael@0 | 500 | call.onheld = null; |
michael@0 | 501 | checkEventCallState(event, call, "held"); |
michael@0 | 502 | ok(gotHolding); |
michael@0 | 503 | deferred.resolve(call); |
michael@0 | 504 | }; |
michael@0 | 505 | call.hold(); |
michael@0 | 506 | |
michael@0 | 507 | return deferred.promise; |
michael@0 | 508 | } |
michael@0 | 509 | |
michael@0 | 510 | /** |
michael@0 | 511 | * Simulate an incoming call. |
michael@0 | 512 | * |
michael@0 | 513 | * @param number |
michael@0 | 514 | * A string. |
michael@0 | 515 | * @return A deferred promise. |
michael@0 | 516 | */ |
michael@0 | 517 | function remoteDial(number) { |
michael@0 | 518 | log("Simulating an incoming call."); |
michael@0 | 519 | |
michael@0 | 520 | let deferred = Promise.defer(); |
michael@0 | 521 | |
michael@0 | 522 | telephony.onincoming = function onincoming(event) { |
michael@0 | 523 | log("Received 'incoming' call event."); |
michael@0 | 524 | telephony.onincoming = null; |
michael@0 | 525 | |
michael@0 | 526 | let call = event.call; |
michael@0 | 527 | |
michael@0 | 528 | ok(call); |
michael@0 | 529 | is(call.number, number); |
michael@0 | 530 | is(call.state, "incoming"); |
michael@0 | 531 | |
michael@0 | 532 | deferred.resolve(call); |
michael@0 | 533 | }; |
michael@0 | 534 | emulator.run("gsm call " + number); |
michael@0 | 535 | |
michael@0 | 536 | return deferred.promise; |
michael@0 | 537 | } |
michael@0 | 538 | |
michael@0 | 539 | /** |
michael@0 | 540 | * Remote party answers the call. |
michael@0 | 541 | * |
michael@0 | 542 | * @param call |
michael@0 | 543 | * A TelephonyCall object. |
michael@0 | 544 | * @return A deferred promise. |
michael@0 | 545 | */ |
michael@0 | 546 | function remoteAnswer(call) { |
michael@0 | 547 | log("Remote answering the call."); |
michael@0 | 548 | |
michael@0 | 549 | let deferred = Promise.defer(); |
michael@0 | 550 | |
michael@0 | 551 | call.onconnected = function onconnected(event) { |
michael@0 | 552 | log("Received 'connected' call event."); |
michael@0 | 553 | call.onconnected = null; |
michael@0 | 554 | checkEventCallState(event, call, "connected"); |
michael@0 | 555 | deferred.resolve(call); |
michael@0 | 556 | }; |
michael@0 | 557 | emulator.run("gsm accept " + call.number); |
michael@0 | 558 | |
michael@0 | 559 | return deferred.promise; |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | /** |
michael@0 | 563 | * Remote party hangs up the call. |
michael@0 | 564 | * |
michael@0 | 565 | * @param call |
michael@0 | 566 | * A TelephonyCall object. |
michael@0 | 567 | * @return A deferred promise. |
michael@0 | 568 | */ |
michael@0 | 569 | function remoteHangUp(call) { |
michael@0 | 570 | log("Remote hanging up the call."); |
michael@0 | 571 | |
michael@0 | 572 | let deferred = Promise.defer(); |
michael@0 | 573 | |
michael@0 | 574 | call.ondisconnected = function ondisconnected(event) { |
michael@0 | 575 | log("Received 'disconnected' call event."); |
michael@0 | 576 | call.ondisconnected = null; |
michael@0 | 577 | checkEventCallState(event, call, "disconnected"); |
michael@0 | 578 | deferred.resolve(call); |
michael@0 | 579 | }; |
michael@0 | 580 | emulator.run("gsm cancel " + call.number); |
michael@0 | 581 | |
michael@0 | 582 | return deferred.promise; |
michael@0 | 583 | } |
michael@0 | 584 | |
michael@0 | 585 | /** |
michael@0 | 586 | * Remote party hangs up all the calls. |
michael@0 | 587 | * |
michael@0 | 588 | * @param calls |
michael@0 | 589 | * An array of TelephonyCall objects. |
michael@0 | 590 | * @return A deferred promise. |
michael@0 | 591 | */ |
michael@0 | 592 | function remoteHangUpCalls(calls) { |
michael@0 | 593 | let promise = Promise.resolve(); |
michael@0 | 594 | |
michael@0 | 595 | for (let call of calls) { |
michael@0 | 596 | promise = promise.then(remoteHangUp.bind(null, call)); |
michael@0 | 597 | } |
michael@0 | 598 | |
michael@0 | 599 | return promise; |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | /** |
michael@0 | 603 | * Add calls to conference. |
michael@0 | 604 | * |
michael@0 | 605 | * @param callsToAdd |
michael@0 | 606 | * An array of TelephonyCall objects to be added into conference. The |
michael@0 | 607 | * length of the array should be 1 or 2. |
michael@0 | 608 | * @param connectedCallback [optional] |
michael@0 | 609 | * A callback function which is called when conference state becomes |
michael@0 | 610 | * connected. |
michael@0 | 611 | * @return A deferred promise. |
michael@0 | 612 | */ |
michael@0 | 613 | function addCallsToConference(callsToAdd, connectedCallback) { |
michael@0 | 614 | log("Add " + callsToAdd.length + " calls into conference."); |
michael@0 | 615 | |
michael@0 | 616 | let deferred = Promise.defer(); |
michael@0 | 617 | let done = function() { |
michael@0 | 618 | deferred.resolve(); |
michael@0 | 619 | }; |
michael@0 | 620 | |
michael@0 | 621 | let pending = ["conference.oncallschanged", "conference.onconnected"]; |
michael@0 | 622 | let receive = function(name) { |
michael@0 | 623 | receivedPending(name, pending, done); |
michael@0 | 624 | }; |
michael@0 | 625 | |
michael@0 | 626 | let check_onconnected = StateEventChecker('connected', 'onresuming'); |
michael@0 | 627 | |
michael@0 | 628 | for (let call of callsToAdd) { |
michael@0 | 629 | let callName = "callToAdd (" + call.number + ')'; |
michael@0 | 630 | |
michael@0 | 631 | let ongroupchange = callName + ".ongroupchange"; |
michael@0 | 632 | pending.push(ongroupchange); |
michael@0 | 633 | check_ongroupchange(call, callName, conference, |
michael@0 | 634 | receive.bind(null, ongroupchange)); |
michael@0 | 635 | |
michael@0 | 636 | let onstatechange = callName + ".onstatechange"; |
michael@0 | 637 | pending.push(onstatechange); |
michael@0 | 638 | check_onstatechange(call, callName, 'connected', |
michael@0 | 639 | receive.bind(null, onstatechange)); |
michael@0 | 640 | } |
michael@0 | 641 | |
michael@0 | 642 | check_oncallschanged(conference, 'conference', callsToAdd, |
michael@0 | 643 | receive.bind(null, "conference.oncallschanged")); |
michael@0 | 644 | |
michael@0 | 645 | check_onconnected(conference, "conference", function() { |
michael@0 | 646 | ok(!conference.oncallschanged); |
michael@0 | 647 | if (typeof connectedCallback === 'function') { |
michael@0 | 648 | connectedCallback(); |
michael@0 | 649 | } |
michael@0 | 650 | receive("conference.onconnected"); |
michael@0 | 651 | }); |
michael@0 | 652 | |
michael@0 | 653 | // Cannot use apply() through webidl, so just separate the cases to handle. |
michael@0 | 654 | if (callsToAdd.length == 2) { |
michael@0 | 655 | conference.add(callsToAdd[0], callsToAdd[1]); |
michael@0 | 656 | } else { |
michael@0 | 657 | conference.add(callsToAdd[0]); |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | return deferred.promise; |
michael@0 | 661 | } |
michael@0 | 662 | |
michael@0 | 663 | /** |
michael@0 | 664 | * Hold the conference. |
michael@0 | 665 | * |
michael@0 | 666 | * @param calls |
michael@0 | 667 | * An array of TelephonyCall objects existing in conference. |
michael@0 | 668 | * @param heldCallback [optional] |
michael@0 | 669 | * A callback function which is called when conference state becomes |
michael@0 | 670 | * held. |
michael@0 | 671 | * @return A deferred promise. |
michael@0 | 672 | */ |
michael@0 | 673 | function holdConference(calls, heldCallback) { |
michael@0 | 674 | log("Holding the conference call."); |
michael@0 | 675 | |
michael@0 | 676 | let deferred = Promise.defer(); |
michael@0 | 677 | let done = function() { |
michael@0 | 678 | deferred.resolve(); |
michael@0 | 679 | }; |
michael@0 | 680 | |
michael@0 | 681 | let pending = ["conference.onholding", "conference.onheld"]; |
michael@0 | 682 | let receive = function(name) { |
michael@0 | 683 | receivedPending(name, pending, done); |
michael@0 | 684 | }; |
michael@0 | 685 | |
michael@0 | 686 | let check_onholding = StateEventChecker('holding', null); |
michael@0 | 687 | let check_onheld = StateEventChecker('held', 'onholding'); |
michael@0 | 688 | |
michael@0 | 689 | for (let call of calls) { |
michael@0 | 690 | let callName = "call (" + call.number + ')'; |
michael@0 | 691 | |
michael@0 | 692 | let onholding = callName + ".onholding"; |
michael@0 | 693 | pending.push(onholding); |
michael@0 | 694 | check_onholding(call, callName, receive.bind(null, onholding)); |
michael@0 | 695 | |
michael@0 | 696 | let onheld = callName + ".onheld"; |
michael@0 | 697 | pending.push(onheld); |
michael@0 | 698 | check_onheld(call, callName, receive.bind(null, onheld)); |
michael@0 | 699 | } |
michael@0 | 700 | |
michael@0 | 701 | check_onholding(conference, "conference", |
michael@0 | 702 | receive.bind(null, "conference.onholding")); |
michael@0 | 703 | |
michael@0 | 704 | check_onheld(conference, "conference", function() { |
michael@0 | 705 | if (typeof heldCallback === 'function') { |
michael@0 | 706 | heldCallback(); |
michael@0 | 707 | } |
michael@0 | 708 | receive("conference.onheld"); |
michael@0 | 709 | }); |
michael@0 | 710 | |
michael@0 | 711 | conference.hold(); |
michael@0 | 712 | |
michael@0 | 713 | return deferred.promise; |
michael@0 | 714 | } |
michael@0 | 715 | |
michael@0 | 716 | /** |
michael@0 | 717 | * Resume the conference. |
michael@0 | 718 | * |
michael@0 | 719 | * @param calls |
michael@0 | 720 | * An array of TelephonyCall objects existing in conference. |
michael@0 | 721 | * @param connectedCallback [optional] |
michael@0 | 722 | * A callback function which is called when conference state becomes |
michael@0 | 723 | * connected. |
michael@0 | 724 | * @return A deferred promise. |
michael@0 | 725 | */ |
michael@0 | 726 | function resumeConference(calls, connectedCallback) { |
michael@0 | 727 | log("Resuming the held conference call."); |
michael@0 | 728 | |
michael@0 | 729 | let deferred = Promise.defer(); |
michael@0 | 730 | let done = function() { |
michael@0 | 731 | deferred.resolve(); |
michael@0 | 732 | }; |
michael@0 | 733 | |
michael@0 | 734 | let pending = ["conference.onresuming", "conference.onconnected"]; |
michael@0 | 735 | let receive = function(name) { |
michael@0 | 736 | receivedPending(name, pending, done); |
michael@0 | 737 | }; |
michael@0 | 738 | |
michael@0 | 739 | let check_onresuming = StateEventChecker('resuming', null); |
michael@0 | 740 | let check_onconnected = StateEventChecker('connected', 'onresuming'); |
michael@0 | 741 | |
michael@0 | 742 | for (let call of calls) { |
michael@0 | 743 | let callName = "call (" + call.number + ')'; |
michael@0 | 744 | |
michael@0 | 745 | let onresuming = callName + ".onresuming"; |
michael@0 | 746 | pending.push(onresuming); |
michael@0 | 747 | check_onresuming(call, callName, receive.bind(null, onresuming)); |
michael@0 | 748 | |
michael@0 | 749 | let onconnected = callName + ".onconnected"; |
michael@0 | 750 | pending.push(onconnected); |
michael@0 | 751 | check_onconnected(call, callName, receive.bind(null, onconnected)); |
michael@0 | 752 | } |
michael@0 | 753 | |
michael@0 | 754 | check_onresuming(conference, "conference", |
michael@0 | 755 | receive.bind(null, "conference.onresuming")); |
michael@0 | 756 | |
michael@0 | 757 | check_onconnected(conference, "conference", function() { |
michael@0 | 758 | if (typeof connectedCallback === 'function') { |
michael@0 | 759 | connectedCallback(); |
michael@0 | 760 | } |
michael@0 | 761 | receive("conference.onconnected"); |
michael@0 | 762 | }); |
michael@0 | 763 | |
michael@0 | 764 | conference.resume(); |
michael@0 | 765 | |
michael@0 | 766 | return deferred.promise; |
michael@0 | 767 | } |
michael@0 | 768 | |
michael@0 | 769 | /** |
michael@0 | 770 | * Remove a call out of conference. |
michael@0 | 771 | * |
michael@0 | 772 | * @param callToRemove |
michael@0 | 773 | * A TelephonyCall object existing in conference. |
michael@0 | 774 | * @param autoRemovedCalls |
michael@0 | 775 | * An array of TelephonyCall objects which is going to be automatically |
michael@0 | 776 | * removed. The length of the array should be 0 or 1. |
michael@0 | 777 | * @param remainedCalls |
michael@0 | 778 | * An array of TelephonyCall objects which remain in conference. |
michael@0 | 779 | * @param stateChangeCallback [optional] |
michael@0 | 780 | * A callback function which is called when conference state changes. |
michael@0 | 781 | * @return A deferred promise. |
michael@0 | 782 | */ |
michael@0 | 783 | function removeCallInConference(callToRemove, autoRemovedCalls, remainedCalls, |
michael@0 | 784 | statechangeCallback) { |
michael@0 | 785 | log("Removing a participant from the conference call."); |
michael@0 | 786 | |
michael@0 | 787 | is(conference.state, 'connected'); |
michael@0 | 788 | |
michael@0 | 789 | let deferred = Promise.defer(); |
michael@0 | 790 | let done = function() { |
michael@0 | 791 | deferred.resolve(); |
michael@0 | 792 | }; |
michael@0 | 793 | |
michael@0 | 794 | let pending = ["callToRemove.ongroupchange", "telephony.oncallschanged", |
michael@0 | 795 | "conference.oncallschanged", "conference.onstatechange"]; |
michael@0 | 796 | let receive = function(name) { |
michael@0 | 797 | receivedPending(name, pending, done); |
michael@0 | 798 | }; |
michael@0 | 799 | |
michael@0 | 800 | // Remained call in conference will be held. |
michael@0 | 801 | for (let call of remainedCalls) { |
michael@0 | 802 | let callName = "remainedCall (" + call.number + ')'; |
michael@0 | 803 | |
michael@0 | 804 | let onstatechange = callName + ".onstatechange"; |
michael@0 | 805 | pending.push(onstatechange); |
michael@0 | 806 | check_onstatechange(call, callName, 'held', |
michael@0 | 807 | receive.bind(null, onstatechange)); |
michael@0 | 808 | } |
michael@0 | 809 | |
michael@0 | 810 | // When a call is removed from conference with 2 calls, another one will be |
michael@0 | 811 | // automatically removed from group and be put on hold. |
michael@0 | 812 | for (let call of autoRemovedCalls) { |
michael@0 | 813 | let callName = "autoRemovedCall (" + call.number + ')'; |
michael@0 | 814 | |
michael@0 | 815 | let ongroupchange = callName + ".ongroupchange"; |
michael@0 | 816 | pending.push(ongroupchange); |
michael@0 | 817 | check_ongroupchange(call, callName, null, |
michael@0 | 818 | receive.bind(null, ongroupchange)); |
michael@0 | 819 | |
michael@0 | 820 | let onstatechange = callName + ".onstatechange"; |
michael@0 | 821 | pending.push(onstatechange); |
michael@0 | 822 | check_onstatechange(call, callName, 'held', |
michael@0 | 823 | receive.bind(null, onstatechange)); |
michael@0 | 824 | } |
michael@0 | 825 | |
michael@0 | 826 | check_ongroupchange(callToRemove, "callToRemove", null, function() { |
michael@0 | 827 | is(callToRemove.state, 'connected'); |
michael@0 | 828 | receive("callToRemove.ongroupchange"); |
michael@0 | 829 | }); |
michael@0 | 830 | |
michael@0 | 831 | check_oncallschanged(telephony, 'telephony', |
michael@0 | 832 | autoRemovedCalls.concat(callToRemove), |
michael@0 | 833 | receive.bind(null, "telephony.oncallschanged")); |
michael@0 | 834 | |
michael@0 | 835 | check_oncallschanged(conference, 'conference', |
michael@0 | 836 | autoRemovedCalls.concat(callToRemove), function() { |
michael@0 | 837 | is(conference.calls.length, remainedCalls.length); |
michael@0 | 838 | receive("conference.oncallschanged"); |
michael@0 | 839 | }); |
michael@0 | 840 | |
michael@0 | 841 | check_onstatechange(conference, 'conference', |
michael@0 | 842 | (remainedCalls.length ? 'held' : ''), function() { |
michael@0 | 843 | ok(!conference.oncallschanged); |
michael@0 | 844 | if (typeof statechangeCallback === 'function') { |
michael@0 | 845 | statechangeCallback(); |
michael@0 | 846 | } |
michael@0 | 847 | receive("conference.onstatechange"); |
michael@0 | 848 | }); |
michael@0 | 849 | |
michael@0 | 850 | conference.remove(callToRemove); |
michael@0 | 851 | |
michael@0 | 852 | return deferred.promise; |
michael@0 | 853 | } |
michael@0 | 854 | |
michael@0 | 855 | /** |
michael@0 | 856 | * Hangup a call in conference. |
michael@0 | 857 | * |
michael@0 | 858 | * @param callToHangUp |
michael@0 | 859 | * A TelephonyCall object existing in conference. |
michael@0 | 860 | * @param autoRemovedCalls |
michael@0 | 861 | * An array of TelephonyCall objects which is going to be automatically |
michael@0 | 862 | * removed. The length of the array should be 0 or 1. |
michael@0 | 863 | * @param remainedCalls |
michael@0 | 864 | * An array of TelephonyCall objects which remain in conference. |
michael@0 | 865 | * @param stateChangeCallback [optional] |
michael@0 | 866 | * A callback function which is called when conference state changes. |
michael@0 | 867 | * @return A deferred promise. |
michael@0 | 868 | */ |
michael@0 | 869 | function hangUpCallInConference(callToHangUp, autoRemovedCalls, remainedCalls, |
michael@0 | 870 | statechangeCallback) { |
michael@0 | 871 | log("Release one call in conference."); |
michael@0 | 872 | |
michael@0 | 873 | let deferred = Promise.defer(); |
michael@0 | 874 | let done = function() { |
michael@0 | 875 | deferred.resolve(); |
michael@0 | 876 | }; |
michael@0 | 877 | |
michael@0 | 878 | let pending = ["conference.oncallschanged", "remoteHangUp"]; |
michael@0 | 879 | let receive = function(name) { |
michael@0 | 880 | receivedPending(name, pending, done); |
michael@0 | 881 | }; |
michael@0 | 882 | |
michael@0 | 883 | // When a call is hang up from conference with 2 calls, another one will be |
michael@0 | 884 | // automatically removed from group. |
michael@0 | 885 | for (let call of autoRemovedCalls) { |
michael@0 | 886 | let callName = "autoRemovedCall (" + call.number + ')'; |
michael@0 | 887 | |
michael@0 | 888 | let ongroupchange = callName + ".ongroupchange"; |
michael@0 | 889 | pending.push(ongroupchange); |
michael@0 | 890 | check_ongroupchange(call, callName, null, |
michael@0 | 891 | receive.bind(null, ongroupchange)); |
michael@0 | 892 | } |
michael@0 | 893 | |
michael@0 | 894 | if (autoRemovedCalls.length) { |
michael@0 | 895 | pending.push("telephony.oncallschanged"); |
michael@0 | 896 | check_oncallschanged(telephony, 'telephony', |
michael@0 | 897 | autoRemovedCalls, |
michael@0 | 898 | receive.bind(null, "telephony.oncallschanged")); |
michael@0 | 899 | } |
michael@0 | 900 | |
michael@0 | 901 | check_oncallschanged(conference, 'conference', |
michael@0 | 902 | autoRemovedCalls.concat(callToHangUp), function() { |
michael@0 | 903 | is(conference.calls.length, remainedCalls.length); |
michael@0 | 904 | receive("conference.oncallschanged"); |
michael@0 | 905 | }); |
michael@0 | 906 | |
michael@0 | 907 | if (remainedCalls.length === 0) { |
michael@0 | 908 | pending.push("conference.onstatechange"); |
michael@0 | 909 | check_onstatechange(conference, 'conference', '', function() { |
michael@0 | 910 | ok(!conference.oncallschanged); |
michael@0 | 911 | if (typeof statechangeCallback === 'function') { |
michael@0 | 912 | statechangeCallback(); |
michael@0 | 913 | } |
michael@0 | 914 | receive("conference.onstatechange"); |
michael@0 | 915 | }); |
michael@0 | 916 | } |
michael@0 | 917 | |
michael@0 | 918 | remoteHangUp(callToHangUp) |
michael@0 | 919 | .then(receive.bind(null, "remoteHangUp")); |
michael@0 | 920 | |
michael@0 | 921 | return deferred.promise; |
michael@0 | 922 | } |
michael@0 | 923 | |
michael@0 | 924 | /** |
michael@0 | 925 | * Setup a conference with an outgoing call and an incoming call. |
michael@0 | 926 | * |
michael@0 | 927 | * @param outNumber |
michael@0 | 928 | * Number of an outgoing call. |
michael@0 | 929 | * @param inNumber |
michael@0 | 930 | * Number of an incoming call. |
michael@0 | 931 | * @return Promise<[outCall, inCall]> |
michael@0 | 932 | */ |
michael@0 | 933 | function setupConferenceTwoCalls(outNumber, inNumber) { |
michael@0 | 934 | log('Create conference with two calls.'); |
michael@0 | 935 | |
michael@0 | 936 | let outCall; |
michael@0 | 937 | let inCall; |
michael@0 | 938 | let outInfo = outCallStrPool(outNumber); |
michael@0 | 939 | let inInfo = inCallStrPool(inNumber); |
michael@0 | 940 | |
michael@0 | 941 | return Promise.resolve() |
michael@0 | 942 | .then(checkInitialState) |
michael@0 | 943 | .then(() => dial(outNumber)) |
michael@0 | 944 | .then(call => { outCall = call; }) |
michael@0 | 945 | .then(() => checkAll(outCall, [outCall], '', [], [outInfo.ringing])) |
michael@0 | 946 | .then(() => remoteAnswer(outCall)) |
michael@0 | 947 | .then(() => checkAll(outCall, [outCall], '', [], [outInfo.active])) |
michael@0 | 948 | .then(() => remoteDial(inNumber)) |
michael@0 | 949 | .then(call => { inCall = call; }) |
michael@0 | 950 | .then(() => checkAll(outCall, [outCall, inCall], '', [], |
michael@0 | 951 | [outInfo.active, inInfo.incoming])) |
michael@0 | 952 | .then(() => answer(inCall)) |
michael@0 | 953 | .then(() => checkAll(inCall, [outCall, inCall], '', [], |
michael@0 | 954 | [outInfo.held, inInfo.active])) |
michael@0 | 955 | .then(() => addCallsToConference([outCall, inCall], function() { |
michael@0 | 956 | checkState(conference, [], 'connected', [outCall, inCall]); |
michael@0 | 957 | })) |
michael@0 | 958 | .then(() => checkAll(conference, [], 'connected', [outCall, inCall], |
michael@0 | 959 | [outInfo.active, inInfo.active])) |
michael@0 | 960 | .then(() => { |
michael@0 | 961 | return [outCall, inCall]; |
michael@0 | 962 | }); |
michael@0 | 963 | } |
michael@0 | 964 | |
michael@0 | 965 | /** |
michael@0 | 966 | * Setup a conference with an outgoing call and two incoming calls. |
michael@0 | 967 | * |
michael@0 | 968 | * @param outNumber |
michael@0 | 969 | * Number of an outgoing call. |
michael@0 | 970 | * @param inNumber |
michael@0 | 971 | * Number of an incoming call. |
michael@0 | 972 | * @param inNumber2 |
michael@0 | 973 | * Number of an incoming call. |
michael@0 | 974 | * @return Promise<[outCall, inCall, inCall2]> |
michael@0 | 975 | */ |
michael@0 | 976 | function setupConferenceThreeCalls(outNumber, inNumber, inNumber2) { |
michael@0 | 977 | log('Create conference with three calls.'); |
michael@0 | 978 | |
michael@0 | 979 | let outCall; |
michael@0 | 980 | let inCall; |
michael@0 | 981 | let inCall2; |
michael@0 | 982 | let outInfo = outCallStrPool(outNumber); |
michael@0 | 983 | let inInfo = inCallStrPool(inNumber); |
michael@0 | 984 | let inInfo2 = inCallStrPool(inNumber2); |
michael@0 | 985 | |
michael@0 | 986 | return Promise.resolve() |
michael@0 | 987 | .then(() => setupConferenceTwoCalls(outNumber, inNumber)) |
michael@0 | 988 | .then(calls => { |
michael@0 | 989 | outCall = calls[0]; |
michael@0 | 990 | inCall = calls[1]; |
michael@0 | 991 | }) |
michael@0 | 992 | .then(() => remoteDial(inNumber2)) |
michael@0 | 993 | .then(call => { inCall2 = call; }) |
michael@0 | 994 | .then(() => checkAll(conference, [inCall2], 'connected', [outCall, inCall], |
michael@0 | 995 | [outInfo.active, inInfo.active, inInfo2.incoming])) |
michael@0 | 996 | .then(() => answer(inCall2, function() { |
michael@0 | 997 | checkState(inCall2, [inCall2], 'held', [outCall, inCall]); |
michael@0 | 998 | })) |
michael@0 | 999 | .then(() => checkAll(inCall2, [inCall2], 'held', [outCall, inCall], |
michael@0 | 1000 | [outInfo.held, inInfo.held, inInfo2.active])) |
michael@0 | 1001 | .then(() => addCallsToConference([inCall2], function() { |
michael@0 | 1002 | checkState(conference, [], 'connected', [outCall, inCall, inCall2]); |
michael@0 | 1003 | })) |
michael@0 | 1004 | .then(() => checkAll(conference, [], |
michael@0 | 1005 | 'connected', [outCall, inCall, inCall2], |
michael@0 | 1006 | [outInfo.active, inInfo.active, inInfo2.active])) |
michael@0 | 1007 | .then(() => { |
michael@0 | 1008 | return [outCall, inCall, inCall2]; |
michael@0 | 1009 | }); |
michael@0 | 1010 | } |
michael@0 | 1011 | |
michael@0 | 1012 | /** |
michael@0 | 1013 | * Setup a conference with an outgoing call and four incoming calls. |
michael@0 | 1014 | * |
michael@0 | 1015 | * @param outNumber |
michael@0 | 1016 | * Number of an outgoing call. |
michael@0 | 1017 | * @param inNumber |
michael@0 | 1018 | * Number of an incoming call. |
michael@0 | 1019 | * @param inNumber2 |
michael@0 | 1020 | * Number of an incoming call. |
michael@0 | 1021 | * @param inNumber3 |
michael@0 | 1022 | * Number of an incoming call. |
michael@0 | 1023 | * @param inNumber4 |
michael@0 | 1024 | * Number of an incoming call. |
michael@0 | 1025 | * @return Promise<[outCall, inCall, inCall2, inCall3, inCall4]> |
michael@0 | 1026 | */ |
michael@0 | 1027 | function setupConferenceFiveCalls(outNumber, inNumber, inNumber2, inNumber3, |
michael@0 | 1028 | inNumber4) { |
michael@0 | 1029 | log('Create conference with five calls.'); |
michael@0 | 1030 | |
michael@0 | 1031 | let outCall; |
michael@0 | 1032 | let inCall; |
michael@0 | 1033 | let inCall2; |
michael@0 | 1034 | let inCall3; |
michael@0 | 1035 | let inCall4; |
michael@0 | 1036 | let outInfo = outCallStrPool(outNumber); |
michael@0 | 1037 | let inInfo = inCallStrPool(inNumber); |
michael@0 | 1038 | let inInfo2 = inCallStrPool(inNumber2); |
michael@0 | 1039 | let inInfo3 = inCallStrPool(inNumber3); |
michael@0 | 1040 | let inInfo4 = inCallStrPool(inNumber4); |
michael@0 | 1041 | |
michael@0 | 1042 | return Promise.resolve() |
michael@0 | 1043 | .then(() => setupConferenceThreeCalls(outNumber, inNumber, inNumber2)) |
michael@0 | 1044 | .then(calls => { |
michael@0 | 1045 | [outCall, inCall, inCall2] = calls; |
michael@0 | 1046 | }) |
michael@0 | 1047 | .then(() => remoteDial(inNumber3)) |
michael@0 | 1048 | .then(call => {inCall3 = call;}) |
michael@0 | 1049 | .then(() => checkAll(conference, [inCall3], 'connected', |
michael@0 | 1050 | [outCall, inCall, inCall2], |
michael@0 | 1051 | [outInfo.active, inInfo.active, inInfo2.active, |
michael@0 | 1052 | inInfo3.incoming])) |
michael@0 | 1053 | .then(() => answer(inCall3, function() { |
michael@0 | 1054 | checkState(inCall3, [inCall3], 'held', [outCall, inCall, inCall2]); |
michael@0 | 1055 | })) |
michael@0 | 1056 | .then(() => checkAll(inCall3, [inCall3], 'held', |
michael@0 | 1057 | [outCall, inCall, inCall2], |
michael@0 | 1058 | [outInfo.held, inInfo.held, inInfo2.held, |
michael@0 | 1059 | inInfo3.active])) |
michael@0 | 1060 | .then(() => addCallsToConference([inCall3], function() { |
michael@0 | 1061 | checkState(conference, [], 'connected', [outCall, inCall, inCall2, inCall3]); |
michael@0 | 1062 | })) |
michael@0 | 1063 | .then(() => checkAll(conference, [], 'connected', |
michael@0 | 1064 | [outCall, inCall, inCall2, inCall3], |
michael@0 | 1065 | [outInfo.active, inInfo.active, inInfo2.active, |
michael@0 | 1066 | inInfo3.active])) |
michael@0 | 1067 | .then(() => remoteDial(inNumber4)) |
michael@0 | 1068 | .then(call => {inCall4 = call;}) |
michael@0 | 1069 | .then(() => checkAll(conference, [inCall4], 'connected', |
michael@0 | 1070 | [outCall, inCall, inCall2, inCall3], |
michael@0 | 1071 | [outInfo.active, inInfo.active, inInfo2.active, |
michael@0 | 1072 | inInfo3.active, inInfo4.incoming])) |
michael@0 | 1073 | .then(() => answer(inCall4, function() { |
michael@0 | 1074 | checkState(inCall4, [inCall4], 'held', [outCall, inCall, inCall2, inCall3]); |
michael@0 | 1075 | })) |
michael@0 | 1076 | .then(() => checkAll(inCall4, [inCall4], 'held', |
michael@0 | 1077 | [outCall, inCall, inCall2, inCall3], |
michael@0 | 1078 | [outInfo.held, inInfo.held, inInfo2.held, |
michael@0 | 1079 | inInfo3.held, inInfo4.active])) |
michael@0 | 1080 | .then(() => addCallsToConference([inCall4], function() { |
michael@0 | 1081 | checkState(conference, [], 'connected', [outCall, inCall, inCall2, |
michael@0 | 1082 | inCall3, inCall4]); |
michael@0 | 1083 | })) |
michael@0 | 1084 | .then(() => checkAll(conference, [], 'connected', |
michael@0 | 1085 | [outCall, inCall, inCall2, inCall3, inCall4], |
michael@0 | 1086 | [outInfo.active, inInfo.active, inInfo2.active, |
michael@0 | 1087 | inInfo3.active, inInfo4.active])) |
michael@0 | 1088 | .then(() => { |
michael@0 | 1089 | return [outCall, inCall, inCall2, inCall3, inCall4]; |
michael@0 | 1090 | }); |
michael@0 | 1091 | } |
michael@0 | 1092 | |
michael@0 | 1093 | /** |
michael@0 | 1094 | * Public members. |
michael@0 | 1095 | */ |
michael@0 | 1096 | |
michael@0 | 1097 | this.gCheckInitialState = checkInitialState; |
michael@0 | 1098 | this.gClearCalls = clearCalls; |
michael@0 | 1099 | this.gOutCallStrPool = outCallStrPool; |
michael@0 | 1100 | this.gInCallStrPool = inCallStrPool; |
michael@0 | 1101 | this.gCheckState = checkState; |
michael@0 | 1102 | this.gCheckAll = checkAll; |
michael@0 | 1103 | this.gDial = dial; |
michael@0 | 1104 | this.gAnswer = answer; |
michael@0 | 1105 | this.gHold = hold; |
michael@0 | 1106 | this.gRemoteDial = remoteDial; |
michael@0 | 1107 | this.gRemoteAnswer = remoteAnswer; |
michael@0 | 1108 | this.gRemoteHangUp = remoteHangUp; |
michael@0 | 1109 | this.gRemoteHangUpCalls = remoteHangUpCalls; |
michael@0 | 1110 | this.gAddCallsToConference = addCallsToConference; |
michael@0 | 1111 | this.gHoldConference = holdConference; |
michael@0 | 1112 | this.gResumeConference = resumeConference; |
michael@0 | 1113 | this.gRemoveCallInConference = removeCallInConference; |
michael@0 | 1114 | this.gHangUpCallInConference = hangUpCallInConference; |
michael@0 | 1115 | this.gSetupConferenceTwoCalls = setupConferenceTwoCalls; |
michael@0 | 1116 | this.gSetupConferenceThreeCalls = setupConferenceThreeCalls; |
michael@0 | 1117 | this.gSetupConferenceFiveCalls = setupConferenceFiveCalls; |
michael@0 | 1118 | this.gReceivedPending = receivedPending; |
michael@0 | 1119 | }()); |
michael@0 | 1120 | |
michael@0 | 1121 | function _startTest(permissions, test) { |
michael@0 | 1122 | function permissionSetUp() { |
michael@0 | 1123 | SpecialPowers.setBoolPref("dom.mozSettings.enabled", true); |
michael@0 | 1124 | for (let per of permissions) { |
michael@0 | 1125 | SpecialPowers.addPermission(per, true, document); |
michael@0 | 1126 | } |
michael@0 | 1127 | } |
michael@0 | 1128 | |
michael@0 | 1129 | function permissionTearDown() { |
michael@0 | 1130 | SpecialPowers.clearUserPref("dom.mozSettings.enabled"); |
michael@0 | 1131 | for (let per of permissions) { |
michael@0 | 1132 | SpecialPowers.removePermission(per, document); |
michael@0 | 1133 | } |
michael@0 | 1134 | } |
michael@0 | 1135 | |
michael@0 | 1136 | function setUp() { |
michael@0 | 1137 | log("== Test SetUp =="); |
michael@0 | 1138 | permissionSetUp(); |
michael@0 | 1139 | // Make sure that we get the telephony after adding permission. |
michael@0 | 1140 | telephony = window.navigator.mozTelephony; |
michael@0 | 1141 | ok(telephony); |
michael@0 | 1142 | delayTelephonyDial(); |
michael@0 | 1143 | conference = telephony.conferenceGroup; |
michael@0 | 1144 | ok(conference); |
michael@0 | 1145 | return gClearCalls().then(gCheckInitialState); |
michael@0 | 1146 | } |
michael@0 | 1147 | |
michael@0 | 1148 | // Extend finish() with tear down. |
michael@0 | 1149 | finish = (function() { |
michael@0 | 1150 | let originalFinish = finish; |
michael@0 | 1151 | |
michael@0 | 1152 | function tearDown() { |
michael@0 | 1153 | log("== Test TearDown =="); |
michael@0 | 1154 | restoreTelephonyDial(); |
michael@0 | 1155 | emulator.waitFinish() |
michael@0 | 1156 | .then(permissionTearDown) |
michael@0 | 1157 | .then(function() { |
michael@0 | 1158 | originalFinish.apply(this, arguments); |
michael@0 | 1159 | }); |
michael@0 | 1160 | } |
michael@0 | 1161 | |
michael@0 | 1162 | return tearDown.bind(this); |
michael@0 | 1163 | }()); |
michael@0 | 1164 | |
michael@0 | 1165 | function mainTest() { |
michael@0 | 1166 | setUp() |
michael@0 | 1167 | .then(function onSuccess() { |
michael@0 | 1168 | log("== Test Start =="); |
michael@0 | 1169 | test(); |
michael@0 | 1170 | }, function onError(error) { |
michael@0 | 1171 | SpecialPowers.Cu.reportError(error); |
michael@0 | 1172 | ok(false, "SetUp error"); |
michael@0 | 1173 | }); |
michael@0 | 1174 | } |
michael@0 | 1175 | |
michael@0 | 1176 | mainTest(); |
michael@0 | 1177 | } |
michael@0 | 1178 | |
michael@0 | 1179 | function startTest(test) { |
michael@0 | 1180 | _startTest(["telephony"], test); |
michael@0 | 1181 | } |
michael@0 | 1182 | |
michael@0 | 1183 | function startTestWithPermissions(permissions, test) { |
michael@0 | 1184 | _startTest(permissions.concat("telephony"), test); |
michael@0 | 1185 | } |
michael@0 | 1186 | |
michael@0 | 1187 | function startDSDSTest(test) { |
michael@0 | 1188 | let numRIL; |
michael@0 | 1189 | try { |
michael@0 | 1190 | numRIL = SpecialPowers.getIntPref("ril.numRadioInterfaces"); |
michael@0 | 1191 | } catch (ex) { |
michael@0 | 1192 | numRIL = 1; // Pref not set. |
michael@0 | 1193 | } |
michael@0 | 1194 | |
michael@0 | 1195 | if (numRIL > 1) { |
michael@0 | 1196 | startTest(test); |
michael@0 | 1197 | } else { |
michael@0 | 1198 | log("Not a DSDS environment. Test is skipped."); |
michael@0 | 1199 | ok(true); // We should run at least one test. |
michael@0 | 1200 | finish(); |
michael@0 | 1201 | } |
michael@0 | 1202 | } |