Enable to use avd skins
79
README.md
|
@ -15,7 +15,7 @@ Docker is installed in your system.
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
1. Android emulator
|
1. Android emulator with different devices / skins
|
||||||
2. noVNC
|
2. noVNC
|
||||||
3. Appium server
|
3. Appium server
|
||||||
4. Able to connect to selenium grid
|
4. Able to connect to selenium grid
|
||||||
|
@ -28,26 +28,22 @@ Quick Start
|
||||||
|
|
||||||
1. Enable **Virtualization** under **System Setup** in **BIOS**. (It is only for Ubuntu OS. If you use different OS, you can skip this step).
|
1. Enable **Virtualization** under **System Setup** in **BIOS**. (It is only for Ubuntu OS. If you use different OS, you can skip this step).
|
||||||
|
|
||||||
2. Run docker-appium with command:
|
2. Run docker-appium.
|
||||||
|
|
||||||
|
**Optional arguments**
|
||||||
|
|
||||||
|
--privileged : Only for ubuntu OS. This flag allow to use system image x86 for better performance
|
||||||
|
-v <path_of_apk>:/target_apk : Path of android apk that want to be tested
|
||||||
|
-e DEVICE="<device_name>" : Device name. Default device is Nexus 5
|
||||||
|
-e ANDROID_VERSION=<android_version>: Android version of emulator. Default android version is 5.0
|
||||||
|
-e EMULATOR_TYPE=<armeabi/x86> : Emulator system image. Default system image is armeabi
|
||||||
|
|
||||||
|
**An Example command to run docker-appium under linux**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d -p 6080:6080 -p 4723:4723 -v <path_of_apk_that_want_to_be_tested>:/target_apk -e ANDROID_VERSION=<target_android_version> -e EMULATOR_TYPE=<emulator_type> -e CONNECT_TO_GRID=<True/False> --name appium-container butomo1989/docker-appium
|
docker run --privileged -d -p 6080:6080 -p 4723:4723 -v $PWD/example/sample_apk:/target_apk -e DEVICE="Nexus 5" -e ANDROID_VERSION=5.0 -e EMULATOR_TYPE=armeabi --name appium-container butomo1989/docker-appium
|
||||||
```
|
```
|
||||||
|
|
||||||
An Example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run -d -p 6080:6080 -p 4723:4723 -v $PWD/example/sample_apk:/target_apk -e ANDROID_VERSION=4.2.2 -e EMULATOR_TYPE=armeabi -e CONNECT_TO_GRID=False --name appium-container butomo1989/docker-appium
|
|
||||||
```
|
|
||||||
**Optional arguments for CONNECT\_TO\_GRID=True**
|
|
||||||
|
|
||||||
-e APPIUM_HOST="<host_ip_address>": if appium is running under different host. default value: 127.0.0.1
|
|
||||||
-e APPIUM_PORT=<port_number>: if appium is running under different port. default port: 4723
|
|
||||||
-e SELENIUM_HOST="<host_ip_address>": if selenium hub is running under different host. default value: 172.17.0.1
|
|
||||||
-e SELENIUM_PORT=<port_number>: if selenium hub is running under different port. default port: 4444
|
|
||||||
|
|
||||||
**Note: use flag *--privileged* and *EMULATOR_TYPE=x86* for ubuntu OS to make emulator faster**
|
|
||||||
|
|
||||||
2. Verify the ip address of docker-machine.
|
2. Verify the ip address of docker-machine.
|
||||||
|
|
||||||
- For OSX, you can find out by using following command:
|
- For OSX, you can find out by using following command:
|
||||||
|
@ -58,7 +54,7 @@ Quick Start
|
||||||
|
|
||||||
- For different OS, localhost should work.
|
- For different OS, localhost should work.
|
||||||
|
|
||||||
3. Open ***http://docker-machine-ip-address:6080/vnc.html*** from web browser and connect to it without password.
|
3. Open ***http://docker-machine-ip-address:6080/vnc.html*** from web browser.
|
||||||
|
|
||||||
![][noVNC]
|
![][noVNC]
|
||||||
|
|
||||||
|
@ -66,17 +62,60 @@ Quick Start
|
||||||
|
|
||||||
![][Appium is ready]
|
![][Appium is ready]
|
||||||
|
|
||||||
*The name of created emulator can be seen in that terminal. In screenshot above, the emulator name is* ***emulator_4.2.2***.
|
*The name of created emulator can be seen in that terminal. In screenshot above, the emulator name is* ***nexus\_5_5.0***.
|
||||||
|
|
||||||
5. Run your UI tests by using docker-appium and Android emulator will be started automatically by following desire capability:
|
5. Run your UI tests by using docker-appium and Android emulator will be started automatically by following desire capability:
|
||||||
|
|
||||||
```
|
```
|
||||||
desired_caps = {
|
desired_caps = {
|
||||||
'avd': 'emulator_4.2.2'
|
'avd': 'nexus_5_5.0'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
***Note: In folder "example" there is an example of Appium-UITest that is written in python.***
|
***Note: In folder "example" there is an example of Appium-UITest that is written in python.***
|
||||||
|
|
||||||
|
Connect to Selenium Grid
|
||||||
|
------------------------
|
||||||
|
pass environment variable **CONNECT\_TO\_GRID=True** to connect docker-appium to your selenium grid.
|
||||||
|
|
||||||
|
**Optional arguments**
|
||||||
|
|
||||||
|
-e APPIUM_HOST="<host_ip_address>" : where / on which instance is appium server running. Default value: 127.0.0.1
|
||||||
|
-e APPIUM_PORT=<port_number> : which port is appium server running. Default port: 4723
|
||||||
|
-e SELENIUM_HOST="<host_ip_address>" : where / on which instance is selenium grid running. Default value: 172.17.0.1
|
||||||
|
-e SELENIUM_PORT=<port_number> : which port is selenium grid running. default port: 4444
|
||||||
|
|
||||||
|
![][connect to grid 1] ![][connect to grid 2]
|
||||||
|
|
||||||
|
List of Devices
|
||||||
|
---------------
|
||||||
|
Type | Device Name
|
||||||
|
--- | ---
|
||||||
|
Phone | Galaxy Nexus
|
||||||
|
Phone | Nexus 4
|
||||||
|
Phone | Nexus 5
|
||||||
|
Phone | Nexus 5x
|
||||||
|
Phone | Nexus 6
|
||||||
|
Phone | Nexus 6P
|
||||||
|
Phone | Nexus One
|
||||||
|
Phone | Nexus S
|
||||||
|
Tablet | Pixel C
|
||||||
|
Tablet | Nexus 7
|
||||||
|
Tablet | Nexus 9
|
||||||
|
Tablet | Nexus 10
|
||||||
|
|
||||||
|
![][nexus 5]
|
||||||
|
|
||||||
|
Troubleshooting
|
||||||
|
---------------
|
||||||
|
All logs inside container are stored under folder **/var/log/supervisor**. you can print out log file by using **docker exec**. Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it appium-container tail -f /var/log/supervisor/docker-appium.stdout.log
|
||||||
|
```
|
||||||
|
|
||||||
[noVNC]: <images/noVNC.png> "login with noVNC to see what happen inside container"
|
[noVNC]: <images/noVNC.png> "login with noVNC to see what happen inside container"
|
||||||
[Appium is ready]: <images/appium.png> "appium is ready"
|
[Appium is ready]: <images/appium.png> "appium is ready"
|
||||||
|
[connect to grid 1]: <images/appium_with_selenium_grid_01.png>
|
||||||
|
[connect to grid 2]: <images/appium_with_selenium_grid_02.png>
|
||||||
|
[nexus 5]: <images/run_under_nexus_5.png>
|
||||||
|
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 40 KiB |
BIN
images/appium_with_selenium_grid_01.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
images/appium_with_selenium_grid_02.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
images/noVNC.png
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 51 KiB |
BIN
images/run_under_nexus_5.png
Normal file
After Width: | Height: | Size: 219 KiB |
Before Width: | Height: | Size: 326 KiB |
Before Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 48 KiB |
|
@ -1,59 +0,0 @@
|
||||||
parts {
|
|
||||||
device {
|
|
||||||
display {
|
|
||||||
width 1200
|
|
||||||
height 1920
|
|
||||||
x 0
|
|
||||||
y 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
portrait {
|
|
||||||
background {
|
|
||||||
image port_back.png
|
|
||||||
}
|
|
||||||
onion {
|
|
||||||
image port_fore.png
|
|
||||||
}
|
|
||||||
}
|
|
||||||
landscape {
|
|
||||||
background {
|
|
||||||
image land_back.png
|
|
||||||
}
|
|
||||||
onion {
|
|
||||||
image land_fore.png
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
layouts {
|
|
||||||
portrait {
|
|
||||||
width 1596
|
|
||||||
height 2571
|
|
||||||
event EV_SW:0:1
|
|
||||||
part1 {
|
|
||||||
name portrait
|
|
||||||
x 0
|
|
||||||
y 0
|
|
||||||
}
|
|
||||||
part2 {
|
|
||||||
name device
|
|
||||||
x 195
|
|
||||||
y 301
|
|
||||||
}
|
|
||||||
}
|
|
||||||
landscape {
|
|
||||||
width 2772
|
|
||||||
height 1479
|
|
||||||
event EV_SW:0:0
|
|
||||||
part1 {
|
|
||||||
name landscape
|
|
||||||
x 0
|
|
||||||
y 0
|
|
||||||
}
|
|
||||||
part2 {
|
|
||||||
name device
|
|
||||||
x 423
|
|
||||||
y 1320
|
|
||||||
rotation 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 358 KiB |
Before Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 42 KiB |
1
skins/source.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
copied from Android Studio IDE
|
|
@ -28,8 +28,7 @@ def get_api_level(android_version):
|
||||||
|
|
||||||
item_info = item.split('-')
|
item_info = item.split('-')
|
||||||
api_version = re.search('%s(.*)%s' % ('API', ','), item_info[1]).group(1).strip()
|
api_version = re.search('%s(.*)%s' % ('API', ','), item_info[1]).group(1).strip()
|
||||||
logger.info(
|
logger.info('API level: {api}'.format(api=api_version))
|
||||||
'API level: {api}'.format(api=api_version))
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('List of packages is empty!')
|
raise RuntimeError('List of packages is empty!')
|
||||||
|
|
||||||
|
@ -64,12 +63,16 @@ def install_package(android_path, emulator_file, api_level, sys_img):
|
||||||
subprocess.check_call('xterm -e \"{cmd}\"'.format(cmd=cmd), shell=True)
|
subprocess.check_call('xterm -e \"{cmd}\"'.format(cmd=cmd), shell=True)
|
||||||
|
|
||||||
|
|
||||||
def create_avd(android_path, avd_name, api_level):
|
def create_avd(android_path, device, skin, avd_name, api_level):
|
||||||
"""
|
"""
|
||||||
Create android virtual device.
|
Create android virtual device.
|
||||||
|
|
||||||
:param android_path: location where android SDK is installed
|
:param android_path: location where android SDK is installed
|
||||||
:type android_path: str
|
:type android_path: str
|
||||||
|
:param device: name of device
|
||||||
|
:type device: str
|
||||||
|
:param skin: emulator skin that want to be used
|
||||||
|
:type skin: str
|
||||||
:param avd_name: desire name
|
:param avd_name: desire name
|
||||||
:type avd_name: str
|
:type avd_name: str
|
||||||
:param api_level: api level
|
:param api_level: api level
|
||||||
|
@ -83,6 +86,8 @@ def create_avd(android_path, avd_name, api_level):
|
||||||
|
|
||||||
# Create android emulator
|
# Create android emulator
|
||||||
cmd = 'echo no | android create avd -f -n {name} -t android-{api}'.format(name=avd_name, api=api_level)
|
cmd = 'echo no | android create avd -f -n {name} -t android-{api}'.format(name=avd_name, api=api_level)
|
||||||
|
if device and skin:
|
||||||
|
cmd += ' -d {device} -s {skin}'.format(device=device.replace(' ', '\ '), skin=skin)
|
||||||
logger.info('Emulator creation command : {cmd}'.format(cmd=cmd))
|
logger.info('Emulator creation command : {cmd}'.format(cmd=cmd))
|
||||||
subprocess.check_call('xterm -e \"{cmd}\"'.format(cmd=cmd), shell=True)
|
subprocess.check_call('xterm -e \"{cmd}\"'.format(cmd=cmd), shell=True)
|
||||||
|
|
||||||
|
|
|
@ -30,14 +30,18 @@ def start():
|
||||||
emulator_file = 'emulator64-x86' if emulator_type == TYPE_X86 else 'emulator64-arm'
|
emulator_file = 'emulator64-x86' if emulator_type == TYPE_X86 else 'emulator64-arm'
|
||||||
logger.info('Emulator file: {file}'.format(file=emulator_file))
|
logger.info('Emulator file: {file}'.format(file=emulator_file))
|
||||||
api_level = android.get_api_level(android_version)
|
api_level = android.get_api_level(android_version)
|
||||||
sys_img = 'x86' if emulator_type == TYPE_X86 else 'armeabi-v7a'
|
sys_img = 'x86_64' if emulator_type == TYPE_X86 else 'armeabi-v7a'
|
||||||
logger.info('System image: {sys_img}'.format(sys_img=sys_img))
|
logger.info('System image: {sys_img}'.format(sys_img=sys_img))
|
||||||
android.install_package(android_path, emulator_file, api_level, sys_img)
|
android.install_package(android_path, emulator_file, api_level, sys_img)
|
||||||
|
|
||||||
# Create android virtual device
|
# Create android virtual device
|
||||||
avd_name = 'emulator_{version}'.format(version=android_version)
|
device_name = os.getenv('DEVICE', 'Nexus 5')
|
||||||
|
logger.info('Device: {device}'.format(device=device_name))
|
||||||
|
skin_name = device_name.replace(' ', '_').lower()
|
||||||
|
logger.info('Skin: {skin}'.format(skin=skin_name))
|
||||||
|
avd_name = '{device}_{version}'.format(device=skin_name, version=android_version)
|
||||||
logger.info('AVD name: {avd}'.format(avd=avd_name))
|
logger.info('AVD name: {avd}'.format(avd=avd_name))
|
||||||
android.create_avd(android_path, avd_name, api_level)
|
android.create_avd(android_path, device_name, skin_name, avd_name, api_level)
|
||||||
|
|
||||||
# Run appium server
|
# Run appium server
|
||||||
appium.run(connect_to_grid, avd_name, android_version)
|
appium.run(connect_to_grid, avd_name, android_version)
|
||||||
|
|
|
@ -11,7 +11,9 @@ class TestAvd(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.android_path = '/root'
|
self.android_path = '/root'
|
||||||
self.avd_name = 'test'
|
self.device = 'Nexus\ 5'
|
||||||
|
self.skin = 'nexus_5'
|
||||||
|
self.avd_name = 'nexus_5_5.0'
|
||||||
self.api_level = 21
|
self.api_level = 21
|
||||||
|
|
||||||
@mock.patch('os.symlink')
|
@mock.patch('os.symlink')
|
||||||
|
@ -22,7 +24,7 @@ class TestAvd(TestCase):
|
||||||
self.assertFalse(mocked_list_dir.called)
|
self.assertFalse(mocked_list_dir.called)
|
||||||
self.assertFalse(mocked_sys_link.called)
|
self.assertFalse(mocked_sys_link.called)
|
||||||
self.assertFalse(mocked_suprocess.called)
|
self.assertFalse(mocked_suprocess.called)
|
||||||
android.create_avd(self.android_path, self.avd_name, self.api_level)
|
android.create_avd(self.android_path, self.device, self.skin, self.avd_name, self.api_level)
|
||||||
self.assertTrue(mocked_list_dir.called)
|
self.assertTrue(mocked_list_dir.called)
|
||||||
self.assertTrue(mocked_sys_link.called)
|
self.assertTrue(mocked_sys_link.called)
|
||||||
self.assertTrue(mocked_suprocess.called)
|
self.assertTrue(mocked_suprocess.called)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""Unit test for appium.py."""
|
"""Unit test for appium.py."""
|
||||||
from unittest import TestCase
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
|
|
@ -25,5 +25,6 @@ stderr_logfile=%(ENV_LOG_PATH)s/novnc.stderr.log
|
||||||
|
|
||||||
[program:docker-appium]
|
[program:docker-appium]
|
||||||
command=python -m src.service
|
command=python -m src.service
|
||||||
|
autorestart=false
|
||||||
stdout_logfile=%(ENV_LOG_PATH)s/docker-appium.stdout.log
|
stdout_logfile=%(ENV_LOG_PATH)s/docker-appium.stdout.log
|
||||||
stderr_logfile=%(ENV_LOG_PATH)s/docker-appium.stderr.log
|
stderr_logfile=%(ENV_LOG_PATH)s/docker-appium.stderr.log
|
||||||
|
|