236 lines
8.7 KiB
Python
236 lines
8.7 KiB
Python
from pythonforandroid.build import Context
|
|
from pythonforandroid.graph import (
|
|
fix_deplist, get_dependency_tuple_list_for_recipe,
|
|
get_recipe_order_and_bootstrap, obvious_conflict_checker,
|
|
)
|
|
from pythonforandroid.bootstrap import Bootstrap
|
|
from pythonforandroid.recipe import Recipe
|
|
from pythonforandroid.util import BuildInterruptingException
|
|
from itertools import product
|
|
|
|
from unittest import mock
|
|
import pytest
|
|
|
|
ctx = Context()
|
|
|
|
name_sets = [['python3'],
|
|
['kivy']]
|
|
bootstraps = [None,
|
|
Bootstrap.get_bootstrap('sdl2', ctx)]
|
|
valid_combinations = list(product(name_sets, bootstraps))
|
|
valid_combinations.extend(
|
|
[(['python3'], Bootstrap.get_bootstrap('sdl2', ctx)),
|
|
(['kivy', 'python3'], Bootstrap.get_bootstrap('sdl2', ctx)),
|
|
(['flask'], Bootstrap.get_bootstrap('webview', ctx)),
|
|
(['pysdl2'], None), # auto-detect bootstrap! important corner case
|
|
]
|
|
)
|
|
invalid_combinations = [
|
|
[['pil', 'pillow'], None],
|
|
[['pysdl2', 'genericndkbuild'], None],
|
|
]
|
|
invalid_combinations_simple = list(invalid_combinations)
|
|
# NOTE !! keep in mind when setting invalid_combinations_simple:
|
|
#
|
|
# This is used to test obvious_conflict_checker(), which only
|
|
# catches CERTAIN conflicts:
|
|
#
|
|
# This must be a list of conflicts where the conflict is ONLY in
|
|
# non-tuple/non-ambiguous dependencies, e.g.:
|
|
#
|
|
# dependencies_1st = ["python2", "pillow"]
|
|
# dependencies_2nd = ["python3", "pillow"]
|
|
#
|
|
# This however won't work:
|
|
#
|
|
# dependencies_1st = [("python2", "python3"), "pillow"]
|
|
#
|
|
# (This is simply because the conflict checker doesn't resolve this to
|
|
# keep the code ismple enough)
|
|
|
|
|
|
def get_fake_recipe(name, depends=None, conflicts=None):
|
|
recipe = mock.Mock()
|
|
recipe.name = name
|
|
recipe.get_opt_depends_in_list = lambda: []
|
|
recipe.get_dir_name = lambda: name
|
|
recipe.depends = list(depends or [])
|
|
recipe.conflicts = list(conflicts or [])
|
|
return recipe
|
|
|
|
|
|
def register_fake_recipes_for_test(monkeypatch, recipe_list):
|
|
_orig_get_recipe = Recipe.get_recipe
|
|
|
|
def mock_get_recipe(name, ctx):
|
|
for recipe in recipe_list:
|
|
if recipe.name == name:
|
|
return recipe
|
|
return _orig_get_recipe(name, ctx)
|
|
# Note: staticmethod() needed for python ONLY, don't ask me why:
|
|
monkeypatch.setattr(Recipe, 'get_recipe', staticmethod(mock_get_recipe))
|
|
|
|
|
|
@pytest.mark.parametrize('names,bootstrap', valid_combinations)
|
|
def test_valid_recipe_order_and_bootstrap(names, bootstrap):
|
|
get_recipe_order_and_bootstrap(ctx, names, bootstrap)
|
|
|
|
|
|
@pytest.mark.parametrize('names,bootstrap', invalid_combinations)
|
|
def test_invalid_recipe_order_and_bootstrap(names, bootstrap):
|
|
with pytest.raises(BuildInterruptingException) as e_info:
|
|
get_recipe_order_and_bootstrap(ctx, names, bootstrap)
|
|
assert "conflict" in e_info.value.message.lower()
|
|
|
|
|
|
def test_blacklist():
|
|
# First, get order without blacklist:
|
|
build_order, python_modules, bs = get_recipe_order_and_bootstrap(
|
|
ctx, ["python3", "kivy"], None
|
|
)
|
|
# Now, obtain again with blacklist:
|
|
build_order_2, python_modules_2, bs_2 = get_recipe_order_and_bootstrap(
|
|
ctx, ["python3", "kivy"], None, blacklist=["libffi"]
|
|
)
|
|
assert "libffi" not in build_order_2
|
|
assert set(build_order_2).union({"libffi"}) == set(build_order)
|
|
|
|
# Check that we get a conflict when using webview and kivy combined:
|
|
wbootstrap = Bootstrap.get_bootstrap('webview', ctx)
|
|
with pytest.raises(BuildInterruptingException) as e_info:
|
|
get_recipe_order_and_bootstrap(ctx, ["flask", "kivy"], wbootstrap)
|
|
assert "conflict" in e_info.value.message.lower()
|
|
|
|
# We should no longer get a conflict blacklisting sdl2:
|
|
get_recipe_order_and_bootstrap(
|
|
ctx, ["flask", "kivy"], wbootstrap, blacklist=["sdl2"]
|
|
)
|
|
|
|
|
|
def test_get_dependency_tuple_list_for_recipe(monkeypatch):
|
|
r = get_fake_recipe("recipe1", depends=[
|
|
"libffi",
|
|
("libffi", "Pillow")
|
|
])
|
|
dep_list = get_dependency_tuple_list_for_recipe(
|
|
r, blacklist={"libffi"}
|
|
)
|
|
assert dep_list == [("pillow",)]
|
|
|
|
|
|
@pytest.mark.parametrize('names,bootstrap', valid_combinations)
|
|
def test_valid_obvious_conflict_checker(names, bootstrap):
|
|
# Note: obvious_conflict_checker is stricter on input
|
|
# (needs fix_deplist) than get_recipe_order_and_bootstrap!
|
|
obvious_conflict_checker(ctx, fix_deplist(names))
|
|
|
|
|
|
@pytest.mark.parametrize('names,bootstrap',
|
|
invalid_combinations_simple # see above for why this
|
|
) # is a separate list
|
|
def test_invalid_obvious_conflict_checker(names, bootstrap):
|
|
# Note: obvious_conflict_checker is stricter on input
|
|
# (needs fix_deplist) than get_recipe_order_and_bootstrap!
|
|
with pytest.raises(BuildInterruptingException) as e_info:
|
|
obvious_conflict_checker(ctx, fix_deplist(names))
|
|
assert "conflict" in e_info.value.message.lower()
|
|
|
|
|
|
def test_misc_obvious_conflict_checker(monkeypatch):
|
|
# Check that the assert about wrong input data is hit:
|
|
with pytest.raises(AssertionError) as e_info:
|
|
obvious_conflict_checker(
|
|
ctx,
|
|
["this_is_invalid"]
|
|
# (invalid because it isn't properly nested as tuple)
|
|
)
|
|
|
|
# Test that non-recipe dependencies work in overall:
|
|
obvious_conflict_checker(
|
|
ctx, fix_deplist(["python3", "notarecipelibrary"])
|
|
)
|
|
|
|
# Test that a conflict with a non-recipe dependency works:
|
|
# This is currently not used, so we need a custom test recipe:
|
|
# To get that, we simply modify one!
|
|
with monkeypatch.context() as m:
|
|
register_fake_recipes_for_test(m, [
|
|
get_fake_recipe("recipe1", conflicts=[("fakelib")]),
|
|
])
|
|
with pytest.raises(BuildInterruptingException) as e_info:
|
|
obvious_conflict_checker(ctx, fix_deplist(["recipe1", "fakelib"]))
|
|
assert "conflict" in e_info.value.message.lower()
|
|
|
|
# Test a case where a recipe pulls in a conditional tuple
|
|
# of additional dependencies. This is e.g. done for ('python3',
|
|
# 'python2', ...) but most recipes don't depend on this anymore,
|
|
# so we need to add a manual test for this case:
|
|
with monkeypatch.context() as m:
|
|
register_fake_recipes_for_test(m, [
|
|
get_fake_recipe("recipe1", depends=[("libffi", "Pillow")]),
|
|
])
|
|
obvious_conflict_checker(ctx, fix_deplist(["recipe1"]))
|
|
|
|
|
|
def test_indirectconflict_obvious_conflict_checker(monkeypatch):
|
|
# Test a case where there's an indirect conflict, which also
|
|
# makes sure the error message correctly blames the OUTER recipes
|
|
# as original conflict source:
|
|
with monkeypatch.context() as m:
|
|
register_fake_recipes_for_test(m, [
|
|
get_fake_recipe("outerrecipe1", depends=["innerrecipe1"]),
|
|
get_fake_recipe("outerrecipe2", depends=["innerrecipe2"]),
|
|
get_fake_recipe("innerrecipe1"),
|
|
get_fake_recipe("innerrecipe2", conflicts=["innerrecipe1"]),
|
|
])
|
|
with pytest.raises(BuildInterruptingException) as e_info:
|
|
obvious_conflict_checker(
|
|
ctx,
|
|
fix_deplist(["outerrecipe1", "outerrecipe2"])
|
|
)
|
|
assert ("conflict" in e_info.value.message.lower() and
|
|
"outerrecipe1" in e_info.value.message.lower() and
|
|
"outerrecipe2" in e_info.value.message.lower())
|
|
|
|
|
|
def test_multichoice_obvious_conflict_checker(monkeypatch):
|
|
# Test a case where there's a conflict with a multi-choice tuple:
|
|
with monkeypatch.context() as m:
|
|
register_fake_recipes_for_test(m, [
|
|
get_fake_recipe("recipe1", conflicts=["lib1", "lib2"]),
|
|
get_fake_recipe("recipe2", depends=[("lib1", "lib2")]),
|
|
])
|
|
with pytest.raises(BuildInterruptingException) as e_info:
|
|
obvious_conflict_checker(
|
|
ctx,
|
|
fix_deplist([("lib1", "lib2"), "recipe1"])
|
|
)
|
|
assert "conflict" in e_info.value.message.lower()
|
|
|
|
|
|
def test_bootstrap_dependency_addition():
|
|
build_order, python_modules, bs = get_recipe_order_and_bootstrap(
|
|
ctx, ['kivy'], None)
|
|
assert ('hostpython3' in build_order)
|
|
|
|
|
|
def test_graph_deplist_transformation():
|
|
test_pairs = [
|
|
(["Pillow", ('python2', 'python3')],
|
|
[('pillow',), ('python2', 'python3')]),
|
|
(["Pillow", ('python2',)],
|
|
[('pillow',), ('python2',)]),
|
|
]
|
|
for (before_list, after_list) in test_pairs:
|
|
assert fix_deplist(before_list) == after_list
|
|
|
|
|
|
def test_bootstrap_dependency_addition2():
|
|
build_order, python_modules, bs = get_recipe_order_and_bootstrap(
|
|
ctx, ['kivy', 'python3'], None)
|
|
assert 'hostpython3' in build_order
|
|
|
|
|
|
if __name__ == "__main__":
|
|
get_recipe_order_and_bootstrap(ctx, ['python3'],
|
|
Bootstrap.get_bootstrap('sdl2', ctx))
|