diff --git a/Dockerfile b/Dockerfile index 5bf853f..08910a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,8 +46,8 @@ ENV PATH="${PATH}:${JAVA_HOME}/bin" #===================== RUN apt-get install wget unzip libqt5webkit5 -y -RUN wget https://dl.google.com/android/repository/tools_r25.2.3-linux.zip -RUN unzip tools_r25.2.3-linux.zip && rm tools_r25.2.3-linux.zip +RUN wget https://dl.google.com/android/repository/tools_r25.2.3-linux.zip && \ + unzip tools_r25.2.3-linux.zip && rm tools_r25.2.3-linux.zip ENV ANDROID_HOME="/root" ENV PATH="${PATH}:${ANDROID_HOME}/tools" @@ -55,9 +55,8 @@ 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" +RUN echo y | android update sdk --no-ui --filter platform-tools,build-tools-25.0.2 +ENV PATH="${PATH}:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/build-tools" RUN mv ${ANDROID_HOME}/tools/emulator ${ANDROID_HOME}/tools/emulator.backup #==================================== diff --git a/src/__init__.py b/src/__init__.py index c4ddef8..6d2f72b 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,6 +1,7 @@ import os ROOT = '/root' +ANDROID_PATH = os.getenv('ANDROID_HOME', '/root') WORKDIR = os.path.dirname(__file__) CONFIG_FILE = os.path.join(WORKDIR, 'nodeconfig.json') LOGGING_FILE = os.path.join(WORKDIR, 'logging.conf') diff --git a/src/android.py b/src/android.py index d0191d0..c1667b5 100644 --- a/src/android.py +++ b/src/android.py @@ -1,44 +1,37 @@ import logging import os -import re import subprocess -logging.basicConfig() +from src import ANDROID_PATH + logger = logging.getLogger('android') -# not using enum because need to install pip that will make docker image size bigger +EMULATOR = 'emulator' TYPE_ARMEABI = 'armeabi' TYPE_X86 = 'x86' TYPE_X86_64 = 'x86_64' -API_LEVEL_ANDROID_5 = 21 - - -def get_available_sdk_packages(): - """ - Get list of available sdk packages. - - :return: List of available packages. - :rtype: bytearray - """ - logger.info('List of Android SDK: ') - output_str = subprocess.check_output('android list sdk'.split()) - 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 # Get the last item that match with keyword - return pos +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): @@ -47,38 +40,25 @@ def get_api_level(android_version): :param android_version: android version :type android_version: str - :return: api version - :rtype: str + :return: api level + :rtype: int """ - api_version = None + api_level = None try: - packages = get_available_sdk_packages() + 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) - if packages: - item_pos = get_item_position(android_version, packages) - logger.info('Package in position: {pos}'.format(pos=item_pos)) - item = packages[item_pos] - logger.info('Item: {item}'.format(item=item)) - - item_info = item.split('-') - api_version = re.search('%s(.*)%s' % ('API', ','), item_info[1]).group(1).strip() - logger.info('API level: {api}'.format(api=api_version)) - else: - raise RuntimeError('List of packages is empty!') - - except IndexError as i_err: - logger.error(i_err) - - return api_version + return api_level -def install_package(android_path, emulator_file, api_level, sys_img): +def install_package(emulator_file, api_level, sys_img): """ Install sdk package. - :param android_path: location where android SDK is installed - :type android_path: str :param emulator_file: emulator file that need to be link :type emulator_file: str :param api_level: api level @@ -87,8 +67,8 @@ def install_package(android_path, emulator_file, api_level, sys_img): :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') + 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 @@ -96,16 +76,13 @@ def install_package(android_path, emulator_file, api_level, sys_img): 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) + subprocess.check_call('xterm -T "{titel}" -n "{titel}" -e \"{cmd}\"'.format(titel=titel, cmd=cmd), shell=True) -def create_avd(android_path, device, avd_name, api_level): +def create_avd(device, avd_name, api_level): """ Create android virtual device. - :param android_path: location where android SDK is installed - :type android_path: str :param device: name of device :type device: str :param avd_name: desire name @@ -115,13 +92,13 @@ def create_avd(android_path, device, avd_name, api_level): """ # 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': + 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') + 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): @@ -146,5 +123,4 @@ def create_avd(android_path, device, avd_name, api_level): 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) + subprocess.check_call('xterm -T "{titel}" -n "{titel}" -e \"{cmd}\"'.format(titel=titel, cmd=cmd), shell=True) diff --git a/src/appium.py b/src/appium.py index b55b007..1ee9bbe 100644 --- a/src/appium.py +++ b/src/appium.py @@ -6,14 +6,14 @@ import subprocess logger = logging.getLogger('appium') -def run(connect_to_grid, emulator_name, android_version): +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 emulator_name: name of emulator - :type emulator_name: str + :param avd_name: name of device + :type avd_name: str :param android_version: android version :type android_version: str """ @@ -25,14 +25,13 @@ def run(connect_to_grid, emulator_name, android_version): 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, emulator_name, android_version, - appium_host, appium_port, selenium_host, selenium_port) + 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=emulator_name) - subprocess.check_call('xterm -T "{titel}" -n "{titel}" -e \"{cmd}\"'.format( - titel=titel, cmd=cmd), shell=True) + 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, diff --git a/src/service.py b/src/service.py index 846f5c7..b5f220d 100644 --- a/src/service.py +++ b/src/service.py @@ -11,25 +11,20 @@ def start(): Installation of needed sdk package, creation of android emulator and execution of appium server. """ - # Android SDK path - android_path = os.getenv('ANDROID_HOME', '/root') - logger.info('Android path: {path}'.format(path=android_path)) - - # Emulator informations - 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)) - # Device name device = os.getenv('DEVICE', 'Nexus 5') # Android version - android_version = os.getenv('ANDROID_VERSION', '4.2.2') + 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)) @@ -38,21 +33,21 @@ def start(): 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.API_LEVEL_ANDROID_5: + if int(api_level) < android.get_api_level('5.0'): sys_img = android.TYPE_X86 - device = 'emulator' + 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(android_path, emu_file, api_level, 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(android_path, device, avd_name, api_level) + android.create_avd(device, avd_name, api_level) # Run appium server appium.run(connect_to_grid, avd_name, android_version) diff --git a/src/tests/android/test_api_level.py b/src/tests/android/test_api_level.py index 568f306..462af49 100644 --- a/src/tests/android/test_api_level.py +++ b/src/tests/android/test_api_level.py @@ -1,33 +1,20 @@ """Unit test for android.py.""" from unittest import TestCase -import mock - from src import android -@mock.patch('src.android.get_available_sdk_packages') 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, 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'] - api_level = android.get_api_level(self.android_version) - self.assertEqual(api_level, '17') + def test_get_api_level(self): + api_level = android.get_api_level('4.2') + self.assertEqual(api_level, 19) - def test_empty_packages(self, mocked_packages): - mocked_packages.return_value = None - with self.assertRaises(RuntimeError): - android.get_api_level(self.android_version) - - 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.get_api_level(self.android_version) - self.assertRaises(IndexError) + def test_wrong_type(self): + api_level = android.get_api_level(4) + self.assertRaises(TypeError) + self.assertEqual(api_level, None) diff --git a/src/tests/android/test_available_packages.py b/src/tests/android/test_available_packages.py deleted file mode 100644 index 704ef8c..0000000 --- a/src/tests/android/test_available_packages.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Unit test for android.py.""" -from unittest import TestCase - -import mock - -from src import android - - -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 = android.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 = android.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 = android.get_available_sdk_packages() - self.assertEqual(None, output) diff --git a/src/tests/android/test_create_avd.py b/src/tests/android/test_create_avd.py index ad584d5..bd2d380 100644 --- a/src/tests/android/test_create_avd.py +++ b/src/tests/android/test_create_avd.py @@ -12,7 +12,6 @@ class TestAvd(TestCase): """Unit test class to test method create_avd.""" def setUp(self): - self.android_path = '/root' self.avd_name = 'test_avd' self.api_level = 21 @@ -22,7 +21,7 @@ class TestAvd(TestCase): self.assertFalse(mocked_list_dir.called) self.assertFalse(mocked_sys_link.called) self.assertFalse(mocked_suprocess.called) - android.create_avd(self.android_path, 'Nexus 5', self.avd_name, self.api_level) + android.create_avd('Nexus 5', self.avd_name, self.api_level) self.assertTrue(mocked_list_dir.called) self.assertTrue(mocked_sys_link.called) self.assertTrue(mocked_suprocess.called) @@ -33,7 +32,7 @@ class TestAvd(TestCase): self.assertFalse(mocked_list_dir.called) self.assertFalse(mocked_sys_link.called) self.assertFalse(mocked_suprocess.called) - android.create_avd(self.android_path, 'Samsung Galaxy S6', self.avd_name, self.api_level) + android.create_avd('Samsung Galaxy S6', self.avd_name, self.api_level) self.assertTrue(mocked_list_dir.called) self.assertTrue(mocked_sys_link.called) self.assertTrue(mocked_suprocess.called) @@ -44,5 +43,5 @@ class TestAvd(TestCase): self.assertFalse(mocked_list_dir.called) self.assertFalse(mocked_sys_link.called) self.assertFalse(mocked_suprocess.called) - android.create_avd(self.android_path, 'emulator', self.avd_name, self.api_level) + android.create_avd('emulator', self.avd_name, self.api_level) self.assertFalse(mocked_list_dir.called) diff --git a/src/tests/android/test_install_package.py b/src/tests/android/test_install_package.py index 9ded4cb..77d74c2 100644 --- a/src/tests/android/test_install_package.py +++ b/src/tests/android/test_install_package.py @@ -10,7 +10,6 @@ class TestInstallPackage(TestCase): """Unit test class to test method install_package.""" def setUp(self): - self.android_path = '/root' self.emulator_file = 'emulator64-arm' self.api_level = 21 self.sys_img = 'armeabi-v7a' @@ -20,6 +19,6 @@ class TestInstallPackage(TestCase): 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.android_path, self.emulator_file, self.api_level, self.sys_img) + android.install_package(self.emulator_file, self.api_level, self.sys_img) self.assertTrue(mocked_sys_link.called) self.assertTrue(mocked_suprocess.called) diff --git a/src/tests/android/test_item_position.py b/src/tests/android/test_item_position.py deleted file mode 100644 index 9dac508..0000000 --- a/src/tests/android/test_item_position.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Unit test for android.py.""" -from unittest import TestCase - -from src import android - - -class TestItemPosition(TestCase): - """Unit test class to test method get_item_position.""" - - def setUp(self): - self.items = ['android 4.1', 'android 4.2.2', 'android 4.3', 'android 4.4', 'android 4.4.2'] - - def test_valid_params(self): - keyword = '4.2' - output = android.get_item_position(keyword, self.items) - self.assertEqual(4, output) - - def test_invalid_keyword(self): - keyword = 'fake' - output = android.get_item_position(keyword, self.items) - self.assertEqual(0, output) - - def test_empty_array(self): - items = [] - keyword = '4.2' - output = android.get_item_position(keyword, items) - self.assertEqual(0, output)