commit
34fa736916
|
@ -51,7 +51,7 @@ If you want to backup/reuse the avds created with furture upgrades or for replic
|
||||||
- -v local_backup/android_emulator:/root/android_emulator
|
- -v local_backup/android_emulator:/root/android_emulator
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --privileged -d -p 6080:6080 -p 4723:4723 -p 5554:5554 -p 5555:5555 -v local_backup/.android:/root/.android -v local_backup/android_emulator:local_backup/android_emulator -e DEVICE="Nexus 5" --name android-container budtmo/docker-android-x86-8.1
|
docker run --privileged -d -p 6080:6080 -p 4723:4723 -p 5554:5554 -p 5555:5555 -v local_backup/.android:/root/.android -v local_backup/android_emulator:/root/android_emulator -e DEVICE="Nexus 5" --name android-container budtmo/docker-android-x86-8.1
|
||||||
```
|
```
|
||||||
|
|
||||||
For the first run, this will create a new avd and all the changes will be accessible in the `local_backup` directory. Now for all future runs, it will reuse the avds. Even this should work with new releases of `docker-android`
|
For the first run, this will create a new avd and all the changes will be accessible in the `local_backup` directory. Now for all future runs, it will reuse the avds. Even this should work with new releases of `docker-android`
|
||||||
|
|
34
src/app.py
34
src/app.py
|
@ -50,8 +50,22 @@ def convert_str_to_bool(str: str) -> bool:
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
|
|
||||||
|
|
||||||
def is_initialized() -> bool:
|
def is_initialized(device_name) -> bool:
|
||||||
return os.path.exists(os.path.join(ROOT, '.android', 'devices.xml'))
|
config_path = os.path.join(ROOT, 'android_emulator', 'config.ini')
|
||||||
|
|
||||||
|
if os.path.exists(config_path):
|
||||||
|
logger.info('Found existing config file at {}.'.format(config_path))
|
||||||
|
with open(config_path, 'r') as f:
|
||||||
|
if any('hw.device.name={}'.format(device_name) in line for line in f):
|
||||||
|
logger.info('Existing config file references {}. Assuming device was previously initialized.'.format(device_name))
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.info('Existing config file does not reference {}. Assuming new device.'.format(device_name))
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info('No config file file was found at {}. Assuming new device.'.format(config_path))
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
ANDROID_HOME = get_or_raise('ANDROID_HOME')
|
ANDROID_HOME = get_or_raise('ANDROID_HOME')
|
||||||
ANDROID_VERSION = get_or_raise('ANDROID_VERSION')
|
ANDROID_VERSION = get_or_raise('ANDROID_VERSION')
|
||||||
|
@ -68,7 +82,7 @@ logger.info('Android version: {version} \n'
|
||||||
img=SYS_IMG, img_type=IMG_TYPE))
|
img=SYS_IMG, img_type=IMG_TYPE))
|
||||||
|
|
||||||
|
|
||||||
def prepare_avd(device: str, avd_name: str):
|
def prepare_avd(device: str, avd_name: str, dp_size: str):
|
||||||
"""
|
"""
|
||||||
Create and run android virtual device.
|
Create and run android virtual device.
|
||||||
|
|
||||||
|
@ -101,6 +115,8 @@ def prepare_avd(device: str, avd_name: str):
|
||||||
config_path = '/'.join([avd_path, 'config.ini'])
|
config_path = '/'.join([avd_path, 'config.ini'])
|
||||||
with open(config_path, 'a') as file:
|
with open(config_path, 'a') as file:
|
||||||
file.write('skin.path={sp}'.format(sp=skin_path))
|
file.write('skin.path={sp}'.format(sp=skin_path))
|
||||||
|
file.write('\ndisk.dataPartition.size={dp}'.format(dp=dp_size))
|
||||||
|
|
||||||
logger.info('Skin was added in config.ini')
|
logger.info('Skin was added in config.ini')
|
||||||
|
|
||||||
|
|
||||||
|
@ -192,21 +208,23 @@ def run():
|
||||||
|
|
||||||
avd_name = '{device}_{version}'.format(device=device.replace(' ', '_').lower(), version=ANDROID_VERSION)
|
avd_name = '{device}_{version}'.format(device=device.replace(' ', '_').lower(), version=ANDROID_VERSION)
|
||||||
logger.info('AVD name: {avd}'.format(avd=avd_name))
|
logger.info('AVD name: {avd}'.format(avd=avd_name))
|
||||||
is_first_run = not is_initialized()
|
is_first_run = not is_initialized(device)
|
||||||
|
|
||||||
|
dp_size = os.getenv('DATAPARTITION', '550m')
|
||||||
|
|
||||||
if is_first_run:
|
if is_first_run:
|
||||||
logger.info('Preparing emulator...')
|
logger.info('Preparing emulator...')
|
||||||
prepare_avd(device, avd_name)
|
prepare_avd(device, avd_name, dp_size)
|
||||||
|
|
||||||
logger.info('Run emulator...')
|
logger.info('Run emulator...')
|
||||||
dp_size = os.getenv('DATAPARTITION', '550m')
|
|
||||||
with open("/root/android_emulator/config.ini", "a") as cfg:
|
|
||||||
cfg.write('\ndisk.dataPartition.size={dp}'.format(dp=dp_size))
|
|
||||||
|
|
||||||
if is_first_run:
|
if is_first_run:
|
||||||
|
logger.info('Emulator was not previously initialized. Preparing a new one...')
|
||||||
cmd = 'emulator/emulator @{name} -gpu swiftshader_indirect -accel on -wipe-data -writable-system -verbose {custom_args}'.format(name=avd_name, custom_args=custom_args)
|
cmd = 'emulator/emulator @{name} -gpu swiftshader_indirect -accel on -wipe-data -writable-system -verbose {custom_args}'.format(name=avd_name, custom_args=custom_args)
|
||||||
else:
|
else:
|
||||||
|
logger.info('Using previously initialized AVD...')
|
||||||
cmd = 'emulator/emulator @{name} -gpu swiftshader_indirect -accel on -verbose -writable-system {custom_args}'.format(name=avd_name, custom_args=custom_args)
|
cmd = 'emulator/emulator @{name} -gpu swiftshader_indirect -accel on -verbose -writable-system {custom_args}'.format(name=avd_name, custom_args=custom_args)
|
||||||
|
|
||||||
appium = convert_str_to_bool(str(os.getenv('APPIUM', False)))
|
appium = convert_str_to_bool(str(os.getenv('APPIUM', False)))
|
||||||
if appium:
|
if appium:
|
||||||
subprocess.Popen(cmd.split())
|
subprocess.Popen(cmd.split())
|
||||||
|
|
|
@ -7,6 +7,13 @@ import mock
|
||||||
from src import app
|
from src import app
|
||||||
|
|
||||||
|
|
||||||
|
# https://bugs.python.org/issue21258
|
||||||
|
def mock_open(*args, **kargs):
|
||||||
|
f_open = mock.mock_open(*args, **kargs)
|
||||||
|
f_open.return_value.__iter__ = lambda self : iter(self.readline, '')
|
||||||
|
return f_open
|
||||||
|
|
||||||
|
|
||||||
class TestApp(TestCase):
|
class TestApp(TestCase):
|
||||||
"""Unit test class to test other methods in the app."""
|
"""Unit test class to test other methods in the app."""
|
||||||
|
|
||||||
|
@ -60,7 +67,6 @@ class TestApp(TestCase):
|
||||||
os.environ['APPIUM'] = str(True)
|
os.environ['APPIUM'] = str(True)
|
||||||
app.run()
|
app.run()
|
||||||
self.assertTrue(mocked_avd.called)
|
self.assertTrue(mocked_avd.called)
|
||||||
self.assertTrue(mocked_open.called)
|
|
||||||
self.assertTrue(mocked_subprocess.called)
|
self.assertTrue(mocked_subprocess.called)
|
||||||
self.assertTrue(mocked_appium.called)
|
self.assertTrue(mocked_appium.called)
|
||||||
|
|
||||||
|
@ -73,7 +79,6 @@ class TestApp(TestCase):
|
||||||
os.environ['RELAXED_SECURITY'] = str(True)
|
os.environ['RELAXED_SECURITY'] = str(True)
|
||||||
app.run()
|
app.run()
|
||||||
self.assertTrue(mocked_avd.called)
|
self.assertTrue(mocked_avd.called)
|
||||||
self.assertTrue(mocked_open.called)
|
|
||||||
self.assertTrue(mocked_subprocess.called)
|
self.assertTrue(mocked_subprocess.called)
|
||||||
self.assertTrue(mocked_appium.called)
|
self.assertTrue(mocked_appium.called)
|
||||||
|
|
||||||
|
@ -85,24 +90,32 @@ class TestApp(TestCase):
|
||||||
os.environ['APPIUM'] = str(False)
|
os.environ['APPIUM'] = str(False)
|
||||||
app.run()
|
app.run()
|
||||||
self.assertTrue(mocked_avd.called)
|
self.assertTrue(mocked_avd.called)
|
||||||
self.assertTrue(mocked_open.called)
|
|
||||||
self.assertTrue(mocked_subprocess.called)
|
self.assertTrue(mocked_subprocess.called)
|
||||||
self.assertFalse(mocked_appium.called)
|
self.assertFalse(mocked_appium.called)
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
@mock.patch('builtins.open')
|
||||||
@mock.patch('subprocess.Popen')
|
@mock.patch('subprocess.Popen')
|
||||||
@mock.patch('os.path.exists', mock.MagicMock(return_value=False))
|
@mock.patch('os.path.exists', mock.MagicMock(return_value=False))
|
||||||
def test_run_first_run(self, mocked_open, mocked_subprocess):
|
def test_it_prepares_avd_on_first_run(self, mocked_open, mocked_subprocess):
|
||||||
with mock.patch('src.app.prepare_avd') as mocked_prepare_avd:
|
with mock.patch('src.app.prepare_avd') as mocked_prepare_avd:
|
||||||
app.run()
|
app.run()
|
||||||
self.assertFalse(app.is_initialized())
|
self.assertFalse(app.is_initialized('Nexus 5'))
|
||||||
self.assertTrue(mocked_prepare_avd.called)
|
self.assertTrue(mocked_prepare_avd.called)
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
|
||||||
@mock.patch('subprocess.Popen')
|
@mock.patch('subprocess.Popen')
|
||||||
@mock.patch('os.path.exists', mock.MagicMock(return_value=True))
|
@mock.patch('os.path.exists', mock.MagicMock(return_value=True))
|
||||||
def test_run_next_run(self, mocked_open, mocked_subprocess):
|
@mock.patch('builtins.open', mock_open(read_data="\nhw.device.name=Nexus 5\n"))
|
||||||
|
def test_it_doesnt_prepare_avd_if_config_exists(self, mocked_subprocess):
|
||||||
with mock.patch('src.app.prepare_avd') as mocked_prepare_avd:
|
with mock.patch('src.app.prepare_avd') as mocked_prepare_avd:
|
||||||
app.run()
|
app.run()
|
||||||
self.assertTrue(app.is_initialized())
|
self.assertTrue(app.is_initialized('Nexus 5'))
|
||||||
self.assertFalse(mocked_prepare_avd.called)
|
self.assertFalse(mocked_prepare_avd.called)
|
||||||
|
|
||||||
|
@mock.patch('subprocess.Popen')
|
||||||
|
@mock.patch('os.path.exists', mock.MagicMock(return_value=True))
|
||||||
|
@mock.patch('builtins.open', mock_open(read_data="\nhw.device.name=Samsung S7\n"))
|
||||||
|
def test_it_prepares_avd_if_config_is_for_another_device(self, mocked_subprocess):
|
||||||
|
with mock.patch('src.app.prepare_avd') as mocked_prepare_avd:
|
||||||
|
app.run()
|
||||||
|
self.assertFalse(app.is_initialized('Nexus 5'))
|
||||||
|
self.assertTrue(mocked_prepare_avd.called)
|
||||||
|
|
|
@ -18,7 +18,7 @@ class TestAvd(TestCase):
|
||||||
def test_nexus_avd_as_default(self, mocked_suprocess, mocked_open):
|
def test_nexus_avd_as_default(self, mocked_suprocess, mocked_open):
|
||||||
self.assertFalse(mocked_suprocess.called)
|
self.assertFalse(mocked_suprocess.called)
|
||||||
self.assertFalse(mocked_open.called)
|
self.assertFalse(mocked_open.called)
|
||||||
app.prepare_avd('Nexus 5', self.avd_name)
|
app.prepare_avd('Nexus 5', self.avd_name, '550m')
|
||||||
self.assertTrue(mocked_suprocess.called)
|
self.assertTrue(mocked_suprocess.called)
|
||||||
self.assertTrue(mocked_open.called)
|
self.assertTrue(mocked_open.called)
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class TestAvd(TestCase):
|
||||||
self.assertFalse(mocked_sys_link.called)
|
self.assertFalse(mocked_sys_link.called)
|
||||||
self.assertFalse(mocked_suprocess.called)
|
self.assertFalse(mocked_suprocess.called)
|
||||||
self.assertFalse(mocked_open.called)
|
self.assertFalse(mocked_open.called)
|
||||||
app.prepare_avd('Samsung Galaxy S6', self.avd_name)
|
app.prepare_avd('Samsung Galaxy S6', self.avd_name, '550m')
|
||||||
self.assertTrue(mocked_sys_link.called)
|
self.assertTrue(mocked_sys_link.called)
|
||||||
self.assertTrue(mocked_suprocess.called)
|
self.assertTrue(mocked_suprocess.called)
|
||||||
self.assertTrue(mocked_open.called)
|
self.assertTrue(mocked_open.called)
|
||||||
|
|
Loading…
Reference in a new issue