lbry-android-sdk/p4a/tests/recipes/test_python3.py

212 lines
7.8 KiB
Python
Raw Normal View History

2022-11-29 21:35:24 +01:00
import unittest
from os.path import join
from unittest import mock
from pythonforandroid.recipes.python3 import (
NDK_API_LOWER_THAN_SUPPORTED_MESSAGE,
)
from pythonforandroid.util import BuildInterruptingException, build_platform
from tests.recipes.recipe_lib_test import RecipeCtx
class TestPython3Recipe(RecipeCtx, unittest.TestCase):
"""
TestCase for recipe :mod:`~pythonforandroid.recipes.python3`
"""
recipe_name = "python3"
expected_compiler = (
f"/opt/android/android-ndk/toolchains/"
f"llvm/prebuilt/{build_platform}/bin/clang"
)
def test_property__libpython(self):
self.assertEqual(
self.recipe._libpython,
f'libpython{self.recipe.link_version}.so'
)
@mock.patch('pythonforandroid.recipes.python3.Path.is_file')
def test_should_build(self, mock_is_file):
# in case that python lib exists, we shouldn't trigger the build
self.assertFalse(self.recipe.should_build(self.arch))
# in case that python lib doesn't exist, we should trigger the build
mock_is_file.return_value = False
self.assertTrue(self.recipe.should_build(self.arch))
def test_include_root(self):
expected_include_dir = join(
self.recipe.get_build_dir(self.arch.arch), 'Include',
)
self.assertEqual(
expected_include_dir, self.recipe.include_root(self.arch.arch)
)
def test_link_root(self):
expected_link_root = join(
self.recipe.get_build_dir(self.arch.arch), 'android-build',
)
self.assertEqual(
expected_link_root, self.recipe.link_root(self.arch.arch)
)
@mock.patch("pythonforandroid.recipes.python3.subprocess.call")
def test_compile_python_files(self, mock_subprocess):
fake_compile_dir = '/fake/compile/dir'
hostpy = self.recipe.ctx.hostpython = '/fake/hostpython3'
self.recipe.compile_python_files(fake_compile_dir)
mock_subprocess.assert_called_once_with(
[hostpy, '-OO', '-m', 'compileall', '-b', '-f', fake_compile_dir],
)
@mock.patch("pythonforandroid.recipe.Recipe.check_recipe_choices")
@mock.patch("pythonforandroid.archs.find_executable")
def test_get_recipe_env(
self,
mock_find_executable,
mock_check_recipe_choices,
):
"""
Test that method
:meth:`~pythonforandroid.recipes.python3.Python3Recipe.get_recipe_env`
returns the expected flags
"""
mock_find_executable.return_value = self.expected_compiler
mock_check_recipe_choices.return_value = sorted(
self.ctx.recipe_build_order
)
env = self.recipe.get_recipe_env(self.arch)
self.assertIn('-fPIC -DANDROID', env["CFLAGS"])
self.assertEqual(env["CC"], self.arch.get_clang_exe(with_target=True))
# make sure that the mocked methods are actually called
mock_check_recipe_choices.assert_called()
def test_set_libs_flags(self):
# todo: properly check `Python3Recipe.set_lib_flags`
pass
# These decorators are to mock calls to `get_recipe_env`
# and `set_libs_flags`, since these calls are tested separately
@mock.patch("pythonforandroid.util.chdir")
@mock.patch("pythonforandroid.util.makedirs")
@mock.patch("pythonforandroid.archs.find_executable")
def test_build_arch(
self,
mock_find_executable,
mock_makedirs,
mock_chdir):
mock_find_executable.return_value = self.expected_compiler
# specific `build_arch` mocks
with mock.patch(
"builtins.open",
mock.mock_open(read_data="#define ZLIB_VERSION 1.1\nfoo")
) as mock_open_zlib, mock.patch(
"pythonforandroid.recipes.python3.sh.Command"
) as mock_sh_command, mock.patch(
"pythonforandroid.recipes.python3.sh.make"
) as mock_make, mock.patch(
"pythonforandroid.recipes.python3.sh.cp"
) as mock_cp:
self.recipe.build_arch(self.arch)
# make sure that the mocked methods are actually called
recipe_build_dir = self.recipe.get_build_dir(self.arch.arch)
sh_command_calls = {
f"{recipe_build_dir}/config.guess",
f"{recipe_build_dir}/configure",
}
for command in sh_command_calls:
self.assertIn(
mock.call(command),
mock_sh_command.mock_calls,
)
mock_open_zlib.assert_called()
self.assertEqual(mock_make.call_count, 1)
for make_call, kw in mock_make.call_args_list:
self.assertIn(
f'INSTSONAME={self.recipe._libpython}', make_call
)
mock_cp.assert_called_with(
"pyconfig.h", join(recipe_build_dir, 'Include'),
)
mock_makedirs.assert_called()
mock_chdir.assert_called()
def test_build_arch_wrong_ndk_api(self):
# we check ndk_api using recipe's ctx
self.recipe.ctx.ndk_api = 20
with self.assertRaises(BuildInterruptingException) as e:
self.recipe.build_arch(self.arch)
self.assertEqual(
e.exception.args[0],
NDK_API_LOWER_THAN_SUPPORTED_MESSAGE.format(
ndk_api=self.recipe.ctx.ndk_api,
min_ndk_api=self.recipe.MIN_NDK_API,
),
)
# restore recipe's ctx or we could get failures with other test,
# since we share `self.recipe with all the tests of the class
self.recipe.ctx.ndk_api = self.ctx.ndk_api
@mock.patch('shutil.copystat')
@mock.patch('shutil.copyfile')
@mock.patch("pythonforandroid.util.chdir")
@mock.patch("pythonforandroid.util.makedirs")
@mock.patch("pythonforandroid.util.walk")
@mock.patch("pythonforandroid.recipes.python3.sh.find")
@mock.patch("pythonforandroid.recipes.python3.sh.cp")
@mock.patch("pythonforandroid.recipes.python3.sh.zip")
@mock.patch("pythonforandroid.recipes.python3.subprocess.call")
def test_create_python_bundle(
self,
mock_subprocess,
mock_sh_zip,
mock_sh_cp,
mock_sh_find,
mock_walk,
mock_makedirs,
mock_chdir,
mock_copyfile,
mock_copystat,
):
fake_compile_dir = '/fake/compile/dir'
simulated_walk_result = [
["/fake_dir", ["__pycache__", "Lib"], ["README", "setup.py"]],
["/fake_dir/Lib", ["ctypes"], ["abc.pyc", "abc.py"]],
["/fake_dir/Lib/ctypes", [], ["util.pyc", "util.py"]],
]
mock_walk.return_value = simulated_walk_result
self.recipe.create_python_bundle(fake_compile_dir, self.arch)
recipe_build_dir = self.recipe.get_build_dir(self.arch.arch)
modules_build_dir = join(
recipe_build_dir,
'android-build',
'build',
'lib.linux{}-{}-{}'.format(
'2' if self.recipe.version[0] == '2' else '',
self.arch.command_prefix.split('-')[0],
self.recipe.major_minor_version_string
))
expected_sp_paths = [
modules_build_dir,
join(recipe_build_dir, 'Lib'),
self.ctx.get_python_install_dir(self.arch.arch),
]
for n, (sp_call, kw) in enumerate(mock_subprocess.call_args_list):
self.assertEqual(sp_call[0][-1], expected_sp_paths[n])
# we expect two calls to `walk_valid_filens`
self.assertEqual(len(mock_walk.call_args_list), 2)
mock_sh_zip.assert_called()
mock_sh_cp.assert_called()
mock_sh_find.assert_called()
mock_makedirs.assert_called()
mock_chdir.assert_called()
mock_copyfile.assert_called()
mock_copystat.assert_called()