2017-03-15 04:12:57 +08:00
/ * *
Licensed to the Apache Software Foundation ( ASF ) under one
or more contributor license agreements . See the NOTICE file
distributed with this work for additional information
regarding copyright ownership . The ASF licenses this file
to you under the Apache License , Version 2.0 ( the
"License" ) ; you may not use this file except in compliance
with the License . You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing ,
software distributed under the License is distributed on an
"AS IS" BASIS , WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND , either express or implied . See the License for the
specific language governing permissions and limitations
under the License .
* /
2018-07-03 09:30:15 +08:00
const fs = require ( 'fs' ) ;
const path = require ( 'path' ) ;
const rewire = require ( 'rewire' ) ;
const shelljs = require ( 'shelljs' ) ;
const CordovaError = require ( 'cordova-common' ) . CordovaError ;
const check _reqs = require ( '../../bin/templates/cordova/lib/check_reqs' ) ;
const superspawn = require ( 'cordova-common' ) . superspawn ;
describe ( 'emulator' , ( ) => {
const EMULATOR _LIST = [ 'emulator-5555' , 'emulator-5556' , 'emulator-5557' ] ;
let emu ;
beforeEach ( ( ) => {
emu = rewire ( '../../bin/templates/cordova/lib/emulator' ) ;
} ) ;
describe ( 'list_images_using_avdmanager' , ( ) => {
it ( 'should properly parse details of SDK Tools 25.3.1 `avdmanager` output' , ( ) => {
const avdList = fs . readFileSync ( path . join ( 'spec' , 'fixtures' , 'sdk25.3-avdmanager_list_avd.txt' ) , 'utf-8' ) ;
spyOn ( superspawn , 'spawn' ) . and . returnValue ( Promise . resolve ( avdList ) ) ;
return emu . list _images _using _avdmanager ( ) . then ( list => {
2017-03-15 04:12:57 +08:00
expect ( list ) . toBeDefined ( ) ;
2017-06-14 02:42:20 +08:00
expect ( list [ 0 ] . name ) . toEqual ( 'nexus5-5.1' ) ;
expect ( list [ 0 ] . target ) . toEqual ( 'Android 5.1 (API level 22)' ) ;
expect ( list [ 1 ] . device ) . toEqual ( 'pixel (Google)' ) ;
expect ( list [ 2 ] . abi ) . toEqual ( 'default/x86_64' ) ;
2017-03-15 04:12:57 +08:00
} ) ;
} ) ;
} ) ;
2018-07-03 09:30:15 +08:00
describe ( 'list_images_using_android' , ( ) => {
it ( 'should invoke `android` with the `list avd` command and _not_ the `list avds` command, as the plural form is not supported in some Android SDK Tools versions' , ( ) => {
spyOn ( superspawn , 'spawn' ) . and . returnValue ( new Promise ( ( ) => { } , ( ) => { } ) ) ;
2017-04-07 04:48:58 +08:00
emu . list _images _using _android ( ) ;
2017-06-14 02:42:20 +08:00
expect ( superspawn . spawn ) . toHaveBeenCalledWith ( 'android' , [ 'list' , 'avd' ] ) ;
2017-04-07 04:48:58 +08:00
} ) ;
2018-07-03 09:30:15 +08:00
it ( 'should properly parse details of SDK Tools pre-25.3.1 `android list avd` output' , ( ) => {
const avdList = fs . readFileSync ( path . join ( 'spec' , 'fixtures' , 'sdk25.2-android_list_avd.txt' ) , 'utf-8' ) ;
spyOn ( superspawn , 'spawn' ) . and . returnValue ( Promise . resolve ( avdList ) ) ;
return emu . list _images _using _android ( ) . then ( list => {
2017-03-15 04:12:57 +08:00
expect ( list ) . toBeDefined ( ) ;
2017-06-14 02:42:20 +08:00
expect ( list [ 0 ] . name ) . toEqual ( 'QWR' ) ;
expect ( list [ 0 ] . device ) . toEqual ( 'Nexus 5 (Google)' ) ;
expect ( list [ 0 ] . path ) . toEqual ( '/Users/shazron/.android/avd/QWR.avd' ) ;
expect ( list [ 0 ] . target ) . toEqual ( 'Android 7.1.1 (API level 25)' ) ;
expect ( list [ 0 ] . abi ) . toEqual ( 'google_apis/x86_64' ) ;
expect ( list [ 0 ] . skin ) . toEqual ( '1080x1920' ) ;
2017-03-15 04:12:57 +08:00
} ) ;
} ) ;
} ) ;
2018-07-03 09:30:15 +08:00
describe ( 'list_images' , ( ) => {
beforeEach ( ( ) => {
spyOn ( fs , 'realpathSync' ) . and . callFake ( cmd => cmd ) ;
2017-03-15 04:12:57 +08:00
} ) ;
2018-07-03 09:30:15 +08:00
it ( 'should try to parse AVD information using `avdmanager` first' , ( ) => {
spyOn ( shelljs , 'which' ) . and . callFake ( cmd => cmd === 'avdmanager' ) ;
const avdmanager _spy = spyOn ( emu , 'list_images_using_avdmanager' ) . and . returnValue ( Promise . resolve ( [ ] ) ) ;
return emu . list _images ( ) . then ( ( ) => {
2017-09-14 06:17:59 +08:00
expect ( avdmanager _spy ) . toHaveBeenCalled ( ) ;
} ) ;
2017-03-15 04:12:57 +08:00
} ) ;
2018-07-03 09:30:15 +08:00
it ( 'should delegate to `android` if `avdmanager` cant be found and `android` can' , ( ) => {
spyOn ( shelljs , 'which' ) . and . callFake ( cmd => cmd !== 'avdmanager' ) ;
const android _spy = spyOn ( emu , 'list_images_using_android' ) . and . returnValue ( Promise . resolve ( [ ] ) ) ;
return emu . list _images ( ) . then ( ( ) => {
2017-09-14 06:17:59 +08:00
expect ( android _spy ) . toHaveBeenCalled ( ) ;
} ) ;
} ) ;
2018-07-03 09:30:15 +08:00
it ( 'should correct api level information and fill in the blanks about api level if exists' , ( ) => {
spyOn ( shelljs , 'which' ) . and . callFake ( cmd => cmd === 'avdmanager' ) ;
spyOn ( emu , 'list_images_using_avdmanager' ) . and . returnValue ( Promise . resolve ( [
2017-09-14 06:17:59 +08:00
{
name : 'Pixel_7.0' ,
device : 'pixel (Google)' ,
path : '/Users/maj/.android/avd/Pixel_7.0.avd' ,
abi : 'google_apis/x86_64' ,
target : 'Android 7.0 (API level 24)'
} , {
name : 'Pixel_8.0' ,
device : 'pixel (Google)' ,
path : '/Users/maj/.android/avd/Pixel_8.0.avd' ,
abi : 'google_apis/x86' ,
target : 'Android API 26'
}
2018-07-03 09:30:15 +08:00
] ) ) ;
return emu . list _images ( ) . then ( avds => {
2017-09-14 06:17:59 +08:00
expect ( avds [ 1 ] . target ) . toContain ( 'Android 8' ) ;
expect ( avds [ 1 ] . target ) . toContain ( 'API level 26' ) ;
} ) ;
2017-03-15 04:12:57 +08:00
} ) ;
2018-07-03 09:30:15 +08:00
it ( 'should throw an error if neither `avdmanager` nor `android` are able to be found' , ( ) => {
2017-06-14 02:42:20 +08:00
spyOn ( shelljs , 'which' ) . and . returnValue ( false ) ;
2018-07-03 09:30:15 +08:00
return emu . list _images ( ) . then (
( ) => fail ( 'Unexpectedly resolved' ) ,
err => {
expect ( err ) . toBeDefined ( ) ;
expect ( err . message ) . toContain ( 'Could not find either `android` or `avdmanager`' ) ;
}
) ;
2017-03-15 04:12:57 +08:00
} ) ;
} ) ;
2018-07-03 09:30:15 +08:00
describe ( 'best_image' , ( ) => {
let target _mock ;
beforeEach ( ( ) => {
spyOn ( emu , 'list_images' ) ;
2017-10-06 04:18:42 +08:00
target _mock = spyOn ( check _reqs , 'get_target' ) . and . returnValue ( 'android-26' ) ;
} ) ;
2018-07-03 09:30:15 +08:00
it ( 'should return undefined if there are no defined AVDs' , ( ) => {
emu . list _images . and . returnValue ( Promise . resolve ( [ ] ) ) ;
return emu . best _image ( ) . then ( best _avd => {
2017-10-06 04:18:42 +08:00
expect ( best _avd ) . toBeUndefined ( ) ;
2018-07-03 09:30:15 +08:00
} ) ;
} ) ;
it ( 'should return the first available image if there is no available target information for existing AVDs' , ( ) => {
const fake _avd = { name : 'MyFakeAVD' } ;
const second _fake _avd = { name : 'AnotherAVD' } ;
emu . list _images . and . returnValue ( Promise . resolve ( [ fake _avd , second _fake _avd ] ) ) ;
return emu . best _image ( ) . then ( best _avd => {
2017-10-06 04:18:42 +08:00
expect ( best _avd ) . toBe ( fake _avd ) ;
2018-07-03 09:30:15 +08:00
} ) ;
2017-10-06 04:18:42 +08:00
} ) ;
2018-07-03 09:30:15 +08:00
it ( 'should return the first AVD for the API level that matches the project target' , ( ) => {
2017-10-06 04:18:42 +08:00
target _mock . and . returnValue ( 'android-25' ) ;
2018-07-03 09:30:15 +08:00
const fake _avd = { name : 'MyFakeAVD' , target : 'Android 7.0 (API level 24)' } ;
const second _fake _avd = { name : 'AnotherAVD' , target : 'Android 7.1 (API level 25)' } ;
const third _fake _avd = { name : 'AVDThree' , target : 'Android 8.0 (API level 26)' } ;
emu . list _images . and . returnValue ( Promise . resolve ( [ fake _avd , second _fake _avd , third _fake _avd ] ) ) ;
return emu . best _image ( ) . then ( best _avd => {
2017-10-06 04:18:42 +08:00
expect ( best _avd ) . toBe ( second _fake _avd ) ;
2018-07-03 09:30:15 +08:00
} ) ;
2017-10-06 04:18:42 +08:00
} ) ;
2018-07-03 09:30:15 +08:00
it ( 'should return the AVD with API level that is closest to the project target API level, without going over' , ( ) => {
2017-10-06 04:18:42 +08:00
target _mock . and . returnValue ( 'android-26' ) ;
2018-07-03 09:30:15 +08:00
const fake _avd = { name : 'MyFakeAVD' , target : 'Android 7.0 (API level 24)' } ;
const second _fake _avd = { name : 'AnotherAVD' , target : 'Android 7.1 (API level 25)' } ;
const third _fake _avd = { name : 'AVDThree' , target : 'Android 99.0 (API level 134)' } ;
emu . list _images . and . returnValue ( Promise . resolve ( [ fake _avd , second _fake _avd , third _fake _avd ] ) ) ;
return emu . best _image ( ) . then ( best _avd => {
2017-10-06 04:18:42 +08:00
expect ( best _avd ) . toBe ( second _fake _avd ) ;
2018-07-03 09:30:15 +08:00
} ) ;
2017-10-06 04:18:42 +08:00
} ) ;
2018-07-03 09:30:15 +08:00
it ( 'should not try to compare API levels when an AVD definition is missing API level info' , ( ) => {
emu . list _images . and . returnValue ( Promise . resolve ( [ {
name : 'Samsung_S8_API_26' ,
2017-10-06 04:18:42 +08:00
device : 'Samsung S8+ (User)' ,
path : '/Users/daviesd/.android/avd/Samsung_S8_API_26.avd' ,
abi : 'google_apis/x86' ,
target : 'Android 8.0'
2018-07-03 09:30:15 +08:00
} ] ) ) ;
return emu . best _image ( ) . then ( best _avd => {
2017-10-06 04:18:42 +08:00
expect ( best _avd ) . toBeDefined ( ) ;
2018-07-03 09:30:15 +08:00
} ) ;
} ) ;
} ) ;
describe ( 'list_started' , ( ) => {
it ( 'should call adb devices with the emulators flag' , ( ) => {
const AdbSpy = jasmine . createSpyObj ( 'Adb' , [ 'devices' ] ) ;
AdbSpy . devices . and . returnValue ( Promise . resolve ( ) ) ;
emu . _ _set _ _ ( 'Adb' , AdbSpy ) ;
return emu . list _started ( ) . then ( ( ) => {
2019-01-08 13:31:14 +08:00
expect ( AdbSpy . devices ) . toHaveBeenCalledWith ( { emulators : true } ) ;
2018-07-03 09:30:15 +08:00
} ) ;
} ) ;
} ) ;
describe ( 'get_available_port' , ( ) => {
let emus ;
beforeEach ( ( ) => {
emus = [ ] ;
spyOn ( emu , 'list_started' ) . and . returnValue ( Promise . resolve ( emus ) ) ;
} ) ;
it ( 'should find the closest available port below 5584 for the emulator' , ( ) => {
const lowestUsedPort = 5565 ;
for ( let i = 5584 ; i >= lowestUsedPort ; i -- ) {
emus . push ( ` emulator- ${ i } ` ) ;
}
return emu . get _available _port ( ) . then ( port => {
expect ( port ) . toBe ( lowestUsedPort - 1 ) ;
} ) ;
} ) ;
it ( 'should throw an error if no port is available between 5554 and 5584' , ( ) => {
for ( let i = 5584 ; i >= 5554 ; i -- ) {
emus . push ( ` emulator- ${ i } ` ) ;
}
return emu . get _available _port ( ) . then (
( ) => fail ( 'Unexpectedly resolved' ) ,
err => {
expect ( err ) . toEqual ( jasmine . any ( CordovaError ) ) ;
}
) ;
} ) ;
} ) ;
describe ( 'start' , ( ) => {
const port = 5555 ;
let emulator ;
let AdbSpy ;
let checkReqsSpy ;
let childProcessSpy ;
let shellJsSpy ;
beforeEach ( ( ) => {
emulator = {
name : 'Samsung_S8_API_26' ,
device : 'Samsung S8+ (User)' ,
path : '/Users/daviesd/.android/avd/Samsung_S8_API_26.avd' ,
abi : 'google_apis/x86' ,
target : 'Android 8.0'
} ;
AdbSpy = jasmine . createSpyObj ( 'Adb' , [ 'shell' ] ) ;
AdbSpy . shell . and . returnValue ( Promise . resolve ( ) ) ;
emu . _ _set _ _ ( 'Adb' , AdbSpy ) ;
checkReqsSpy = jasmine . createSpyObj ( 'create_reqs' , [ 'getAbsoluteAndroidCmd' ] ) ;
emu . _ _set _ _ ( 'check_reqs' , checkReqsSpy ) ;
childProcessSpy = jasmine . createSpyObj ( 'child_process' , [ 'spawn' ] ) ;
childProcessSpy . spawn . and . returnValue ( jasmine . createSpyObj ( 'spawnFns' , [ 'unref' ] ) ) ;
emu . _ _set _ _ ( 'child_process' , childProcessSpy ) ;
spyOn ( emu , 'get_available_port' ) . and . returnValue ( Promise . resolve ( port ) ) ;
spyOn ( emu , 'wait_for_emulator' ) . and . returnValue ( Promise . resolve ( 'randomname' ) ) ;
spyOn ( emu , 'wait_for_boot' ) . and . returnValue ( Promise . resolve ( ) ) ;
// Prevent pollution of the test logs
const proc = emu . _ _get _ _ ( 'process' ) ;
spyOn ( proc . stdout , 'write' ) . and . stub ( ) ;
shellJsSpy = jasmine . createSpyObj ( 'shelljs' , [ 'which' ] ) ;
shellJsSpy . which . and . returnValue ( '/dev/android-sdk/tools' ) ;
emu . _ _set _ _ ( 'shelljs' , shellJsSpy ) ;
} ) ;
it ( 'should find an emulator if an id is not specified' , ( ) => {
spyOn ( emu , 'best_image' ) . and . returnValue ( Promise . resolve ( emulator ) ) ;
return emu . start ( ) . then ( ( ) => {
// This is the earliest part in the code where we can hook in and check
// the emulator that has been selected.
const spawnArgs = childProcessSpy . spawn . calls . argsFor ( 0 ) ;
expect ( spawnArgs [ 1 ] ) . toContain ( emulator . name ) ;
} ) ;
} ) ;
it ( 'should use the specified emulator' , ( ) => {
spyOn ( emu , 'best_image' ) ;
return emu . start ( emulator . name ) . then ( ( ) => {
expect ( emu . best _image ) . not . toHaveBeenCalled ( ) ;
const spawnArgs = childProcessSpy . spawn . calls . argsFor ( 0 ) ;
expect ( spawnArgs [ 1 ] ) . toContain ( emulator . name ) ;
} ) ;
} ) ;
it ( 'should throw an error if no emulator is specified and no default is found' , ( ) => {
spyOn ( emu , 'best_image' ) . and . returnValue ( Promise . resolve ( ) ) ;
return emu . start ( ) . then (
( ) => fail ( 'Unexpectedly resolved' ) ,
err => {
expect ( err ) . toEqual ( jasmine . any ( CordovaError ) ) ;
}
) ;
} ) ;
it ( 'should unlock the screen after the emulator has booted' , ( ) => {
emu . wait _for _emulator . and . returnValue ( Promise . resolve ( emulator . name ) ) ;
emu . wait _for _boot . and . returnValue ( Promise . resolve ( true ) ) ;
return emu . start ( emulator . name ) . then ( ( ) => {
expect ( emu . wait _for _emulator ) . toHaveBeenCalledBefore ( AdbSpy . shell ) ;
expect ( emu . wait _for _boot ) . toHaveBeenCalledBefore ( AdbSpy . shell ) ;
expect ( AdbSpy . shell ) . toHaveBeenCalledWith ( emulator . name , 'input keyevent 82' ) ;
} ) ;
} ) ;
it ( 'should resolve with the emulator id after the emulator has started' , ( ) => {
emu . wait _for _emulator . and . returnValue ( Promise . resolve ( emulator . name ) ) ;
emu . wait _for _boot . and . returnValue ( Promise . resolve ( true ) ) ;
return emu . start ( emulator . name ) . then ( emulatorId => {
expect ( emulatorId ) . toBe ( emulator . name ) ;
} ) ;
} ) ;
it ( 'should resolve with null if starting the emulator times out' , ( ) => {
emu . wait _for _emulator . and . returnValue ( Promise . resolve ( emulator . name ) ) ;
emu . wait _for _boot . and . returnValue ( Promise . resolve ( false ) ) ;
return emu . start ( emulator . name ) . then ( emulatorId => {
expect ( emulatorId ) . toBe ( null ) ;
} ) ;
} ) ;
} ) ;
describe ( 'wait_for_emulator' , ( ) => {
const port = 5656 ;
const expectedEmulatorId = ` emulator- ${ port } ` ;
let AdbSpy ;
beforeEach ( ( ) => {
AdbSpy = jasmine . createSpyObj ( 'Adb' , [ 'shell' ] ) ;
AdbSpy . shell . and . returnValue ( Promise . resolve ( ) ) ;
emu . _ _set _ _ ( 'Adb' , AdbSpy ) ;
spyOn ( emu , 'wait_for_emulator' ) . and . callThrough ( ) ;
} ) ;
it ( 'should resolve with the emulator id if the emulator has completed boot' , ( ) => {
AdbSpy . shell . and . callFake ( ( emulatorId , shellArgs ) => {
expect ( emulatorId ) . toBe ( expectedEmulatorId ) ;
expect ( shellArgs ) . toContain ( 'getprop dev.bootcomplete' ) ;
return Promise . resolve ( '1' ) ; // 1 means boot is complete
} ) ;
return emu . wait _for _emulator ( port ) . then ( emulatorId => {
expect ( emulatorId ) . toBe ( expectedEmulatorId ) ;
} ) ;
} ) ;
it ( 'should call itself again if the emulator is not ready' , ( ) => {
AdbSpy . shell . and . returnValues (
Promise . resolve ( '0' ) ,
Promise . resolve ( '0' ) ,
Promise . resolve ( '1' )
) ;
return emu . wait _for _emulator ( port ) . then ( ( ) => {
expect ( emu . wait _for _emulator ) . toHaveBeenCalledTimes ( 3 ) ;
} ) ;
} ) ;
it ( 'should call itself again if shell fails for a known reason' , ( ) => {
AdbSpy . shell . and . returnValues (
2019-01-08 13:31:14 +08:00
Promise . reject ( { message : 'device not found' } ) ,
Promise . reject ( { message : 'device offline' } ) ,
Promise . reject ( { message : 'device still connecting' } ) ,
2018-07-03 09:30:15 +08:00
Promise . resolve ( '1' )
) ;
return emu . wait _for _emulator ( port ) . then ( ( ) => {
expect ( emu . wait _for _emulator ) . toHaveBeenCalledTimes ( 4 ) ;
} ) ;
} ) ;
it ( 'should throw an error if shell fails for an unknown reason' , ( ) => {
const errorMessage = { message : 'Some unknown error' } ;
AdbSpy . shell . and . returnValue ( Promise . reject ( errorMessage ) ) ;
return emu . wait _for _emulator ( port ) . then (
( ) => fail ( 'Unexpectedly resolved' ) ,
err => {
expect ( err ) . toBe ( errorMessage ) ;
}
) ;
} ) ;
} ) ;
describe ( 'wait_for_boot' , ( ) => {
const port = 5656 ;
const emulatorId = ` emulator- ${ port } ` ;
const psOutput = `
root 1 0 8504 1512 SyS _epoll _ 00000000 S / init
u0 _a1 2044 1350 1423452 47256 SyS _epoll _ 00000000 S android . process . acore
u0 _a51 2963 1350 1417724 37492 SyS _epoll _ 00000000 S com . google . process . gapps
` ;
let AdbSpy ;
beforeEach ( ( ) => {
// If we use Jasmine's fake clock, we need to re-require the target module,
// or else it will not work.
jasmine . clock ( ) . install ( ) ;
emu = rewire ( '../../bin/templates/cordova/lib/emulator' ) ;
AdbSpy = jasmine . createSpyObj ( 'Adb' , [ 'shell' ] ) ;
emu . _ _set _ _ ( 'Adb' , AdbSpy ) ;
spyOn ( emu , 'wait_for_boot' ) . and . callThrough ( ) ;
// Stop the logs from being polluted
const proc = emu . _ _get _ _ ( 'process' ) ;
spyOn ( proc . stdout , 'write' ) . and . stub ( ) ;
} ) ;
afterEach ( ( ) => {
jasmine . clock ( ) . uninstall ( ) ;
} ) ;
it ( 'should resolve with true if the system has booted' , ( ) => {
AdbSpy . shell . and . callFake ( ( emuId , shellArgs ) => {
expect ( emuId ) . toBe ( emulatorId ) ;
expect ( shellArgs ) . toContain ( 'ps' ) ;
return Promise . resolve ( psOutput ) ;
} ) ;
return emu . wait _for _boot ( emulatorId ) . then ( isReady => {
expect ( isReady ) . toBe ( true ) ;
} ) ;
} ) ;
it ( 'should should check boot status at regular intervals until ready' , ( ) => {
const retryInterval = emu . _ _get _ _ ( 'CHECK_BOOTED_INTERVAL' ) ;
const RETRIES = 10 ;
let shellPromise = Promise . resolve ( '' ) ;
AdbSpy . shell . and . returnValue ( shellPromise ) ;
const waitPromise = emu . wait _for _boot ( emulatorId ) ;
let attempts = 0 ;
function tickTimer ( ) {
shellPromise . then ( ( ) => {
if ( attempts + 1 === RETRIES ) {
AdbSpy . shell . and . returnValue ( Promise . resolve ( psOutput ) ) ;
jasmine . clock ( ) . tick ( retryInterval ) ;
} else {
attempts ++ ;
shellPromise = Promise . resolve ( '' ) ;
AdbSpy . shell . and . returnValue ( shellPromise ) ;
jasmine . clock ( ) . tick ( retryInterval ) ;
tickTimer ( ) ;
}
} ) ;
}
tickTimer ( ) ;
// After all the retries and eventual success, this is called
return waitPromise . then ( isReady => {
expect ( isReady ) . toBe ( true ) ;
expect ( emu . wait _for _boot ) . toHaveBeenCalledTimes ( RETRIES + 1 ) ;
} ) ;
} ) ;
it ( 'should should check boot status at regular intervals until timeout' , ( ) => {
const retryInterval = emu . _ _get _ _ ( 'CHECK_BOOTED_INTERVAL' ) ;
const TIMEOUT = 9000 ;
const expectedRetries = Math . floor ( TIMEOUT / retryInterval ) ;
let shellPromise = Promise . resolve ( '' ) ;
AdbSpy . shell . and . returnValue ( shellPromise ) ;
const waitPromise = emu . wait _for _boot ( emulatorId , TIMEOUT ) ;
let attempts = 0 ;
function tickTimer ( ) {
shellPromise . then ( ( ) => {
attempts ++ ;
shellPromise = Promise . resolve ( '' ) ;
AdbSpy . shell . and . returnValue ( shellPromise ) ;
jasmine . clock ( ) . tick ( retryInterval ) ;
if ( attempts < expectedRetries ) {
tickTimer ( ) ;
}
} ) ;
}
tickTimer ( ) ;
// After all the retries and eventual success, this is called
return waitPromise . then ( isReady => {
expect ( isReady ) . toBe ( false ) ;
expect ( emu . wait _for _boot ) . toHaveBeenCalledTimes ( expectedRetries + 1 ) ;
} ) ;
} ) ;
} ) ;
describe ( 'resolveTarget' , ( ) => {
const arch = 'arm7-test' ;
beforeEach ( ( ) => {
const buildSpy = jasmine . createSpyObj ( 'build' , [ 'detectArchitecture' ] ) ;
buildSpy . detectArchitecture . and . returnValue ( Promise . resolve ( arch ) ) ;
emu . _ _set _ _ ( 'build' , buildSpy ) ;
spyOn ( emu , 'list_started' ) . and . returnValue ( Promise . resolve ( EMULATOR _LIST ) ) ;
} ) ;
it ( 'should throw an error if there are no running emulators' , ( ) => {
emu . list _started . and . returnValue ( Promise . resolve ( [ ] ) ) ;
return emu . resolveTarget ( ) . then (
( ) => fail ( 'Unexpectedly resolved' ) ,
err => {
expect ( err ) . toEqual ( jasmine . any ( CordovaError ) ) ;
}
) ;
} ) ;
it ( 'should throw an error if the requested emulator is not running' , ( ) => {
const targetEmulator = 'unstarted-emu' ;
return emu . resolveTarget ( targetEmulator ) . then (
( ) => fail ( 'Unexpectedly resolved' ) ,
err => {
expect ( err . message ) . toContain ( targetEmulator ) ;
}
) ;
} ) ;
it ( 'should return info on the first running emulator if none is specified' , ( ) => {
return emu . resolveTarget ( ) . then ( emulatorInfo => {
expect ( emulatorInfo . target ) . toBe ( EMULATOR _LIST [ 0 ] ) ;
} ) ;
} ) ;
it ( 'should return the emulator info' , ( ) => {
return emu . resolveTarget ( EMULATOR _LIST [ 1 ] ) . then ( emulatorInfo => {
expect ( emulatorInfo ) . toEqual ( { target : EMULATOR _LIST [ 1 ] , arch , isEmulator : true } ) ;
} ) ;
} ) ;
} ) ;
describe ( 'install' , ( ) => {
let AndroidManifestSpy ;
let AndroidManifestFns ;
let AndroidManifestGetActivitySpy ;
let AdbSpy ;
let buildSpy ;
let childProcessSpy ;
let target ;
beforeEach ( ( ) => {
target = { target : EMULATOR _LIST [ 1 ] , arch : 'arm7' , isEmulator : true } ;
buildSpy = jasmine . createSpyObj ( 'build' , [ 'findBestApkForArchitecture' ] ) ;
emu . _ _set _ _ ( 'build' , buildSpy ) ;
AndroidManifestFns = jasmine . createSpyObj ( 'AndroidManifestFns' , [ 'getPackageId' , 'getActivity' ] ) ;
AndroidManifestGetActivitySpy = jasmine . createSpyObj ( 'getActivity' , [ 'getName' ] ) ;
AndroidManifestFns . getActivity . and . returnValue ( AndroidManifestGetActivitySpy ) ;
AndroidManifestSpy = jasmine . createSpy ( 'AndroidManifest' ) . and . returnValue ( AndroidManifestFns ) ;
emu . _ _set _ _ ( 'AndroidManifest' , AndroidManifestSpy ) ;
AdbSpy = jasmine . createSpyObj ( 'Adb' , [ 'shell' , 'start' , 'uninstall' ] ) ;
AdbSpy . shell . and . returnValue ( Promise . resolve ( ) ) ;
AdbSpy . start . and . returnValue ( Promise . resolve ( ) ) ;
AdbSpy . uninstall . and . returnValue ( Promise . resolve ( ) ) ;
emu . _ _set _ _ ( 'Adb' , AdbSpy ) ;
childProcessSpy = jasmine . createSpyObj ( 'child_process' , [ 'exec' ] ) ;
childProcessSpy . exec . and . callFake ( ( cmd , opts , callback ) => callback ( ) ) ;
emu . _ _set _ _ ( 'child_process' , childProcessSpy ) ;
} ) ;
it ( 'should get the full target object if only id is specified' , ( ) => {
const targetId = target . target ;
spyOn ( emu , 'resolveTarget' ) . and . returnValue ( Promise . resolve ( target ) ) ;
return emu . install ( targetId , { } ) . then ( ( ) => {
expect ( emu . resolveTarget ) . toHaveBeenCalledWith ( targetId ) ;
} ) ;
} ) ;
it ( 'should install to the passed target' , ( ) => {
return emu . install ( target , { } ) . then ( ( ) => {
const execCmd = childProcessSpy . exec . calls . argsFor ( 0 ) [ 0 ] ;
expect ( execCmd ) . toContain ( ` -s ${ target . target } install ` ) ;
} ) ;
} ) ;
it ( 'should install the correct apk based on the architecture and build results' , ( ) => {
const buildResults = {
apkPaths : 'path/to/apks' ,
buildType : 'debug' ,
buildMethod : 'foo'
} ;
const apkPath = 'my/apk/path/app.apk' ;
buildSpy . findBestApkForArchitecture . and . returnValue ( apkPath ) ;
return emu . install ( target , buildResults ) . then ( ( ) => {
expect ( buildSpy . findBestApkForArchitecture ) . toHaveBeenCalledWith ( buildResults , target . arch ) ;
const execCmd = childProcessSpy . exec . calls . argsFor ( 0 ) [ 0 ] ;
expect ( execCmd ) . toMatch ( new RegExp ( ` install.* ${ apkPath } ` ) ) ;
} ) ;
} ) ;
it ( 'should uninstall and reinstall app if failure is due to different certificates' , ( ) => {
let execAlreadyCalled ;
childProcessSpy . exec . and . callFake ( ( cmd , opts , callback ) => {
if ( ! execAlreadyCalled ) {
execAlreadyCalled = true ;
callback ( null , 'Failure: INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES' ) ;
} else {
callback ( ) ;
}
} ) ;
return emu . install ( target , { } ) . then ( ( ) => {
expect ( childProcessSpy . exec ) . toHaveBeenCalledTimes ( 2 ) ;
expect ( AdbSpy . uninstall ) . toHaveBeenCalled ( ) ;
} ) ;
} ) ;
it ( 'should throw any error not caused by different certificates' , ( ) => {
const errorMsg = 'Failure: Failed to install' ;
childProcessSpy . exec . and . callFake ( ( cmd , opts , callback ) => {
callback ( null , errorMsg ) ;
} ) ;
return emu . install ( target , { } ) . then (
( ) => fail ( 'Unexpectedly resolved' ) ,
err => {
expect ( err ) . toEqual ( jasmine . any ( CordovaError ) ) ;
expect ( err . message ) . toContain ( errorMsg ) ;
}
) ;
} ) ;
it ( 'should unlock the screen on device' , ( ) => {
return emu . install ( target , { } ) . then ( ( ) => {
expect ( AdbSpy . shell ) . toHaveBeenCalledWith ( target . target , 'input keyevent 82' ) ;
} ) ;
} ) ;
it ( 'should start the newly installed app on the device' , ( ) => {
const packageId = 'unittestapp' ;
const activityName = 'TestActivity' ;
AndroidManifestFns . getPackageId . and . returnValue ( packageId ) ;
AndroidManifestGetActivitySpy . getName . and . returnValue ( activityName ) ;
return emu . install ( target , { } ) . then ( ( ) => {
expect ( AdbSpy . start ) . toHaveBeenCalledWith ( target . target , ` ${ packageId } /. ${ activityName } ` ) ;
} ) ;
2017-10-06 04:18:42 +08:00
} ) ;
} ) ;
2017-03-15 04:12:57 +08:00
} ) ;