8000 Basic toolchain.py unit tests · kivy/python-for-android@176a4e4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 176a4e4

Browse files
committed
Basic toolchain.py unit tests
First simple set of tests for toolchain.py Also refactors `Context.prepare_build_environment()` slightly. Splits concerns to improve readability and ease unit testing.
1 parent f6f6f19 commit 176a4e4

File tree

2 files changed

+130
-46
lines changed

2 files changed

+130
-46
lines changed

pythonforandroid/build.py

Lines changed: 68 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,66 @@
2727
RECOMMENDED_NDK_API, RECOMMENDED_TARGET_API)
2828

2929

30+
def get_cython_path():
31+
for cython_fn in ("cython", "cython3", "cython2", "cython-2.7"):
32+
cython = sh.which(cython_fn)
33+
if cython:
34+
return cython
35+
raise BuildInterruptingException('No cython binary found.')
36+
37+
38+
def get_ndk_platform_dir(ndk_dir, ndk_api, arch):
39+
ndk_platform_dir_exists = True
40+
platform_dir = arch.platform_dir
41+
ndk_platform = join(
42+
ndk_dir,
43+
'platforms',
44+
'android-{}'.format(ndk_api),
45+
platform_dir)
46+
if not exists(ndk_platform):
47+
warning("ndk_platform doesn't exist: {}".format(ndk_platform))
48+
ndk_platform_dir_exists = False
49+
return ndk_platform, ndk_platform_dir_exists
50+
51+
52+
def get_toolchain_versions(ndk_dir, arch):
53+
toolchain_versions = []
54+
toolchain_path_exists = True
55+
toolchain_prefix = arch.toolchain_prefix
56+
toolchain_path = join(ndk_dir, 'toolchains')
57+
if isdir(toolchain_path):
58+
toolchain_contents = glob.glob('{}/{}-*'.format(toolchain_path,
59+
toolchain_prefix))
60+
toolchain_versions = [split(path)[-1][len(toolchain_prefix) + 1:]
61+
for path in toolchain_contents]
62+
else:
63+
warning('Could not find toolchain subdirectory!')
64+
toolchain_path_exists = False
65+
return toolchain_versions, toolchain_path_exists
66+
67+
68+
def get_targets(sdk_dir):
69+
if exists(join(sdk_dir, 'tools', 'bin', 'avdmanager')):
70+
avdmanager = sh.Command(join(sdk_dir, 'tools', 'bin', 'avdmanager'))
71+
targets = avdmanager('list', 'target').stdout.decode('utf-8').split('\n')
72+
elif exists(join(sdk_dir, 'tools', 'android')):
73+
android = sh.Command(join(sdk_dir, 'tools', 'android'))
74+
targets = android('list').stdout.decode('utf-8').split('\n')
75+
else:
76+
raise BuildInterruptingException(
77+
'Could not find `android` or `sdkmanager` binaries in Android SDK',
78+
instructions='Make sure the path to the Android SDK is correct')
79+
return targets
80+
81+
82+
def get_available_apis(sdk_dir):
83+
targets = get_targets(sdk_dir)
84+
apis = [s for s in targets if re.match(r'^ *API level: ', s)]
85+
apis = [re.findall(r'[0-9]+', s) for s in apis]
86+
apis = [int(s[0]) for s in apis if s]
87+
return apis
88+
89+
3090
class Context(object):
3191
'''A build context. If anything will be built, an instance this class
3292
will be instantiated and used to hold all the build state.'''
@@ -238,20 +298,7 @@ def prepare_build_environment(self,
238298
self.android_api = android_api
239299

240300
check_target_api(android_api, self.archs[0].arch)
241-
242-
if exists(join(sdk_dir, 'tools', 'bin', 'avdmanager')):
243-
avdmanager = sh.Command(join(sdk_dir, 'tools', 'bin', 'avdmanager'))
244-
targets = avdmanager('list', 'target').stdout.decode('utf-8').split('\n')
245-
elif exists(join(sdk_dir, 'tools', 'android')):
246-
android = sh.Command(join(sdk_dir, 'tools', 'android'))
247-
targets = android('list').stdout.decode('utf-8').split('\n')
248-
else:
249-
raise BuildInterruptingException(
250-
'Could not find `android` or `sdkmanager` binaries in Android SDK',
251-
instructions='Make sure the path to the Android SDK is correct')
252-
apis = [s for s in targets if re.match(r'^ *API level: ', s)]
253-
apis = [re.findall(r'[0-9]+', s) for s in apis]
254-
apis = [int(s[0]) for s in apis if s]
301+
apis = get_available_apis(self.sdk_dir)
255302
info('Available Android APIs are ({})'.format(
256303
', '.join(map(str, apis))))
257304
if android_api in apis:
@@ -327,46 +374,21 @@ def prepare_build_environment(self,
327374
if not self.ccache:
328375
info('ccache is missing, the build will not be optimized in the '
329376
'future.')
330-
for cython_fn in ("cython", "cython3", "cython2", "cython-2.7"):
331-
cython = sh.which(cython_fn)
332-
if cython:
333-
self.cython = cython
334-
break
335-
else:
336-
raise BuildInterruptingException('No cython binary found.')
337-
if not self.cython:
338-
ok = False
339-
warning("Missing requirement: cython is not installed")
377+
self.cython = get_cython_path()
340378

341379
# This would need to be changed if supporting multiarch APKs
342380
arch = self.archs[0]
343-
platform_dir = arch.platform_dir
344381
toolchain_prefix = arch.toolchain_prefix
345-
toolchain_version = None
346-
self.ndk_platform = join(
347-
self.ndk_dir,
348-
'platforms',
349-
'android-{}'.format(self.ndk_api),
350-
platform_dir)
351-
if not exists(self.ndk_platform):
352-
warning('ndk_platform doesn\'t exist: {}'.format(
353-
self.ndk_platform))
354-
ok = False
382+
self.ndk_platform, ndk_platform_dir_exists = get_ndk_platform_dir(
383+
self.ndk_dir, self.ndk_api, arch)
384+
ok = ok and ndk_platform_dir_exists
355385

356386
py_platform = sys.platform
357387
if py_platform in ['linux2', 'linux3']:
358388
py_platform = 'linux'
359-
360-
toolchain_versions = []
361-
toolchain_path = join(self.ndk_dir, 'toolchains')
362-
if isdir(toolchain_path):
363-
toolchain_contents = glob.glob('{}/{}-*'.format(toolchain_path,
364-
toolchain_prefix))
365-
toolchain_versions = [split(path)[-1][len(toolchain_prefix) + 1:]
366-
for path in toolchain_contents]
367-
else:
368-
warning('Could not find toolchain subdirectory!')
369-
ok = False
389+
toolchain_versions, toolchain_path_exists = get_toolchain_versions(
390+
self.ndk_dir, arch)
391+
ok = ok and toolchain_path_exists
370392
toolchain_versions.sort()
371393

372394
toolchain_versions_gcc = []

tests/test_toolchain.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import sys
2+
import pytest
3+
import mock
4+
from pythonforandroid.toolchain import ToolchainCL
5+
from pythonforandroid.util import BuildInterruptingException
6+
7+
8+
class TestToolchainCL:
9+
10+
def test_help(self):
11+
"""
12+
Calling with `--help` should print help and exit 0.
13+
"""
14+
argv = ['toolchain.py', '--help', '--storage-dir=/tmp']
15+
with mock.patch('sys.argv', argv), pytest.raises(SystemExit) as ex_info, \
16+
mock.patch('argparse.ArgumentParser.print_help') as m_print_help:
17+
ToolchainCL()
18+
assert ex_info.value.code == 0
19+
assert m_print_help.call_args_list == [mock.call()]
20+
21+
@pytest.mark.skipif(sys.version_info < (3, 0), reason="requires python3")
22+
def test_unknown(self):
23+
"""
24+
Calling with unknown args should print help and exit 1.
25+
"""
26+
argv = ['toolchain.py', '--unknown']
27+
with mock.patch('sys.argv', argv), pytest.raises(SystemExit) as ex_info, \
28+
mock.patch('argparse.ArgumentParser.print_help') as m_print_help:
29+
ToolchainCL()
30+
assert ex_info.value.code == 1
31+
assert m_print_help.call_args_list == [mock.call()]
32+
33+
def test_create(self):
34+
"""
35+
Basic `create` distribution test.
36+
"""
37+
argv = ['toolchain.py', 'create', '--sdk-dir=/tmp/android-sdk', '--ndk-dir=/tmp/android-ndk']
38+
with mock.patch('sys.argv', argv), \
39+
mock.patch('pythonforandroid.build.get_available_apis') as m_get_available_apis, \
40+
mock.patch('pythonforandroid.build.get_toolchain_versions') as m_get_toolchain_versions, \
41+
mock.patch('pythonforandroid.build.get_ndk_platform_dir') as m_get_ndk_platform_dir, \
42+
mock.patch('pythonforandroid.build.get_cython_path') as m_get_cython_path, \
43+
mock.patch('pythonforandroid.toolchain.build_dist_from_args') as m_build_dist_from_args:
44+
m_get_available_apis.return_value = [27]
45+
m_get_toolchain_versions.return_value = (['4.9'], True)
46+
m_get_ndk_platform_dir.return_value = (
47+
'/tmp/android-ndk/platforms/android-21/arch-arm', True)
48+
ToolchainCL()
49+
assert m_get_available_apis.call_args_list == [mock.call('/tmp/android-sdk')]
50+
assert m_get_toolchain_versions.call_args_list == [
51+
mock.call('/tmp/android-ndk', mock.ANY)]
52+
assert m_get_cython_path.call_args_list == [mock.call()]
53+
assert m_build_dist_from_args.call_count == 1
54+
55+
def test_create_no_sdk_dir(self):
56+
"""
57+
The `--sdk-dir` is mandatory to `create` a distribution.
58+
"""
59+
argv = ['toolchain.py', 'create']
60+
with mock.patch('sys.argv', argv), pytest.raises(BuildInterruptingException) as ex_info:
61+
ToolchainCL()
62+
assert ex_info.value.message == 'Android SDK dir was not specified, exiting.'

0 commit comments

Comments
 (0)
0