Enable preinstallation android packages

This commit is contained in:
butomo1989 2017-03-24 16:49:07 +01:00
parent ef3ead7067
commit 4fdf66f89f
27 changed files with 466 additions and 500 deletions

View file

@ -1,10 +1,15 @@
language: python language: python
python: python:
- "2.7" - "2.7"
install: "pip install -r requirements.txt" install: "pip install -r requirements.txt"
script: nosetests
script: bash release.sh test 5.0 x86
branches: branches:
only: only:
- master - master
after_success: after_success:
- bash <(curl -s https://codecov.io/bash) - bash <(curl -s https://codecov.io/bash)

View file

@ -78,21 +78,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN git clone https://github.com/kanaka/noVNC.git \ RUN git clone https://github.com/kanaka/noVNC.git \
&& cd noVNC/utils && git clone https://github.com/kanaka/websockify websockify && cd noVNC/utils && git clone https://github.com/kanaka/websockify websockify
#====================================== #=====================
# Install Android SDK and its packages # Install Android SDK
#====================================== #=====================
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/jre ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/jre
ENV PATH ${PATH}:${JAVA_HOME}/bin ENV PATH ${PATH}:${JAVA_HOME}/bin
ENV SDK_VERSION=25.2.3 \ ENV SDK_VERSION=25.2.3 \
BUILD_TOOL=25.0.2 \
ANDROID_HOME=/root ANDROID_HOME=/root
RUN wget -O android.zip https://dl.google.com/android/repository/tools_r${SDK_VERSION}-linux.zip \ RUN wget -O android.zip https://dl.google.com/android/repository/tools_r${SDK_VERSION}-linux.zip \
&& unzip android.zip && rm android.zip && unzip android.zip && rm android.zip
ENV PATH ${PATH}:${ANDROID_HOME}/tools ENV PATH ${PATH}:${ANDROID_HOME}/tools
RUN echo y | android update sdk --no-ui -a --filter platform-tools,build-tools-${BUILD_TOOL} RUN echo y | android update sdk --no-ui -a --filter platform-tools
ENV PATH ${PATH}:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/build-tools ENV PATH ${PATH}:${ANDROID_HOME}/platform-tools
RUN mv ${ANDROID_HOME}/tools/emulator ${ANDROID_HOME}/tools/emulator.backup
#==================================== #====================================
# Install latest nodejs, npm, appium # Install latest nodejs, npm, appium
@ -102,6 +100,26 @@ RUN curl -sL https://deb.nodesource.com/setup_7.x | bash - \
ENV APPIUM_VERSION 1.6.3 ENV APPIUM_VERSION 1.6.3
RUN npm install -g appium@$APPIUM_VERSION && npm cache clean RUN npm install -g appium@$APPIUM_VERSION && npm cache clean
#======================
# Install SDK packages
#======================
ARG ANDROID_VERSION=5.0.1
ARG BUILD_TOOL=21.1.2
ARG API_LEVEL=21
ARG PROCESSOR=x86
ARG SYS_IMG=x86_64
ENV ANDROID_VERSION=$ANDROID_VERSION \
BUILD_TOOL=$BUILD_TOOL \
API_LEVEL=$API_LEVEL \
PROCESSOR=$PROCESSOR \
SYS_IMG=$SYS_IMG
RUN echo y | android update sdk --no-ui -a --filter build-tools-${BUILD_TOOL}
ENV PATH ${PATH}:${ANDROID_HOME}/build-tools
RUN rm ${ANDROID_HOME}/tools/emulator \
&& ln -s ${ANDROID_HOME}/tools/emulator64-${PROCESSOR} ${ANDROID_HOME}/tools/emulator
RUN echo y | android update sdk --no-ui -a -t android-${API_LEVEL},sys-img-${SYS_IMG}-android-${API_LEVEL}
#================================================ #================================================
# noVNC Default Configurations # noVNC Default Configurations
# These Configurations can be changed through -e # These Configurations can be changed through -e
@ -127,11 +145,6 @@ RUN ln -s noVNC/vnc_auto.html noVNC/index.html
#=============== #===============
EXPOSE 4723 6080 EXPOSE 4723 6080
#==================
# Add Browser APKs
#==================
COPY browser_apk /root/browser_apk
#====================== #======================
# Add Emulator Devices # Add Emulator Devices
#====================== #======================
@ -140,6 +153,6 @@ COPY devices /root/devices
#=================== #===================
# Run docker-appium # Run docker-appium
#=================== #===================
COPY supervisord.conf /root/
COPY src /root/src COPY src /root/src
COPY supervisord.conf /root/
CMD /usr/bin/supervisord --configuration supervisord.conf CMD /usr/bin/supervisord --configuration supervisord.conf

View file

@ -1 +0,0 @@
Browser apps are downloaded from http://www.apkmirror.com

View file

@ -1,29 +0,0 @@
import unittest
from appium import webdriver
class MSiteChromeAndroidUITests(unittest.TestCase):
def setUp(self):
desired_caps = {
'platformName': 'Android',
'deviceName': 'Android Emulator',
# For emulator type armeabi, please use browser apk :
# /root/browser_apk/chrome_55.0.2883.91-288309100_min_android4.1_armeabi-v7a.apk
'app': '/root/browser_apk/chrome_55.0.2883.91_min_android4.1_x86.apk',
'appPackage': 'com.android.chrome',
'appActivity': 'com.google.android.apps.chrome.Main',
'avd': 'nexus_5_5.0'
}
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
def test_open_url(self):
self.driver.get('http://targeturl.com')
def tearDown(self):
self.driver.quit()
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(MSiteChromeAndroidUITests)
unittest.TextTestRunner(verbosity=2).run(suite)

View file

@ -1,29 +0,0 @@
import unittest
from appium import webdriver
class MSiteFirefoxAndroidUITests(unittest.TestCase):
def setUp(self):
desired_caps = {
'platformName': 'Android',
'deviceName': 'Android Emulator',
# For emulator type armeabi, please use browser apk :
# /root/browser_apk/firefox_51.0-2015466281_min_android4.0.3_armeabi-v7a.apk
'app': '/root/browser_apk/firefox_51.0-2015466284_min_android4.0.3_x86.apk',
'appPackage': 'org.mozilla.firefox',
'appActivity': 'org.mozilla.gecko.LauncherActivity',
'avd': 'nexus_5_5.0'
}
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
def test_open_url(self):
self.driver.get('http://targeturl.com')
def tearDown(self):
self.driver.quit()
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(MSiteFirefoxAndroidUITests)
unittest.TextTestRunner(verbosity=2).run(suite)

126
release.sh Normal file
View file

@ -0,0 +1,126 @@
#!/bin/bash
# Bash version should >= 4 to be able to run this script.
IMAGE="butomo1989/docker-android"
if [ -z "$1" ]; then
read -p "Environment (test|build|push|all) : " TASK
else
TASK=$1
fi
if [ -z "$2" ]; then
read -p "Android version: " GIVEN_VERSION
else
GIVEN_VERSION=$2
fi
if [ -z "$3" ]; then
read -p "Processor type (x86|arm): " PROCESSOR
else
PROCESSOR=$3
fi
function build_tool() {
declare -A build_tools=(
[5.0.1]=21.1.2
[5.1.1]=22.0.1
[6.0]=23.0.3
[7.0]=24.0.3
[7.1.1]=25.0.2
)
# TODO: Need to be sorted
for key in "${!build_tools[@]}"; do
if [[ $key == *"$GIVEN_VERSION"* ]]; then
version=$key
fi
done
# If version cannot be found in the list
if [ -z "$version" ]; then
echo "Version is not found in the list or not supported! Support only version 5.0.1, 5.1.1, 6.0, 7.0, 7.1.1"
exit 1
fi
echo "Android version: $version"
build_tools=${build_tools[$version]}
echo "Build tool: $build_tools"
}
function api_level() {
declare -A levels=(
[5.0.1]=21
[5.1.1]=22
[6.0]=23
[7.0]=24
[7.1.1]=25
)
level=${levels[$version]}
echo "Api level: $level"
}
function system_image() {
case $PROCESSOR in
x86)
sys_img=x86_64
;;
arm)
sys_img=armeabi-v7a
;;
*)
echo "Invalid processor! Valid options: x86, arm"
exit 1
;;
esac
echo "Processor: $PROCESSOR"
echo "System Image: $sys_img"
}
function init() {
build_tool
api_level
system_image
}
init
IMAGE_NAME="$IMAGE-$PROCESSOR-$version"
echo "Image tag: $TAG"
function test() {
(export ANDROID_HOME=/root && export ANDROID_VERSION=$version && export API_LEVEL=$level \
&& export PROCESSOR=$PROCESSOR && export SYS_IMG=$sys_img && nosetests -v)
}
function build() {
# Remove pyc files
find . -name "*.pyc" -exec rm -f {} \;
docker build -t $IMAGE_NAME --build-arg ANDROID_VERSION=$version --build-arg BUILD_TOOL=$$build_tools \
--build-arg API_LEVEL=$level --build-arg PROCESSOR=$PROCESSOR --build-arg SYS_IMG=$sys_img .
}
function push() {
docker push $IMAGE_NAME
}
case $TASK in
test)
test
;;
build)
build
;;
push)
push
;;
all)
test
build
push
;;
*)
echo "Invalid environment! Valid options: test, build, push, all"
;;
esac

View file

@ -1,4 +1,3 @@
autopep8==1.2.4
coverage==4.2 coverage==4.2
mock==2.0.0 mock==2.0.0
nose==1.3.7 nose==1.3.7

View file

@ -1,7 +1,6 @@
import os import os
ROOT = '/root' ROOT = '/root'
ANDROID_PATH = os.getenv('ANDROID_HOME', '/root')
WORKDIR = os.path.dirname(__file__) WORKDIR = os.path.dirname(__file__)
CONFIG_FILE = os.path.join(WORKDIR, 'nodeconfig.json') CONFIG_FILE = os.path.join(WORKDIR, 'nodeconfig.json')
LOGGING_FILE = os.path.join(WORKDIR, 'logging.conf') LOGGING_FILE = os.path.join(WORKDIR, 'logging.conf')

View file

@ -1,126 +0,0 @@
import logging
import os
import subprocess
from src import ANDROID_PATH
logger = logging.getLogger('android')
EMULATOR = 'emulator'
TYPE_ARMEABI = 'armeabi'
TYPE_X86 = 'x86'
TYPE_X86_64 = 'x86_64'
API_LEVELS = {
'2.1': 7,
'2.2': 8,
'2.3.1': 9,
'2.3.3': 1,
'3.0': 11,
'3.1': 12,
'3.2': 13,
'4.0': 14,
'4.0.3': 15,
'4.1.2': 16,
'4.2.2': 17,
'4.3.1': 18,
'4.4.2': 19,
'4.4W.2': 20,
'5.0.1': 21,
'5.1.1': 22,
'6.0': 23,
'7.0': 24,
'7.1.1': 25
}
def get_api_level(android_version):
"""
Get api level of android version.
:param android_version: android version
:type android_version: str
:return: api level
:rtype: int
"""
api_level = None
try:
for key in sorted(API_LEVELS):
if android_version in key:
api_level = API_LEVELS.get(key)
except TypeError as t_err:
logger.error(t_err)
return api_level
def install_package(emulator_file, api_level, sys_img):
"""
Install sdk package.
:param emulator_file: emulator file that need to be link
:type emulator_file: str
:param api_level: api level
:type api_level: str
:param sys_img: system image of emulator
:type sys_img: str
"""
# Link emulator shortcut
emu_file = os.path.join(ANDROID_PATH, 'tools', emulator_file)
emu_target = os.path.join(ANDROID_PATH, 'tools', 'emulator')
os.symlink(emu_file, emu_target)
# Install package based on given android version
cmd = 'echo y | android update sdk --no-ui -a -t android-{api},sys-img-{sys_img}-android-{api}'.format(
api=api_level, sys_img=sys_img)
logger.info('SDK package installation command: {install}'.format(install=cmd))
titel = 'SDK package installation process'
subprocess.check_call('xterm -T "{titel}" -n "{titel}" -e \"{cmd}\"'.format(titel=titel, cmd=cmd), shell=True)
def create_avd(device, avd_name, api_level):
"""
Create android virtual device.
:param device: name of device
:type device: str
:param avd_name: desire name
:type avd_name: str
:param api_level: api level
:type api_level: str
"""
# Create android emulator
cmd = 'echo no | android create avd -f -n {name} -t android-{api}'.format(name=avd_name, api=api_level)
if device != EMULATOR:
# Link emulator skins
from src import ROOT
skin_rsc_path = os.path.join(ROOT, 'devices', 'skins')
logger.info('Skin ressource path: {rsc}'.format(rsc=skin_rsc_path))
skin_dst_path = os.path.join(ANDROID_PATH, 'platforms', 'android-{api}'.format(api=api_level), 'skins')
logger.info('Skin destination path: {dst}'.format(dst=skin_dst_path))
for s in os.listdir(skin_rsc_path):
os.symlink(os.path.join(skin_rsc_path, s), os.path.join(skin_dst_path, s))
# Hardware and its skin
device_name_bash = device.replace(' ', '\ ')
skin_name = device.replace(' ', '_').lower()
logger.info('device name in bash: {db}, skin name: {skin}'.format(db=device_name_bash, skin=skin_name))
# For custom hardware profile
profile_dst_path = os.path.join(ROOT, '.android', 'devices.xml')
if 'samsung' in device.lower():
# profile file name = skin name
profile_src_path = os.path.join(ROOT, 'devices', 'profiles', '{profile}.xml'.format(profile=skin_name))
logger.info('Hardware profile resource path: {rsc}'.format(rsc=profile_src_path))
logger.info('Hardware profile destination path: {dst}'.format(dst=profile_dst_path))
os.symlink(profile_src_path, profile_dst_path)
# append command
cmd += ' -d {device} -s {skin}'.format(device=device_name_bash, skin=skin_name)
logger.info('AVD creation command: {cmd}'.format(cmd=cmd))
titel = 'AVD creation process'
subprocess.check_call('xterm -T "{titel}" -n "{titel}" -e \"{cmd}\"'.format(titel=titel, cmd=cmd), shell=True)

182
src/app.py Normal file
View file

@ -0,0 +1,182 @@
import json
import logging
import os
import subprocess
from src import CONFIG_FILE, ROOT
from src import log
log.init()
logger = logging.getLogger('app')
def get_or_raise(env):
"""
Check if needed environment variables are given.
:param env: key
:type env: str
:return: value
:rtype: str
"""
env_value = os.getenv(env)
if not env_value:
raise RuntimeError('The environment variable {0:s} is missing.'
'Please check docker image or Dockerfile!'.format(env))
return env_value
def str_to_bool(str):
"""
Convert string to boolean.
:param str: given string
:type str: str
:return: converted string
:rtype: bool
"""
try:
return str.lower() in ('yes', 'true', 't', '1')
except AttributeError as err:
logger.error(err)
ANDROID_HOME = get_or_raise('ANDROID_HOME')
ANDROID_VERSION = get_or_raise('ANDROID_VERSION')
API_LEVEL = get_or_raise('API_LEVEL')
PROCESSOR = get_or_raise('PROCESSOR')
SYS_IMG = get_or_raise('SYS_IMG')
logger.info('Android version: {version} \n'
'API level: {level} \n'
'Processor: {processor} \n'
'System image: {img}'.format(version=ANDROID_VERSION, level=API_LEVEL, processor=PROCESSOR,
img=SYS_IMG))
def prepare_avd(device, avd_name):
"""
Create and run android virtual device.
:param device: Device name
:type device: str
:param avd_name: Name of android virtual device / emulator
:type avd_name: str
"""
cmd = 'echo no | android create avd -f -n {name} -t android-{api} -b {sys_img}'.format(
name=avd_name, api=API_LEVEL, sys_img=SYS_IMG)
# Link emulator skins
skin_rsc_path = os.path.join(ROOT, 'devices', 'skins')
logger.info('Skin ressource path: {rsc}'.format(rsc=skin_rsc_path))
skin_dst_path = os.path.join(ANDROID_HOME, 'platforms', 'android-{api}'.format(api=API_LEVEL), 'skins')
logger.info('Skin destination path: {dst}'.format(dst=skin_dst_path))
for s in os.listdir(skin_rsc_path):
os.symlink(os.path.join(skin_rsc_path, s), os.path.join(skin_dst_path, s))
# Hardware and its skin
device_name_bash = device.replace(' ', '\ ')
skin_name = device.replace(' ', '_').lower()
logger.info('Device name in bash: {db}, Skin name: {skin}'.format(db=device_name_bash, skin=skin_name))
# For custom hardware profile
profile_dst_path = os.path.join(ROOT, '.android', 'devices.xml')
if 'samsung' in device.lower():
# profile file name = skin name
profile_src_path = os.path.join(ROOT, 'devices', 'profiles', '{profile}.xml'.format(profile=skin_name))
logger.info('Hardware profile resource path: {rsc}'.format(rsc=profile_src_path))
logger.info('Hardware profile destination path: {dst}'.format(dst=profile_dst_path))
os.symlink(profile_src_path, profile_dst_path)
# Append command
cmd += ' -d {device} -s {skin}'.format(device=device_name_bash, skin=skin_name)
logger.info('AVD creation command: {cmd}'.format(cmd=cmd))
titel = 'AVD creation process'
subprocess.check_call('xterm -T "{titel}" -n "{titel}" -e \"{cmd}\"'.format(titel=titel, cmd=cmd), shell=True)
def appium_run(avd_name):
"""
Run appium server.
:param avd_name: Name of android virtual device / emulator
:type avd_name: str
"""
cmd = 'appium'
grid_connect = str_to_bool(str(os.getenv('CONNECT_TO_GRID', False)))
logger.info('Connect to selenium grid? {connect}'.format(connect=grid_connect))
if grid_connect:
try:
appium_host = os.getenv('APPIUM_HOST', '127.0.0.1')
appium_port = int(os.getenv('APPIUM_PORT', 4723))
selenium_host = os.getenv('SELENIUM_HOST', '172.17.0.1')
selenium_port = int(os.getenv('SELENIUM_PORT', 4444))
create_node_config(avd_name, appium_host, appium_port, selenium_host, selenium_port)
cmd += ' --nodeconfig {file}'.format(file=CONFIG_FILE)
except ValueError as v_err:
logger.error(v_err)
titel = 'Appium Server'
subprocess.check_call('xterm -T "{titel}" -n "{titel}" -e \"{cmd}\"'.format(titel=titel, cmd=cmd), shell=True)
def create_node_config(avd_name, appium_host, appium_port, selenium_host, selenium_port):
"""
Create custom node config file in json format to be able to connect with selenium server.
:param avd_name: Name of android virtual device / emulator
:type avd_name: str
:param appium_host: Host where appium server is running
:type appium_host: str
:param appium_port: Port number where where appium server is running
:type appium_port: int
:param selenium_host: Host where selenium server is running
:type selenium_host: str
:param selenium_port: Port number where selenium server is running
:type selenium_port: int
"""
config = {
'capabilities': [
{
'platform': 'Android',
'platformName': 'Android',
'version': ANDROID_VERSION,
'browserName': avd_name,
'maxInstances': 1,
}
],
'configuration': {
'cleanUpCycle': 2000,
'timeout': 30000,
'proxy': 'org.openqa.grid.selenium.proxy.DefaultRemoteProxy',
'url': 'http://{host}:{port}/wd/hub'.format(host=appium_host, port=appium_port),
'host': appium_host,
'port': appium_port,
'maxSession': 6,
'register': True,
'registerCycle': 5000,
'hubHost': selenium_host,
'hubPort': selenium_port
}
}
logger.info('Appium node config: {config}'.format(config=config))
with open(CONFIG_FILE, 'w') as cf:
cf.write(json.dumps(config))
def run():
"""Run app."""
device = os.getenv('DEVICE', 'Nexus 5')
logger.info('Device: {device}'.format(device=device))
avd_name = '{device}_{version}'.format(device=device.replace(' ', '_').lower(), version=ANDROID_VERSION)
logger.info('AVD name: {avd}'.format(avd=avd_name))
prepare_avd(device, avd_name)
appium = str_to_bool(str(os.getenv('APPIUM', True)))
if appium:
appium_run(avd_name)
if __name__ == '__main__':
run()

View file

@ -1,84 +0,0 @@
import json
import logging
import os
import subprocess
logger = logging.getLogger('appium')
def run(connect_to_grid, avd_name, android_version):
"""
Run appium server.
:param connect_to_grid: option to connect with selenium grid
:type connect_to_grid: bool
:param avd_name: name of device
:type avd_name: str
:param android_version: android version
:type android_version: str
"""
cmd = 'appium'
if connect_to_grid:
from src import CONFIG_FILE
try:
appium_host = os.getenv('APPIUM_HOST', '127.0.0.1')
appium_port = int(os.getenv('APPIUM_PORT', 4723))
selenium_host = os.getenv('SELENIUM_HOST', '172.17.0.1')
selenium_port = int(os.getenv('SELENIUM_PORT', 4444))
create_node_config(CONFIG_FILE, avd_name, android_version, appium_host, appium_port,
selenium_host, selenium_port)
cmd += ' --nodeconfig {file}'.format(file=CONFIG_FILE)
except ValueError as v_err:
logger.error(v_err)
titel = 'avd name: {name}'.format(name=avd_name)
subprocess.check_call('xterm -T "{titel}" -n "{titel}" -e \"{cmd}\"'.format(titel=titel, cmd=cmd), shell=True)
def create_node_config(config_file, emulator_name, android_version, appium_host, appium_port,
selenium_host, selenium_port):
"""
Create custom node config file in json format to be able to connect with selenium server.
:param config_file: config file
:type config_file: str
:param emulator_name: emulator name
:type emulator_name: str
:param android_version: android version of android emulator
:type android_version: str
:param appium_host: host where appium server is running
:type appium_host: str
:param appium_port: port number where where appium server is running
:type appium_port: int
:param selenium_host: host where selenium server is running
:type selenium_host: str
:param selenium_port: port number where selenium server is running
:type selenium_port: int
"""
config = {
'capabilities': [
{
'platform': 'Android',
'platformName': 'Android',
'version': android_version,
'browserName': emulator_name,
'maxInstances': 1,
}
],
'configuration': {
'cleanUpCycle': 2000,
'timeout': 30000,
'proxy': 'org.openqa.grid.selenium.proxy.DefaultRemoteProxy',
'url': 'http://{host}:{port}/wd/hub'.format(host=appium_host, port=appium_port),
'host': appium_host,
'port': appium_port,
'maxSession': 6,
'register': True,
'registerCycle': 5000,
'hubHost': selenium_host,
'hubPort': selenium_port
}
}
logger.info('appium node config: {config}'.format(config=config))
with open(config_file, 'w') as cf:
cf.write(json.dumps(config))

View file

@ -1,5 +1,5 @@
[loggers] [loggers]
keys=root, android, appium, service keys=root, app
[handlers] [handlers]
keys=console keys=console
@ -11,23 +11,11 @@ keys=formatter
level=INFO level=INFO
handlers=console handlers=console
[logger_android] [logger_app]
level=INFO level=INFO
handlers=console handlers=console
propagate=0 propagate=0
qualname=android qualname=app
[logger_appium]
level=INFO
handlers=console
propagate=0
qualname=appium
[logger_service]
level=INFO
handlers=console
propagate=0
qualname=service
[handler_console] [handler_console]
class=StreamHandler class=StreamHandler

View file

@ -1,69 +0,0 @@
import logging
import os
from src import android, appium, log
logger = logging.getLogger('service')
def start():
"""
Installation of needed sdk package, creation of android emulator and execution of appium server.
"""
# Device name
device = os.getenv('DEVICE', 'Nexus 5')
# Android version
android_version = os.getenv('ANDROID_VERSION', '5.0')
logger.info('Android version: {version}'.format(version=android_version))
# Emulator type
emu_type = os.getenv('EMULATOR_TYPE', android.TYPE_ARMEABI).lower()
emu_type = android.TYPE_ARMEABI if emu_type not in [android.TYPE_ARMEABI, android.TYPE_X86] else emu_type
logger.info('Emulator type: {type}'.format(type=emu_type))
emu_file = 'emulator64-x86' if emu_type == android.TYPE_X86 else 'emulator64-arm'
logger.info('Emulator file: {file}'.format(file=emu_file))
# Selenium grid connection
connect_to_grid = str_to_bool(str(os.getenv('CONNECT_TO_GRID', False)))
logger.info('Connect to selenium grid? {input}'.format(input=connect_to_grid))
# Install android sdk package
api_level = android.get_api_level(android_version)
# Bug: cannot use skin for system image x86 with android version < 5.0
if emu_type == android.TYPE_X86:
if int(api_level) < android.get_api_level('5.0'):
sys_img = android.TYPE_X86
device = android.EMULATOR
else:
sys_img = android.TYPE_X86_64
else:
sys_img = '{type}-v7a'.format(type=android.TYPE_ARMEABI)
logger.info('System image: {sys_img}'.format(sys_img=sys_img))
android.install_package(emu_file, api_level, sys_img)
# Create android virtual device
logger.info('Device: {device}'.format(device=device))
avd_name = '{device}_{version}'.format(device=device.replace(' ', '_').lower(), version=android_version)
logger.info('AVD name: {avd}'.format(avd=avd_name))
android.create_avd(device, avd_name, api_level)
# Run appium server
appium.run(connect_to_grid, avd_name, android_version)
def str_to_bool(str):
"""
Convert string to boolean.
:param str: given string
:type str: str
:return: converted string
:rtype: bool
"""
return str.lower() in ('yes', 'true', 't', '1')
if __name__ == '__main__':
log.init()
start()

View file

@ -0,0 +1,49 @@
"""Unit test to test app."""
import os
from unittest import TestCase
import mock
from src import app
class TestApp(TestCase):
"""Unit test class to test other methods in the app."""
def test_valid_env(self):
key = 'ENV_1'
os.environ[key] = 'test'
app.get_or_raise(key)
del os.environ[key]
def test_invalid_env(self):
with self.assertRaises(RuntimeError):
app.get_or_raise('ENV_2')
def test_valid_bool(self):
self.assertEqual(app.str_to_bool('True'), True)
self.assertEqual(app.str_to_bool('t'), True)
self.assertEqual(app.str_to_bool('1'), True)
self.assertEqual(app.str_to_bool('YES'), True)
def test_invalid_bool(self):
self.assertEqual(app.str_to_bool(''), False)
self.assertEqual(app.str_to_bool('test'), False)
def test_invalid_format(self):
self.assertEqual(app.str_to_bool(True), None)
@mock.patch('src.app.prepare_avd')
def test_run_with_appium(self, mocked_avd):
with mock.patch('src.app.appium_run') as mocked_appium:
app.run()
self.assertTrue(mocked_avd.called)
self.assertTrue(mocked_appium.called)
@mock.patch('src.app.prepare_avd')
def test_run_withhout_appium(self, mocked_avd):
with mock.patch('src.app.appium_run') as mocked_appium:
os.environ['APPIUM'] = str(False)
app.run()
self.assertTrue(mocked_avd.called)
self.assertFalse(mocked_appium.called)

View file

@ -1,20 +0,0 @@
"""Unit test for android.py."""
from unittest import TestCase
from src import android
class TestApiLevel(TestCase):
"""Unit test class to test method get_api_level."""
def setUp(self):
self.android_version = '4.2.2'
def test_get_api_level(self):
api_level = android.get_api_level('4.2')
self.assertEqual(api_level, 19)
def test_wrong_type(self):
api_level = android.get_api_level(4)
self.assertRaises(TypeError)
self.assertEqual(api_level, None)

View file

@ -1,24 +0,0 @@
"""Unit test for android.py."""
from unittest import TestCase
import mock
from src import android
class TestInstallPackage(TestCase):
"""Unit test class to test method install_package."""
def setUp(self):
self.emulator_file = 'emulator64-arm'
self.api_level = 21
self.sys_img = 'armeabi-v7a'
@mock.patch('os.symlink')
@mock.patch('subprocess.check_call')
def test_package_installation(self, mocked_sys_link, mocked_suprocess):
self.assertFalse(mocked_sys_link.called)
self.assertFalse(mocked_suprocess.called)
android.install_package(self.emulator_file, self.api_level, self.sys_img)
self.assertTrue(mocked_sys_link.called)
self.assertTrue(mocked_suprocess.called)

View file

@ -1,16 +0,0 @@
"""Unit test for appium.py."""
import os
from unittest import TestCase
from src import CONFIG_FILE, appium
class TestAppiumConfig(TestCase):
"""Unit test class to test method create_node_config."""
def test_config_creation(self):
self.assertFalse(os.path.exists(CONFIG_FILE))
appium.create_node_config(CONFIG_FILE, 'emulator_name', '4.2.2', '127.0.0.1', 4723, '127.0.0.1', 4444)
self.assertTrue(os.path.exists(CONFIG_FILE))
os.remove(CONFIG_FILE)

View file

@ -1,46 +0,0 @@
"""Unit test for appium.py."""
import os
from unittest import TestCase
import mock
from src import appium
@mock.patch('subprocess.check_call')
class TestAppiumConfig(TestCase):
"""Unit test class to test method run."""
def setUp(self):
self.emulator_name = 'test'
self.android_version = '4.2.2'
def test_without_selenium_grid(self, mocked_subprocess):
with mock.patch('src.appium.create_node_config') as mocked_config:
self.assertFalse(mocked_config.called)
self.assertFalse(mocked_subprocess.called)
appium.run(False, self.emulator_name, self.android_version)
self.assertFalse(mocked_config.called)
self.assertTrue(mocked_subprocess.called)
def test_with_selenium_grid(self, mocked_subprocess):
with mock.patch('src.appium.create_node_config') as mocked_config:
self.assertFalse(mocked_config.called)
self.assertFalse(mocked_subprocess.called)
appium.run(True, self.emulator_name, self.android_version)
self.assertTrue(mocked_config.called)
self.assertTrue(mocked_subprocess.called)
def test_invalid_integer(self, mocked_subprocess):
os.environ['APPIUM_PORT'] = 'test'
with mock.patch('src.appium.create_node_config') as mocked_config:
self.assertFalse(mocked_config.called)
self.assertFalse(mocked_subprocess.called)
appium.run(True, self.emulator_name, self.android_version)
self.assertFalse(mocked_config.called)
self.assertTrue(mocked_subprocess.called)
self.assertRaises(ValueError)
def tearDown(self):
if os.getenv('APPIUM_PORT'):
del os.environ['APPIUM_PORT']

54
src/tests/test_appium.py Normal file
View file

@ -0,0 +1,54 @@
"""Unit test to test appium service."""
import os
from unittest import TestCase
import mock
from src import app
class TestAppium(TestCase):
"""Unit test class to test appium methods."""
def setUp(self):
os.environ['CONNECT_TO_GRID'] = str(True)
self.avd_name = 'test_avd'
@mock.patch('subprocess.check_call')
def test_without_selenium_grid(self, mocked_subprocess):
os.environ['CONNECT_TO_GRID'] = str(False)
self.assertFalse(mocked_subprocess.called)
app.appium_run(self.avd_name)
self.assertTrue(mocked_subprocess.called)
@mock.patch('subprocess.check_call')
def test_with_selenium_grid(self, mocked_subprocess):
with mock.patch('src.app.create_node_config') as mocked_config:
self.assertFalse(mocked_config.called)
self.assertFalse(mocked_subprocess.called)
app.appium_run(self.avd_name)
self.assertTrue(mocked_config.called)
self.assertTrue(mocked_subprocess.called)
@mock.patch('subprocess.check_call')
def test_invalid_integer(self, mocked_subprocess):
os.environ['APPIUM_PORT'] = 'test'
with mock.patch('src.app.create_node_config') as mocked_config:
self.assertFalse(mocked_config.called)
self.assertFalse(mocked_subprocess.called)
app.appium_run(self.avd_name)
self.assertFalse(mocked_config.called)
self.assertTrue(mocked_subprocess.called)
self.assertRaises(ValueError)
def test_config_creation(self):
from src import CONFIG_FILE
self.assertFalse(os.path.exists(CONFIG_FILE))
app.create_node_config('test', '127.0.0.1', 4723, '127.0.0.1', 4444)
self.assertTrue(os.path.exists(CONFIG_FILE))
os.remove(CONFIG_FILE)
def tearDown(self):
del os.environ['CONNECT_TO_GRID']
if os.getenv('APPIUM_PORT'):
del os.environ['APPIUM_PORT']

View file

@ -1,9 +1,10 @@
"""Unit test for android.py.""" """Unit test for android virtual device creation.py."""
import os
from unittest import TestCase from unittest import TestCase
import mock import mock
from src import android from src import app
@mock.patch('subprocess.check_call') @mock.patch('subprocess.check_call')
@ -13,15 +14,14 @@ class TestAvd(TestCase):
def setUp(self): def setUp(self):
self.avd_name = 'test_avd' self.avd_name = 'test_avd'
self.api_level = 21
def test_nexus_avd(self, mocked_suprocess, mocked_sys_link): def test_nexus_avd_as_default(self, mocked_suprocess, mocked_sys_link):
with mock.patch('os.listdir') as mocked_list_dir: with mock.patch('os.listdir') as mocked_list_dir:
mocked_list_dir.return_value = ['file1', 'file2'] mocked_list_dir.return_value = ['file1', 'file2']
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('Nexus 5', self.avd_name, self.api_level) app.prepare_avd('Nexus 5', self.avd_name)
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)
@ -32,16 +32,11 @@ 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('Samsung Galaxy S6', self.avd_name, self.api_level) app.prepare_avd('Samsung Galaxy S6', self.avd_name)
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)
def test_default_avd(self, mocked_suprocess, mocked_sys_link): def tearDown(self):
with mock.patch('os.listdir') as mocked_list_dir: if os.getenv('DEVICE'):
mocked_list_dir.return_value = ['file1', 'file2'] del os.environ['DEVICE']
self.assertFalse(mocked_list_dir.called)
self.assertFalse(mocked_sys_link.called)
self.assertFalse(mocked_suprocess.called)
android.create_avd('emulator', self.avd_name, self.api_level)
self.assertFalse(mocked_list_dir.called)

View file

@ -5,26 +5,26 @@ childlogdir=%(ENV_LOG_PATH)s
[program:xvfb] [program:xvfb]
command=/usr/bin/Xvfb %(ENV_DISPLAY)s -screen %(ENV_SCREEN)s %(ENV_SCREEN_WIDTH)sx%(ENV_SCREEN_HEIGHT)sx%(ENV_SCREEN_DEPTH)s command=/usr/bin/Xvfb %(ENV_DISPLAY)s -screen %(ENV_SCREEN)s %(ENV_SCREEN_WIDTH)sx%(ENV_SCREEN_HEIGHT)sx%(ENV_SCREEN_DEPTH)s
stdout_logfile=%(ENV_LOG_PATH)s/xvfb.stdout.log stdout_logfile=%(ENV_LOG_PATH)s/xvfb.log
stderr_logfile=%(ENV_LOG_PATH)s/xvfb.stderr.log redirect_stderr=true
[program:openbox] [program:openbox]
command=/usr/bin/openbox-session command=/usr/bin/openbox-session
stdout_logfile=%(ENV_LOG_PATH)s/openbox.stdout.log stdout_logfile=%(ENV_LOG_PATH)s/openbox.log
stderr_logfile=%(ENV_LOG_PATH)s/openbox.stderr.log redirect_stderr=true
[program:x11vnc] [program:x11vnc]
command=/usr/bin/x11vnc -display %(ENV_DISPLAY)s -nopw -ncache 10 -forever command=/usr/bin/x11vnc -display %(ENV_DISPLAY)s -nopw -ncache 10 -forever
stdout_logfile=%(ENV_LOG_PATH)s/x11vnc.stdout.log stdout_logfile=%(ENV_LOG_PATH)s/x11vnc.log
stderr_logfile=%(ENV_LOG_PATH)s/x11vnc.stderr.log redirect_stderr=true
[program:novnc] [program:novnc]
command=./noVNC/utils/launch.sh --vnc localhost:%(ENV_LOCAL_PORT)s --listen %(ENV_TARGET_PORT)s command=./noVNC/utils/launch.sh --vnc localhost:%(ENV_LOCAL_PORT)s --listen %(ENV_TARGET_PORT)s
stdout_logfile=%(ENV_LOG_PATH)s/novnc.stdout.log stdout_logfile=%(ENV_LOG_PATH)s/novnc.log
stderr_logfile=%(ENV_LOG_PATH)s/novnc.stderr.log redirect_stderr=true
[program:docker-appium] [program:docker-appium]
command=python -m src.service command=python -m src.app
autorestart=false autorestart=false
stdout_logfile=%(ENV_LOG_PATH)s/docker-appium.stdout.log stdout_logfile=%(ENV_LOG_PATH)s/docker-appium.log
stderr_logfile=%(ENV_LOG_PATH)s/docker-appium.stderr.log redirect_stderr=true