lbry-android-sdk/p4a/tests/test_archs.py
2022-11-29 15:35:24 -05:00

365 lines
13 KiB
Python

import os
import unittest
from os import environ
from unittest import mock
from pythonforandroid.bootstrap import Bootstrap
from pythonforandroid.distribution import Distribution
from pythonforandroid.recipe import Recipe
from pythonforandroid.build import Context
from pythonforandroid.util import BuildInterruptingException
from pythonforandroid.archs import (
Arch,
ArchARM,
ArchARMv7_a,
ArchAarch_64,
Archx86,
Archx86_64,
)
from pythonforandroid.androidndk import AndroidNDK
expected_env_gcc_keys = {
"CFLAGS",
"LDFLAGS",
"CXXFLAGS",
"CC",
"CXX",
"LDSHARED",
"STRIP",
"MAKE",
"READELF",
"BUILDLIB_PATH",
"PATH",
"ARCH",
"NDK_API",
}
class ArchSetUpBaseClass(object):
"""
An class object which is intended to be used as a base class to configure
an inherited class of `unittest.TestCase`. This class will override the
`setUp` method.
"""
ctx = None
expected_compiler = ""
TEST_ARCH = 'armeabi-v7a'
def setUp(self):
self.ctx = Context()
self.ctx.ndk_api = 21
self.ctx.android_api = 27
self.ctx._sdk_dir = "/opt/android/android-sdk"
self.ctx._ndk_dir = "/opt/android/android-ndk"
self.ctx.ndk = AndroidNDK(self.ctx._ndk_dir)
self.ctx.setup_dirs(os.getcwd())
self.ctx.bootstrap = Bootstrap().get_bootstrap("sdl2", self.ctx)
self.ctx.bootstrap.distribution = Distribution.get_distribution(
self.ctx,
name="sdl2",
recipes=["python3", "kivy"],
archs=[self.TEST_ARCH],
)
self.ctx.python_recipe = Recipe.get_recipe("python3", self.ctx)
# Here we define the expected compiler, which, as per ndk >= r19,
# should be the same for all the tests (no more gcc compiler)
self.expected_compiler = (
f"/opt/android/android-ndk/toolchains/"
f"llvm/prebuilt/{self.ctx.ndk.host_tag}/bin/clang"
)
class TestArch(ArchSetUpBaseClass, unittest.TestCase):
"""
An inherited class of `ArchSetUpBaseClass` and `unittest.TestCase` which
will be used to perform tests for the base class
:class:`~pythonforandroid.archs.Arch`.
"""
def test_arch(self):
arch = Arch(self.ctx)
self.assertEqual(arch.__str__(), arch.arch)
self.assertEqual(arch.target, "None21")
self.assertIsNone(arch.command_prefix)
self.assertIsInstance(arch.include_dirs, list)
class TestArchARM(ArchSetUpBaseClass, unittest.TestCase):
"""
An inherited class of `ArchSetUpBaseClass` and `unittest.TestCase` which
will be used to perform tests for :class:`~pythonforandroid.archs.ArchARM`.
"""
@mock.patch("pythonforandroid.archs.find_executable")
@mock.patch("pythonforandroid.build.ensure_dir")
def test_arch_arm(self, mock_ensure_dir, mock_find_executable):
"""
Test that class :class:`~pythonforandroid.archs.ArchARM` returns some
expected attributes and environment variables.
.. note::
Here we mock two methods:
- `ensure_dir` because we don't want to create any directory
- `find_executable` because otherwise we will
get an error when trying to find the compiler (we are setting
some fake paths for our android sdk and ndk so probably will
not exist)
"""
mock_find_executable.return_value = self.expected_compiler
mock_ensure_dir.return_value = True
arch = ArchARM(self.ctx)
self.assertEqual(arch.arch, "armeabi")
self.assertEqual(arch.__str__(), "armeabi")
self.assertEqual(arch.command_prefix, "arm-linux-androideabi")
self.assertEqual(arch.target, "armv7a-linux-androideabi21")
arch = ArchARM(self.ctx)
# Check environment flags
env = arch.get_env()
self.assertIsInstance(env, dict)
self.assertEqual(
expected_env_gcc_keys, set(env.keys()) & expected_env_gcc_keys
)
# check find_executable calls
mock_find_executable.assert_called_once_with(
self.expected_compiler, path=environ["PATH"]
)
# check gcc compilers
self.assertEqual(env["CC"].split()[0], self.expected_compiler)
self.assertEqual(env["CXX"].split()[0], self.expected_compiler + "++")
# check android binaries
self.assertEqual(
env["STRIP"].split()[0],
os.path.join(
self.ctx._ndk_dir,
f"toolchains/llvm/prebuilt/{self.ctx.ndk.host_tag}/bin",
"llvm-strip",
)
)
self.assertEqual(
env["READELF"].split()[0],
os.path.join(
self.ctx._ndk_dir,
f"toolchains/llvm/prebuilt/{self.ctx.ndk.host_tag}/bin",
"llvm-readelf",
)
)
# check that cflags are in gcc
self.assertIn(env["CFLAGS"], env["CC"])
# check that flags aren't in gcc and also check ccache
self.ctx.ccache = "/usr/bin/ccache"
env = arch.get_env(with_flags_in_cc=False)
self.assertNotIn(env["CFLAGS"], env["CC"])
self.assertEqual(env["USE_CCACHE"], "1")
self.assertEqual(env["NDK_CCACHE"], "/usr/bin/ccache")
# Check exception in case that CC is not found
mock_find_executable.return_value = None
with self.assertRaises(BuildInterruptingException) as e:
arch.get_env()
self.assertEqual(
e.exception.args[0],
"Couldn't find executable for CC. This indicates a problem "
"locating the {expected_compiler} executable in the Android "
"NDK, not that you don't have a normal compiler installed. "
"Exiting.".format(expected_compiler=self.expected_compiler),
)
class TestArchARMv7a(ArchSetUpBaseClass, unittest.TestCase):
"""
An inherited class of `ArchSetUpBaseClass` and `unittest.TestCase` which
will be used to perform tests for
:class:`~pythonforandroid.archs.ArchARMv7_a`.
"""
@mock.patch("pythonforandroid.archs.find_executable")
@mock.patch("pythonforandroid.build.ensure_dir")
def test_arch_armv7a(
self, mock_ensure_dir, mock_find_executable
):
"""
Test that class :class:`~pythonforandroid.archs.ArchARMv7_a` returns
some expected attributes and environment variables.
.. note::
Here we mock the same functions than
:meth:`TestArchARM.test_arch_arm`.
This has to be done because here we tests the `get_env` with clang
"""
mock_find_executable.return_value = self.expected_compiler
mock_ensure_dir.return_value = True
arch = ArchARMv7_a(self.ctx)
self.assertEqual(arch.arch, "armeabi-v7a")
self.assertEqual(arch.__str__(), "armeabi-v7a")
self.assertEqual(arch.command_prefix, "arm-linux-androideabi")
self.assertEqual(arch.target, "armv7a-linux-androideabi21")
env = arch.get_env()
# check find_executable calls
mock_find_executable.assert_called_once_with(
self.expected_compiler, path=environ["PATH"]
)
# check clang
self.assertEqual(
env["CC"].split()[0],
"{ndk_dir}/toolchains/llvm/prebuilt/"
"{host_tag}/bin/clang".format(
ndk_dir=self.ctx._ndk_dir, host_tag=self.ctx.ndk.host_tag
),
)
self.assertEqual(
env["CXX"].split()[0],
"{ndk_dir}/toolchains/llvm/prebuilt/"
"{host_tag}/bin/clang++".format(
ndk_dir=self.ctx._ndk_dir, host_tag=self.ctx.ndk.host_tag
),
)
# For armeabi-v7a we expect some extra cflags
self.assertIn(
" -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -mthumb",
env["CFLAGS"],
)
class TestArchX86(ArchSetUpBaseClass, unittest.TestCase):
"""
An inherited class of `ArchSetUpBaseClass` and `unittest.TestCase` which
will be used to perform tests for :class:`~pythonforandroid.archs.Archx86`.
"""
@mock.patch("pythonforandroid.archs.find_executable")
@mock.patch("pythonforandroid.build.ensure_dir")
def test_arch_x86(self, mock_ensure_dir, mock_find_executable):
"""
Test that class :class:`~pythonforandroid.archs.Archx86` returns
some expected attributes and environment variables.
.. note::
Here we mock the same functions than
:meth:`TestArchARM.test_arch_arm` plus `glob`, so we make sure that
the glob result is the expected even if the folder doesn't exist,
which is probably the case. This has to be done because here we
tests the `get_env` with clang
"""
mock_find_executable.return_value = self.expected_compiler
mock_ensure_dir.return_value = True
arch = Archx86(self.ctx)
self.assertEqual(arch.arch, "x86")
self.assertEqual(arch.__str__(), "x86")
self.assertEqual(arch.command_prefix, "i686-linux-android")
self.assertEqual(arch.target, "i686-linux-android21")
env = arch.get_env()
# check find_executable calls
mock_find_executable.assert_called_once_with(
self.expected_compiler, path=environ["PATH"]
)
# For x86 we expect some extra cflags in our `environment`
self.assertIn(
" -march=i686 -mssse3 -mfpmath=sse -m32",
env["CFLAGS"],
)
class TestArchX86_64(ArchSetUpBaseClass, unittest.TestCase):
"""
An inherited class of `ArchSetUpBaseClass` and `unittest.TestCase` which
will be used to perform tests for
:class:`~pythonforandroid.archs.Archx86_64`.
"""
@mock.patch("pythonforandroid.archs.find_executable")
@mock.patch("pythonforandroid.build.ensure_dir")
def test_arch_x86_64(
self, mock_ensure_dir, mock_find_executable
):
"""
Test that class :class:`~pythonforandroid.archs.Archx86_64` returns
some expected attributes and environment variables.
.. note::
Here we mock the same functions than
:meth:`TestArchARM.test_arch_arm` plus `glob`, so we make sure that
the glob result is the expected even if the folder doesn't exist,
which is probably the case. This has to be done because here we
tests the `get_env` with clang
"""
mock_find_executable.return_value = self.expected_compiler
mock_ensure_dir.return_value = True
arch = Archx86_64(self.ctx)
self.assertEqual(arch.arch, "x86_64")
self.assertEqual(arch.__str__(), "x86_64")
self.assertEqual(arch.command_prefix, "x86_64-linux-android")
self.assertEqual(arch.target, "x86_64-linux-android21")
env = arch.get_env()
# check find_executable calls
mock_find_executable.assert_called_once_with(
self.expected_compiler, path=environ["PATH"]
)
# For x86_64 we expect some extra cflags in our `environment`
mock_find_executable.assert_called_once()
self.assertIn(
" -march=x86-64 -msse4.2 -mpopcnt -m64", env["CFLAGS"]
)
class TestArchAArch64(ArchSetUpBaseClass, unittest.TestCase):
"""
An inherited class of `ArchSetUpBaseClass` and `unittest.TestCase` which
will be used to perform tests for
:class:`~pythonforandroid.archs.ArchAarch_64`.
"""
@mock.patch("pythonforandroid.archs.find_executable")
@mock.patch("pythonforandroid.build.ensure_dir")
def test_arch_aarch_64(
self, mock_ensure_dir, mock_find_executable
):
"""
Test that class :class:`~pythonforandroid.archs.ArchAarch_64` returns
some expected attributes and environment variables.
.. note::
Here we mock the same functions than
:meth:`TestArchARM.test_arch_arm` plus `glob`, so we make sure that
the glob result is the expected even if the folder doesn't exist,
which is probably the case. This has to be done because here we
tests the `get_env` with clang
"""
mock_find_executable.return_value = self.expected_compiler
mock_ensure_dir.return_value = True
arch = ArchAarch_64(self.ctx)
self.assertEqual(arch.arch, "arm64-v8a")
self.assertEqual(arch.__str__(), "arm64-v8a")
self.assertEqual(arch.command_prefix, "aarch64-linux-android")
self.assertEqual(arch.target, "aarch64-linux-android21")
env = arch.get_env()
# check find_executable calls
mock_find_executable.assert_called_once_with(
self.expected_compiler, path=environ["PATH"]
)
# For x86_64 we expect to find an extra key in`environment`
for flag in {"CFLAGS", "CXXFLAGS", "CC", "CXX"}:
self.assertIn("-march=armv8-a", env[flag])