Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
budtmo committed Dec 22, 2016
0 parents commit a5e0096
Show file tree
Hide file tree
Showing 18 changed files with 419 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
@@ -0,0 +1,9 @@
.idea/*
.DS_Store
*.pyc

# Coverage
.coverage
coverage.xml
xunit.xml
coverage/*
10 changes: 10 additions & 0 deletions .travis.yml
@@ -0,0 +1,10 @@
language: python
python:
- "2.7"
install: "pip install -r requirements.txt"
script: nosetests
branches:
only:
- master
after_success:
- bash <(curl -s https://codecov.io/bash)
53 changes: 53 additions & 0 deletions Dockerfile
@@ -0,0 +1,53 @@
FROM ubuntu:16.04

#=======================
# General Configuration
#=======================
RUN apt-get update && apt-get upgrade -y
RUN apt-get install wget -y

#==============
# Install Java
#==============
RUN apt-get install openjdk-8-jdk -y
ENV JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64/jre"
ENV PATH="${PATH}:${JAVA_HOME}/bin"

#=====================
# Install Android SDK
#=====================
RUN wget http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz
RUN tar -xvzf android-sdk_r24.4.1-linux.tgz
ENV ANDROID_HOME="/android-sdk-linux"
ENV PATH="${PATH}:${ANDROID_HOME}/tools"

#=====================================================
# Install Platform-tools, Build-tools
# To see list of available packages: android list sdk
#=====================================================
RUN echo y | android update sdk --no-ui --filter 2,3
ENV PATH="${PATH}:${ANDROID_HOME}/platform-tools"
ENV PATH="${PATH}:${ANDROID_HOME}/build-tools"

#==================================================
# Fix issue regarding 64bit while running emulator
#==================================================
RUN dpkg --add-architecture i386
RUN apt-get update
RUN apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 -y
ENV ANDROID_EMULATOR_FORCE_32BIT=true
RUN adb start-server

#============================================
# Install nodejs, npm, appium, appium-doctor
#============================================
RUN apt-get install npm nodejs-legacy -y
ENV APPIUM_VERSION 1.6.3
RUN npm install -g appium@$APPIUM_VERSION

#===================
# Run docker-appium
#===================
COPY service /service
WORKDIR /service
CMD python start.py
13 changes: 13 additions & 0 deletions LICENSE
@@ -0,0 +1,13 @@
Copyright 2016 budi utomo

Licensed 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.
1 change: 1 addition & 0 deletions MAINTAINERS
@@ -0,0 +1 @@
Budi Utomo <budi.ut.1989@gmail.com>
45 changes: 45 additions & 0 deletions README.md
@@ -0,0 +1,45 @@
Docker-Android-Appium
=====================

Android emulator and Appium server in docker solution.

Requirements
------------

Docker is installed in your system.

Quick Start
-----------

1. Run docker-appium with command:

```bash
docker run -d -p 4723:4723 -v <apk_path_that_will_be_tested>:/target_apk -e ANDROID_VERSION=<target_android_version> --name appium-container butomo1989/docker-appium
```

***Note: There is an example apk in folder example.***

An Example:

```bash
docker run -d -p 4723:4723 -v $PWD/example/sample_apk:/target_apk -e ANDROID_VERSION=4.2.2 --name appium-container docker-android-appium
```

2. See the docker logs with command:

```bash
docker logs appium-container -f
```

3. Wait until you see this following example messages in logs that showing that appium server is ready to use:

```bash
INFO:android_appium:Android emulator is created
INFO:android_appium:android emulator name: emulator_4.2.2
[Appium] Welcome to Appium v1.6.3
[Appium] Appium REST http interface listener started on 0.0.0.0:4723
```

4. Run your UI tests by using docker-appium.

***Note: There is an example UITests in folder example.***
14 changes: 14 additions & 0 deletions example/android/python/README.md
@@ -0,0 +1,14 @@
Docker-Android-Appium Sample
----------------------------

Example UI test to use docker-appium.

Requirements
============
1. docker-appium
2. python

Quick Start
===========
1. pip install -r requirements
2. python android_simple.py
34 changes: 34 additions & 0 deletions example/android/python/app_simple.py
@@ -0,0 +1,34 @@
import unittest

from appium import webdriver


class SimpleAndroidUITests(unittest.TestCase):

def setUp(self):
desired_caps = {
'platformName': 'Android',
'deviceName': 'Android Emulator',
'platformVersion': '4.2',
'app': '/target_apk/sample_apk_debug.apk',
'avd': 'emulator_4.2.2'
}
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

def tearDown(self):
self.driver.quit()

def test_calculation(self):
text_fields = self.driver.find_elements_by_class_name('android.widget.EditText')
text_fields[0].send_keys(4)
text_fields[1].send_keys(6)

btn_calculate = self.driver.find_element_by_class_name('android.widget.Button')
btn_calculate.click()

self.assertEqual('10', text_fields[2].text)


if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(SimpleAndroidUITests)
unittest.TextTestRunner(verbosity=2).run(suite)
1 change: 1 addition & 0 deletions example/android/python/requirements.txt
@@ -0,0 +1 @@
Appium-Python-Client==0.23
Binary file added example/sample_apk/sample_apk_debug.apk
Binary file not shown.
4 changes: 4 additions & 0 deletions requirements.txt
@@ -0,0 +1,4 @@
autopep8==1.2.4
coverage==4.2
mock==2.0.0
nose==1.3.7
Empty file added service/__init__.py
Empty file.
110 changes: 110 additions & 0 deletions service/start.py
@@ -0,0 +1,110 @@
import logging
import os
import re
import subprocess

logging.basicConfig()
logger = logging.getLogger('android_appium')


def run():
"""
Run Android emulator and Appium server.
"""
android_version = os.getenv('ANDROID_VERSION', '4.2.2')
create_android_emulator(android_version)

emulator_name = 'emulator_{version}'.format(version=android_version)

logger.info('android emulator name: {name} '.format(name=emulator_name))
# TODO: check android emulator is ready to use
cmd_run = 'emulator -avd {name} -no-audio -no-window & appium'.format(name=emulator_name)
subprocess.check_call(cmd_run, shell=True)


def get_available_sdk_packages():
"""
Get list of available sdk packages.
:return: List of available packages.
:rtype: bytearray
"""
logger.info('List of Android SDK: ')
cmd = ['android', 'list', 'sdk']

output_str = subprocess.check_output(cmd)
logger.info(output_str)

return [output.strip() for output in output_str.split('\n')] if output_str else None


def get_item_position(keyword, items):
"""
Get position of item in array by given keyword.
:return: Item position.
:rtype: int
"""
pos = 0
for p, v in enumerate(items):
if keyword in v:
pos = p
break # Get the first item that match with keyword
return pos


def create_android_emulator(android_version):
"""
Create android emulator based on given android version.
It include installation of sdk package and its armeabi v7a.
To see list of available targets: android list targets
To see list to avd: android list avd
:param android_version: android version
:type android_version: str
"""
try:
packages = get_available_sdk_packages()

if packages:
item_pos = get_item_position(android_version, packages)
logger.info('item position: {pos}'.format(pos=item_pos))
item = packages[item_pos]

item_info = item.split('-')
package_number = item_info[0]
api_version = re.search('%s(.*)%s' % ('API', ','), item_info[1]).group(1).strip()
logger.info(
'Package number: {number}, API version: {version}'.format(number=package_number, version=api_version))

# Install SDK package
logger.info('Installing SDK package...')
cmd_sdk = 'echo y | android update sdk --no-ui --filter {number}'.format(number=package_number)
subprocess.check_call(cmd_sdk, shell=True)
logger.info('Installation completed')

# Install armeabi v7a
logger.info('Installing its armeabi...')
cmd_arm = 'echo y | android update sdk --no-ui -a --filter sys-img-armeabi-v7a-android-{api}'.format(
api=api_version)
subprocess.check_call(cmd_arm, shell=True)
logger.info('Installation completed')

# Create android emulator
logger.info('Creating android emulator...')
cmd_emu = 'echo no | android create avd -f -n emulator_{version} -t android-{api} --abi armeabi-v7a'.format(
version=android_version, api=api_version)
subprocess.check_call(cmd_emu, shell=True)
logger.info('Android emulator is created')
else:
raise RuntimeError('Packages is empty!')

except IndexError as i_err:
logger.error(i_err)


if __name__ == '__main__':
logger.setLevel(logging.INFO)
run()
19 changes: 19 additions & 0 deletions service/tests/__init__.py
@@ -0,0 +1,19 @@
"""Unit test for start.py."""
from unittest import TestCase

import mock

from service import start


class TestService(TestCase):
"""Unit test class to test method run."""

@mock.patch('service.start.create_android_emulator')
@mock.patch('subprocess.check_call')
def test_service(self, mocked_creation, mocked_subprocess):
self.assertFalse(mocked_creation.called)
self.assertFalse(mocked_subprocess.called)
start.run()
self.assertTrue(mocked_creation.called)
self.assertTrue(mocked_subprocess.called)
34 changes: 34 additions & 0 deletions service/tests/test_android_emulator.py
@@ -0,0 +1,34 @@
"""Unit test for start.py."""
from unittest import TestCase

import mock

from service import start


@mock.patch('service.start.get_available_sdk_packages')
class TestRunService(TestCase):
"""Unit test class to test method create_android_emulator."""

def test_create_emulator(self, mocked_packages):
mocked_packages.return_value = ['9- SDK Platform Android 4.4.2, API 19, revision 4',
'10- SDK Platform Android 4.3.1, API 18, revision 3',
'11- SDK Platform Android 4.2.2, API 17, revision 3']
with mock.patch('subprocess.check_call') as mocked_subprocess:
self.assertFalse(mocked_subprocess.called)
android_version = '4.2.2'
start.create_android_emulator(android_version)
self.assertTrue(mocked_subprocess.called)

def test_empty_packages(self, mocked_packages):
mocked_packages.return_value = None
with self.assertRaises(RuntimeError):
start.create_android_emulator('4.2.2')

def test_index_error(self, mocked_packages):
mocked_packages.return_value = ['9 SDK Platform Android 4.4.2, API 19, revision 4',
'10 SDK Platform Android 4.3.1, API 18, revision 3',
'11 SDK Platform Android 4.2.2, API 17, revision 3']
android_version = '4.2.2'
start.create_android_emulator(android_version)
self.assertRaises(IndexError)
28 changes: 28 additions & 0 deletions service/tests/test_available_packages.py
@@ -0,0 +1,28 @@
"""Unit test for start.py."""
from unittest import TestCase

import mock

from service import start


class TestAvailablePackages(TestCase):
"""Unit test class to test method get_available_sdk_packages."""

@mock.patch('subprocess.check_output')
def test_valid_output(self, mocked_output):
mocked_output.return_value = 'package 1 \n package 2'
output = start.get_available_sdk_packages()
self.assertEqual(['package 1', 'package 2'], output)

@mock.patch('subprocess.check_output')
def test_without_line_break(self, mocked_output):
mocked_output.return_value = 'package 1, package 2'
output = start.get_available_sdk_packages()
self.assertEqual(['package 1, package 2'], output)

@mock.patch('subprocess.check_output')
def test_empty_string(self, mocked_output):
mocked_output.return_value = None
output = start.get_available_sdk_packages()
self.assertEqual(None, output)

0 comments on commit a5e0096

Please sign in to comment.