diff --git a/tests/targets/test_android.py b/tests/targets/test_android.py index e5f9fd3..cc4d009 100644 --- a/tests/targets/test_android.py +++ b/tests/targets/test_android.py @@ -1,11 +1,12 @@ import os -import pytest -import codecs import tempfile +from unittest import mock + +import pytest + import buildozer as buildozer_module from buildozer import Buildozer from buildozer.targets.android import TargetAndroid -from unittest import mock def patch_buildozer(method): @@ -51,25 +52,110 @@ def patch_platform(platform): class TestTargetAndroid: + @staticmethod def default_specfile_path(): return os.path.join(os.path.dirname(buildozer_module.__file__), "default.spec") def setup_method(self): - """Creates a temporary spec file containing the content of the default.spec.""" - self.specfile = tempfile.NamedTemporaryFile(suffix=".spec", delete=False) - default_spec = codecs.open(self.default_specfile_path(), encoding="utf-8") - self.specfile.write(default_spec.read().encode("utf-8")) - self.specfile.close() - self.buildozer = Buildozer(filename=self.specfile.name, target="android") - self.target_android = TargetAndroid(self.buildozer) + """ + Create a temporary directory that will contain the spec file and will + serve as the root_dir. + """ + self.temp_dir = tempfile.TemporaryDirectory() def tear_method(self): - """Deletes the temporary spec file.""" - os.unlink(self.specfile.name) + """ + Remove the temporary directory created in self.setup_method. + """ + self.temp_dir.cleanup() + + def init_target(self, options=None): + """ + Create a buildozer.spec file in the temporary directory and init the + Buildozer and TargetAndroid instances. + + The optional argument can be used to overwrite the config options in + the buildozer.spec file, e.g.: + + self.init_target({'title': 'Test App'}) + + will replace line 4 of the default spec file. + """ + if options is None: + options = {} + + spec_path = os.path.join(self.temp_dir.name, 'buildozer.spec') + + with open(TestTargetAndroid.default_specfile_path()) as f: + default_spec = f.readlines() + + spec = [] + for line in default_spec: + if line.strip(): + key = line.split()[0] + + if key.startswith('#'): + key = key[1:] + + if key in options: + line = '{} = {}\n'.format(key, options[key]) + + spec.append(line) + + with open(spec_path, 'w') as f: + f.writelines(spec) + + self.buildozer = Buildozer(filename=spec_path, target='android') + self.target_android = TargetAndroid(self.buildozer) + + def call_build_package(self): + """ + Call the build_package() method of the tested TargetAndroid instance, + patching the functions that would otherwise produce side-effects. + + Return the mocked execute_build_package() method of the TargetAndroid + instance so that tests can easily check which command-line arguments + would be passed on to python-for-android's toolchain. + """ + expected_dist_dir = ( + '{buildozer_dir}/android/platform/build-armeabi-v7a/dists/myapp__armeabi-v7a'.format( + buildozer_dir=self.buildozer.buildozer_dir) + ) + + with patch_target_android( + '_update_libraries_references' + ) as m_update_libraries_references, patch_target_android( + '_generate_whitelist' + ) as m_generate_whitelist, mock.patch( + 'buildozer.targets.android.TargetAndroid.execute_build_package' + ) as m_execute_build_package, mock.patch( + 'buildozer.targets.android.copyfile' + ) as m_copyfile, mock.patch( + 'buildozer.targets.android.os.listdir' + ) as m_listdir: + m_listdir.return_value = ['30.0.0-rc2'] + self.target_android.build_package() + + assert m_listdir.call_count == 1 + assert m_update_libraries_references.call_args_list == [ + mock.call(expected_dist_dir) + ] + assert m_generate_whitelist.call_args_list == [mock.call(expected_dist_dir)] + assert m_copyfile.call_args_list == [ + mock.call( + '{expected_dist_dir}/bin/MyApplication-0.1-debug.apk'.format( + expected_dist_dir=expected_dist_dir + ), + '{bin_dir}/myapp-0.1-armeabi-v7a-debug.apk'.format(bin_dir=self.buildozer.bin_dir), + ) + ] + + return m_execute_build_package def test_init(self): """Tests init defaults.""" + self.init_target() assert self.target_android._arch == "armeabi-v7a" assert self.target_android._build_dir.endswith( ".buildozer/android/platform/build-armeabi-v7a" @@ -100,6 +186,7 @@ class TestTargetAndroid: def test_sdkmanager(self): """Tests the _sdkmanager() method.""" + self.init_target() kwargs = {} with patch_buildozer_cmd() as m_cmd, patch_buildozer_cmd_expect() as m_cmd_expect, patch_os_isfile() as m_isfile: m_isfile.return_value = True @@ -119,6 +206,7 @@ class TestTargetAndroid: def test_check_requirements(self): """Basic tests for the check_requirements() method.""" + self.init_target() assert not hasattr(self.target_android, "adb_cmd") assert not hasattr(self.target_android, "javac_cmd") assert "PATH" not in self.buildozer.environ @@ -139,6 +227,7 @@ class TestTargetAndroid: def test_check_configuration_tokens(self): """Basic tests for the check_configuration_tokens() method.""" + self.init_target() with mock.patch( "buildozer.targets.android.Target.check_configuration_tokens" ) as m_check_configuration_tokens: @@ -148,6 +237,7 @@ class TestTargetAndroid: @pytest.mark.parametrize("platform", ["linux", "darwin"]) def test_install_android_sdk(self, platform): """Basic tests for the _install_android_sdk() method.""" + self.init_target() with patch_buildozer_file_exists() as m_file_exists, patch_buildozer_download() as m_download: m_file_exists.return_value = True sdk_dir = self.target_android._install_android_sdk() @@ -178,28 +268,8 @@ class TestTargetAndroid: def test_build_package(self): """Basic tests for the build_package() method.""" - expected_dist_dir = ( - "{buildozer_dir}/android/platform/build-armeabi-v7a/dists/myapp__armeabi-v7a".format( - buildozer_dir=self.buildozer.buildozer_dir) - ) - with patch_target_android( - "_update_libraries_references" - ) as m_update_libraries_references, patch_target_android( - "_generate_whitelist" - ) as m_generate_whitelist, mock.patch( - "buildozer.targets.android.TargetAndroid.execute_build_package" - ) as m_execute_build_package, mock.patch( - "buildozer.targets.android.copyfile" - ) as m_copyfile, mock.patch( - "buildozer.targets.android.os.listdir" - ) as m_listdir: - m_listdir.return_value = ["30.0.0-rc2"] - self.target_android.build_package() - assert m_listdir.call_count == 1 - assert m_update_libraries_references.call_args_list == [ - mock.call(expected_dist_dir) - ] - assert m_generate_whitelist.call_args_list == [mock.call(expected_dist_dir)] + self.init_target() + m_execute_build_package = self.call_build_package() assert m_execute_build_package.call_args_list == [ mock.call( [ @@ -217,11 +287,38 @@ class TestTargetAndroid: ] ) ] - assert m_copyfile.call_args_list == [ + + def test_build_package_intent_filters(self): + """ + The build_package() method should honour the manifest.intent_filters + config option. + """ + filters_path = os.path.join(self.temp_dir.name, 'filters.xml') + + with open(filters_path, 'w') as f: + f.write('') + + self.init_target({ + 'android.manifest.intent_filters': 'filters.xml' + }) + + m_execute_build_package = self.call_build_package() + + assert m_execute_build_package.call_args_list == [ mock.call( - "{expected_dist_dir}/bin/MyApplication-0.1-debug.apk".format( - expected_dist_dir=expected_dist_dir - ), - "{bin_dir}/myapp-0.1-armeabi-v7a-debug.apk".format(bin_dir=self.buildozer.bin_dir), + [ + ('--name', "'My Application'"), + ('--version', '0.1'), + ('--package', 'org.test.myapp'), + ('--minsdk', '21'), + ('--ndk-api', '21'), + ('--private', '{buildozer_dir}/android/app'.format(buildozer_dir=self.buildozer.buildozer_dir)), + ('--android-entrypoint', 'org.kivy.android.PythonActivity'), + ('--android-apptheme', '@android:style/Theme.NoTitleBar'), + ('--orientation', 'portrait'), + ('--window',), + ('--intent-filters', os.path.realpath(filters_path)), + ('debug',), + ] ) ]