From 38cfb894b81d9ad56514dafd6a9a9eb172d73743 Mon Sep 17 00:00:00 2001 From: opacam Date: Sat, 8 Jun 2019 14:28:45 +0200 Subject: [PATCH 1/8] [tests] Add unittest for module `pythonforandroid.util` --- tests/test_util.py | 134 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tests/test_util.py diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 0000000000..1f539b0408 --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,134 @@ +import os +import types +import unittest + +try: + from unittest import mock +except ImportError: + # `Python 2` or lower than `Python 3.3` does not + # have the `unittest.mock` module built-in + import mock +from pythonforandroid import util + + +class TestUtil(unittest.TestCase): + @mock.patch("pythonforandroid.util.makedirs") + def test_ensure_dir(self, mock_makedirs): + util.ensure_dir("fake_directory") + mock_makedirs.assert_called_once_with("fake_directory") + + @mock.patch("shutil.rmtree") + @mock.patch("pythonforandroid.util.mkdtemp") + def test_temp_directory(self, mock_mkdtemp, mock_shutil_rmtree): + mock_mkdtemp.return_value = "/temp/any_directory" + with util.temp_directory(): + mock_mkdtemp.assert_called_once() + mock_shutil_rmtree.assert_called_once_with("/temp/any_directory") + + @mock.patch("pythonforandroid.util.chdir") + def test_current_directory(self, moch_chdir): + chdir_dir = "/temp/any_directory" + # test chdir to existing directory + with util.current_directory(chdir_dir): + moch_chdir.assert_called_once_with("/temp/any_directory") + moch_chdir.assert_has_calls( + [ + mock.call("/temp/any_directory"), + mock.call(os.getcwd()), + ] + ) + + def test_current_directory_exception(self): + # test chdir to non-existing directory, should raise error + # for py3 the exception is FileNotFoundError and IOError for py2, to + # avoid introduce conditions, we test with a more generic exception + with self.assertRaises(OSError): + with util.current_directory("/fake/directory"): + # the line below will never be executed + print("") + + @mock.patch("pythonforandroid.util.sh.which") + def test_get_virtualenv_executable(self, mock_sh_which): + # test that all calls to `sh.which` are performed, so we expect the + # first two `sh.which` calls should be None and the last one should + # return the expected virtualenv (the python3 one) + expected_venv = os.path.join( + os.path.expanduser("~"), ".local/bin/virtualenv" + ) + mock_sh_which.side_effect = [None, None, expected_venv] + self.assertEqual(util.get_virtualenv_executable(), expected_venv) + mock_sh_which.assert_has_calls( + [ + mock.call("virtualenv2"), + mock.call("virtualenv-2.7"), + mock.call("virtualenv"), + ] + ) + self.assertEqual(mock_sh_which.call_count, 3) + mock_sh_which.reset_mock() + + # Now test that we don't have virtualenv installed, so all calls to + # `sh.which` should return None + mock_sh_which.side_effect = [None, None, None] + self.assertIsNone(util.get_virtualenv_executable()) + self.assertEqual(mock_sh_which.call_count, 3) + mock_sh_which.assert_has_calls( + [ + mock.call("virtualenv2"), + mock.call("virtualenv-2.7"), + mock.call("virtualenv"), + ] + ) + + def test_walk_valid_filens_sample(self): + file_ens = util.walk_valid_filens( + "/home/opacam/Devel/python-for-android/tests/", + ["__pycache__"], + ["*.pyc"], + ) + for i in os.walk("/home/opacam/Devel/python-for-android/tests/"): + print(i) + for i in file_ens: + print(i) + + @mock.patch("pythonforandroid.util.walk") + def test_walk_valid_filens(self, mock_walk): + 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"]], + ] + # /fake_dir + # |-- README + # |-- setup.py + # |-- __pycache__ + # |-- |__ + # |__Lib + # |-- abc.pyc + # |-- abc.py + # |__ ctypes + # |-- util.pyc + # |-- util.py + mock_walk.return_value = simulated_walk_result + file_ens = util.walk_valid_filens( + "/fake_dir", ["__pycache__"], ["*.py"] + ) + self.assertIsInstance(file_ens, types.GeneratorType) + # given the simulated structure we expect: + expected_result = { + "/fake_dir/README", + "/fake_dir/Lib/abc.pyc", + "/fake_dir/Lib/ctypes/util.pyc", + } + result = set() + for i in file_ens: + result.add(i) + + self.assertEqual(result, expected_result) + + def test_util_exceptions(self): + exc = util.BuildInterruptingException( + "missing dependency xxx", instructions="pip install --user xxx" + ) + with self.assertRaises(SystemExit): + util.handle_build_exception(exc) From a24f1e3836aebda3521c6fab9ef55ffe59d3e886 Mon Sep 17 00:00:00 2001 From: opacam Date: Sun, 9 Jun 2019 19:29:45 +0200 Subject: [PATCH 2/8] [util] Remove unused functions from `pythonforandroid.util` --- pythonforandroid/util.py | 75 ---------------------------------------- 1 file changed, 75 deletions(-) diff --git a/pythonforandroid/util.py b/pythonforandroid/util.py index 9c007c2142..ba392049b6 100644 --- a/pythonforandroid/util.py +++ b/pythonforandroid/util.py @@ -1,8 +1,6 @@ import contextlib from os.path import exists, join from os import getcwd, chdir, makedirs, walk, uname -import io -import json import sh import shutil import sys @@ -62,79 +60,6 @@ def ensure_dir(filename): makedirs(filename) -class JsonStore(object): - """Replacement of shelve using json, needed for support python 2 and 3. - """ - - def __init__(self, filename): - super(JsonStore, self).__init__() - self.filename = filename - self.data = {} - if exists(filename): - try: - with io.open(filename, encoding='utf-8') as fd: - self.data = json.load(fd) - except ValueError: - print("Unable to read the state.db, content will be replaced.") - - def __getitem__(self, key): - return self.data[key] - - def __setitem__(self, key, value): - self.data[key] = value - self.sync() - - def __delitem__(self, key): - del self.data[key] - self.sync() - - def __contains__(self, item): - return item in self.data - - def get(self, item, default=None): - return self.data.get(item, default) - - def keys(self): - return self.data.keys() - - def remove_all(self, prefix): - for key in self.data.keys()[:]: - if not key.startswith(prefix): - continue - del self.data[key] - self.sync() - - def sync(self): - # http://stackoverflow.com/questions/12309269/write-json-data-to-file-in-python/14870531#14870531 - if IS_PY3: - with open(self.filename, 'w') as fd: - json.dump(self.data, fd, ensure_ascii=False) - else: - with io.open(self.filename, 'w', encoding='utf-8') as fd: - fd.write(unicode(json.dumps(self.data, ensure_ascii=False))) # noqa F821 - - -def which(program, path_env): - '''Locate an executable in the system.''' - import os - - def is_exe(fpath): - return os.path.isfile(fpath) and os.access(fpath, os.X_OK) - - fpath, fname = os.path.split(program) - if fpath: - if is_exe(program): - return program - else: - for path in path_env.split(os.pathsep): - path = path.strip('"') - exe_file = os.path.join(path, program) - if is_exe(exe_file): - return exe_file - - return None - - def get_virtualenv_executable(): virtualenv = None if virtualenv is None: From e230b27b039cedc13628a2095963080c48392ba2 Mon Sep 17 00:00:00 2001 From: opacam Date: Mon, 10 Jun 2019 00:25:11 +0200 Subject: [PATCH 3/8] [travis] Make tox use Python 3.7 for the travis's lint stage Because we make use of some mock methods in module `test_util` (eg: `mock.assert_called_once`) which were introduced in python 3.6, so if the system python of the test system is lower than 3.6, then the tox tests will fail --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index ea242c89c3..10dbf7b13a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,8 @@ jobs: include: - stage: lint name: "Tox tests and coverage" + language: python + python: 3.7 script: # we want to fail fast on tox errors without having to `docker build` first - tox -- tests/ --ignore tests/test_pythonpackage.py From 6f65aaf610be6fe6397e00380757f0657a07d5ff Mon Sep 17 00:00:00 2001 From: opacam Date: Mon, 10 Jun 2019 11:49:20 +0200 Subject: [PATCH 4/8] [tests] Enhance some tests for `test_distribution` Verifying the number of calls we made to some functions: - test_folder_exist - test_delete --- tests/test_distribution.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_distribution.py b/tests/test_distribution.py index 8a3dee605d..1780ea6551 100644 --- a/tests/test_distribution.py +++ b/tests/test_distribution.py @@ -74,7 +74,7 @@ def test_folder_exist(self, mock_exists): Bootstrap().get_bootstrap("sdl2", self.ctx) ) self.ctx.bootstrap.distribution.folder_exists() - mock_exists.assert_called_with( + mock_exists.assert_called_once_with( self.ctx.bootstrap.distribution.dist_dir ) @@ -85,7 +85,7 @@ def test_delete(self, mock_rmtree): Bootstrap().get_bootstrap("sdl2", self.ctx) ) self.ctx.bootstrap.distribution.delete() - mock_rmtree.assert_called_with( + mock_rmtree.assert_called_once_with( self.ctx.bootstrap.distribution.dist_dir ) From 44741ae018dfcbd841bb10696c78df9e836a834d Mon Sep 17 00:00:00 2001 From: opacam Date: Tue, 11 Jun 2019 10:44:25 +0200 Subject: [PATCH 5/8] [docs] Add documentation for `tests.test_util` and other reviewers suggestions Reviewers suggestions are: - Remove forgotten debug code - `style code` change for `test_current_directory_exception` - add test `mock_shutil_rmtree.assert_not_called()` for `test_temp_directory` Also changed some lines that shouldn't be split (because are pep8 friendly without breaking it in multiple lines) --- tests/test_util.py | 129 +++++++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 40 deletions(-) diff --git a/tests/test_util.py b/tests/test_util.py index 1f539b0408..9568514890 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -12,46 +12,81 @@ class TestUtil(unittest.TestCase): + """ + An inherited class of `unittest.TestCase`to test the module + :mod:`~pythonforandroid.util`. + """ + @mock.patch("pythonforandroid.util.makedirs") def test_ensure_dir(self, mock_makedirs): + """ + Basic test for method :meth:`~pythonforandroid.util.ensure_dir`. Here + we make sure that the mentioned method is called only once. + """ util.ensure_dir("fake_directory") mock_makedirs.assert_called_once_with("fake_directory") @mock.patch("shutil.rmtree") @mock.patch("pythonforandroid.util.mkdtemp") def test_temp_directory(self, mock_mkdtemp, mock_shutil_rmtree): + """ + Basic test for method :meth:`~pythonforandroid.util.temp_directory`. We + perform this test by `mocking` the command `mkdtemp` and + `shutil.rmtree` and we make sure that those functions are called in the + proper place. + """ mock_mkdtemp.return_value = "/temp/any_directory" with util.temp_directory(): mock_mkdtemp.assert_called_once() + mock_shutil_rmtree.assert_not_called() mock_shutil_rmtree.assert_called_once_with("/temp/any_directory") @mock.patch("pythonforandroid.util.chdir") def test_current_directory(self, moch_chdir): + """ + Basic test for method :meth:`~pythonforandroid.util.current_directory`. + We `mock` chdir and we check that the command is executed once we are + inside a python's `with` statement. Then we check that `chdir has been + called with the proper arguments inside this `with` statement and also + that, once we leave the `with` statement, is called again with the + current working path. + """ chdir_dir = "/temp/any_directory" # test chdir to existing directory with util.current_directory(chdir_dir): moch_chdir.assert_called_once_with("/temp/any_directory") moch_chdir.assert_has_calls( - [ - mock.call("/temp/any_directory"), - mock.call(os.getcwd()), - ] + [mock.call("/temp/any_directory"), mock.call(os.getcwd())] ) def test_current_directory_exception(self): - # test chdir to non-existing directory, should raise error - # for py3 the exception is FileNotFoundError and IOError for py2, to - # avoid introduce conditions, we test with a more generic exception - with self.assertRaises(OSError): - with util.current_directory("/fake/directory"): - # the line below will never be executed - print("") + """ + Another test for method + :meth:`~pythonforandroid.util.current_directory`, but here we check + that using the method with a non-existing-directory raises an `OSError` + exception. + + .. note:: test chdir to non-existing directory, should raise error, + for py3 the exception is FileNotFoundError and IOError for py2, to + avoid introduce conditions, we test with a more generic exception + """ + with self.assertRaises(OSError), util.current_directory( + "/fake/directory" + ): + pass @mock.patch("pythonforandroid.util.sh.which") def test_get_virtualenv_executable(self, mock_sh_which): - # test that all calls to `sh.which` are performed, so we expect the - # first two `sh.which` calls should be None and the last one should - # return the expected virtualenv (the python3 one) + """ + Test method :meth:`~pythonforandroid.util.get_virtualenv_executable`. + In here we test: + + - that all calls to `sh.which` are performed, so we expect the + first two `sh.which` calls should be None and the last one should + return the expected virtualenv (the python3 one) + - that we don't have virtualenv installed, so all calls to + `sh.which` should return None + """ expected_venv = os.path.join( os.path.expanduser("~"), ".local/bin/virtualenv" ) @@ -80,53 +115,67 @@ def test_get_virtualenv_executable(self, mock_sh_which): ] ) - def test_walk_valid_filens_sample(self): - file_ens = util.walk_valid_filens( - "/home/opacam/Devel/python-for-android/tests/", - ["__pycache__"], - ["*.pyc"], - ) - for i in os.walk("/home/opacam/Devel/python-for-android/tests/"): - print(i) - for i in file_ens: - print(i) - @mock.patch("pythonforandroid.util.walk") def test_walk_valid_filens(self, mock_walk): + """ + Test method :meth:`~pythonforandroid.util.walk_valid_filens` + In here we simulate the following directory structure: + + /fake_dir + |-- README + |-- setup.py + |-- __pycache__ + |-- |__ + |__Lib + |-- abc.pyc + |-- abc.py + |__ ctypes + |-- util.pyc + |-- util.py + + Then we execute the method in order to check that we got the expected + result, which should be: + + .. code-block:: python + :emphasize-lines: 2-4 + + expected_result = { + "/fake_dir/README", + "/fake_dir/Lib/abc.pyc", + "/fake_dir/Lib/ctypes/util.pyc", + } + """ 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"]], ] - # /fake_dir - # |-- README - # |-- setup.py - # |-- __pycache__ - # |-- |__ - # |__Lib - # |-- abc.pyc - # |-- abc.py - # |__ ctypes - # |-- util.pyc - # |-- util.py mock_walk.return_value = simulated_walk_result file_ens = util.walk_valid_filens( "/fake_dir", ["__pycache__"], ["*.py"] ) self.assertIsInstance(file_ens, types.GeneratorType) - # given the simulated structure we expect: expected_result = { "/fake_dir/README", "/fake_dir/Lib/abc.pyc", "/fake_dir/Lib/ctypes/util.pyc", } - result = set() - for i in file_ens: - result.add(i) + result = set(file_ens) self.assertEqual(result, expected_result) def test_util_exceptions(self): + """ + Test exceptions for a couple of methods: + + - method :meth:`~pythonforandroid.util.BuildInterruptingException` + - method :meth:`~pythonforandroid.util.handle_build_exception` + + Here we create an exception with method + :meth:`~pythonforandroid.util.BuildInterruptingException` and we run it + inside method :meth:`~pythonforandroid.util.handle_build_exception` to + make sure that it raises an `SystemExit`. + """ exc = util.BuildInterruptingException( "missing dependency xxx", instructions="pip install --user xxx" ) From c2b4c80211fe5385e60c0bed2c53a5299028458b Mon Sep 17 00:00:00 2001 From: opacam Date: Tue, 11 Jun 2019 11:40:20 +0200 Subject: [PATCH 6/8] [docs] Add documentation for module `tests.test_distribution` --- tests/test_distribution.py | 62 +++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/tests/test_distribution.py b/tests/test_distribution.py index 1780ea6551..1716060513 100644 --- a/tests/test_distribution.py +++ b/tests/test_distribution.py @@ -27,7 +27,14 @@ class TestDistribution(unittest.TestCase): + """ + An inherited class of `unittest.TestCase`to test the module + :mod:`~pythonforandroid.distribution`. + """ + def setUp(self): + """Configure a :class:`~pythonforandroid.build.Context` so we can + perform our unittests""" self.ctx = Context() self.ctx.ndk_api = 21 self.ctx.android_api = 27 @@ -42,8 +49,8 @@ def setUp(self): ] def setUp_distribution_with_bootstrap(self, bs, **kwargs): - # extend the setUp by configuring a distribution, because some test - # needs a distribution to be set to be properly tested + """Extend the setUp by configuring a distribution, because some test + needs a distribution to be set to be properly tested""" self.ctx.bootstrap = bs self.ctx.bootstrap.distribution = Distribution.get_distribution( self.ctx, @@ -53,9 +60,13 @@ def setUp_distribution_with_bootstrap(self, bs, **kwargs): ) def tearDown(self): + """Here we make sure that we reset a possible bootstrap created in + `setUp_distribution_with_bootstrap`""" self.ctx.bootstrap = None def test_properties(self): + """Test that some attributes has the expected result (for now, we check + that `__repr__` and `__str__` return the proper values""" self.setUp_distribution_with_bootstrap( Bootstrap().get_bootstrap("sdl2", self.ctx) ) @@ -69,6 +80,9 @@ def test_properties(self): @mock.patch("pythonforandroid.distribution.exists") def test_folder_exist(self, mock_exists): + """Test that method + :meth:`~pythonforandroid.distribution.Distribution.folder_exist` is + called once with the proper arguments.""" self.setUp_distribution_with_bootstrap( Bootstrap().get_bootstrap("sdl2", self.ctx) @@ -80,7 +94,9 @@ def test_folder_exist(self, mock_exists): @mock.patch("pythonforandroid.distribution.rmtree") def test_delete(self, mock_rmtree): - + """Test that method + :meth:`~pythonforandroid.distribution.Distribution.delete` is + called once with the proper arguments.""" self.setUp_distribution_with_bootstrap( Bootstrap().get_bootstrap("sdl2", self.ctx) ) @@ -91,7 +107,9 @@ def test_delete(self, mock_rmtree): @mock.patch("pythonforandroid.distribution.exists") def test_get_distribution_no_name(self, mock_exists): - + """Test that method + :meth:`~pythonforandroid.distribution.Distribution.get_distribution` + returns the proper result which should `unnamed_dist_1`.""" mock_exists.return_value = False self.ctx.bootstrap = Bootstrap().get_bootstrap("sdl2", self.ctx) dist = Distribution.get_distribution(self.ctx) @@ -100,6 +118,9 @@ def test_get_distribution_no_name(self, mock_exists): @mock.patch("pythonforandroid.util.chdir") @mock.patch("pythonforandroid.distribution.open", create=True) def test_save_info(self, mock_open_dist_info, mock_chdir): + """Test that method + :meth:`~pythonforandroid.distribution.Distribution.save_info` + is called once with the proper arguments.""" self.setUp_distribution_with_bootstrap( Bootstrap().get_bootstrap("sdl2", self.ctx) ) @@ -119,6 +140,15 @@ def test_save_info(self, mock_open_dist_info, mock_chdir): def test_get_distributions( self, mock_glob, mock_exists, mock_open_dist_info ): + """Test that method + :meth:`~pythonforandroid.distribution.Distribution.get_distributions` + returns some expected values: + + - A list of instances of class + `~pythonforandroid.distribution.Distribution + - That one of the distributions returned in the result has the + proper values (`name`, `ndk_api` and `recipes`) + """ self.setUp_distribution_with_bootstrap( Bootstrap().get_bootstrap("sdl2", self.ctx) ) @@ -146,6 +176,10 @@ def test_get_distributions( def test_get_distributions_error_ndk_api( self, mock_glob, mock_exists, mock_open_dist_info ): + """Test method + :meth:`~pythonforandroid.distribution.Distribution.get_distributions` + in case that `ndk_api` is not set..which should return a `None`. + """ dist_info_data_no_ndk_api = dist_info_data.copy() dist_info_data_no_ndk_api.pop("ndk_api") self.setUp_distribution_with_bootstrap( @@ -169,6 +203,12 @@ def test_get_distributions_error_ndk_api( def test_get_distributions_error_ndk_api_mismatch( self, mock_glob, mock_exists, mock_get_dists ): + """Test that method + :meth:`~pythonforandroid.distribution.Distribution.get_distribution` + raises an error in case that we have some distribution already build, + with a given `name` and `ndk_api`, and we try to get another + distribution with the same `name` but different `ndk_api`. + """ expected_dist = Distribution.get_distribution( self.ctx, name="test_prj", recipes=["python3", "kivy"] ) @@ -189,10 +229,15 @@ def test_get_distributions_error_ndk_api_mismatch( ) def test_get_distributions_error_extra_dist_dirs(self): + """Test that method + :meth:`~pythonforandroid.distribution.Distribution.get_distributions` + raises an exception of + :class:`~pythonforandroid.util.BuildInterruptingException` in case that + we supply the kwargs `extra_dist_dirs`. + """ self.setUp_distribution_with_bootstrap( Bootstrap().get_bootstrap("sdl2", self.ctx) ) - with self.assertRaises(BuildInterruptingException) as e: self.ctx.bootstrap.distribution.get_distributions( self.ctx, extra_dist_dirs=["/fake/extra/dist_dirs"] @@ -205,6 +250,13 @@ def test_get_distributions_error_extra_dist_dirs(self): @mock.patch("pythonforandroid.distribution.Distribution.get_distributions") def test_get_distributions_possible_dists(self, mock_get_dists): + """Test that method + :meth:`~pythonforandroid.distribution.Distribution.get_distributions` + returns the proper + `:class:`~pythonforandroid.distribution.Distribution` in case that we + already have it build and we request the same + `:class:`~pythonforandroid.distribution.Distribution`. + """ expected_dist = Distribution.get_distribution( self.ctx, name="test_prj", recipes=["python3", "kivy"] ) From d2ff49d12f1aef23bac42b148a88d17b132bf5fe Mon Sep 17 00:00:00 2001 From: opacam Date: Tue, 11 Jun 2019 12:08:29 +0200 Subject: [PATCH 7/8] [docs] Add documentation for module `tests.test_archs` --- tests/test_archs.py | 96 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/tests/test_archs.py b/tests/test_archs.py index ffdc8e1cfe..39bf261654 100644 --- a/tests/test_archs.py +++ b/tests/test_archs.py @@ -46,6 +46,12 @@ 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 def setUp(self): @@ -63,6 +69,12 @@ def setUp(self): 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) with self.assertRaises(AttributeError) as e1: @@ -81,14 +93,28 @@ def test_arch(self): class TestArchARM(ArchSetUpBaseClass, unittest.TestCase): - # Here we mock two functions: - # - `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) + """ + 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 = "arm-linux-androideabi-gcc" mock_ensure_dir.return_value = True @@ -147,16 +173,30 @@ def test_arch_arm(self, mock_ensure_dir, mock_find_executable): class TestArchARMv7a(ArchSetUpBaseClass, unittest.TestCase): - # Here we mock the same functions than the previous tests 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 + """ + 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.glob") @mock.patch("pythonforandroid.archs.find_executable") @mock.patch("pythonforandroid.build.ensure_dir") def test_arch_armv7a( self, mock_ensure_dir, mock_find_executable, mock_glob ): + """ + 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` 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 = "arm-linux-androideabi-gcc" mock_ensure_dir.return_value = True mock_glob.return_value = ["llvm"] @@ -197,9 +237,21 @@ def test_arch_armv7a( 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` + """ mock_find_executable.return_value = "arm-linux-androideabi-gcc" mock_ensure_dir.return_value = True @@ -220,9 +272,22 @@ def test_arch_x86(self, mock_ensure_dir, mock_find_executable): 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` + """ mock_find_executable.return_value = "arm-linux-androideabi-gcc" mock_ensure_dir.return_value = True @@ -242,9 +307,22 @@ def test_arch_x86_64(self, mock_ensure_dir, mock_find_executable): 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` + """ mock_find_executable.return_value = "arm-linux-androideabi-gcc" mock_ensure_dir.return_value = True From 32a4e45f9017af82d9beab791652e096b2e249cd Mon Sep 17 00:00:00 2001 From: opacam Date: Wed, 12 Jun 2019 22:00:08 +0200 Subject: [PATCH 8/8] [travis] Install tox/coveralls dependencies only in travis's `lint` stage Because we only need those dependencies for our `tox` tests so we may speed up a little the overall CI tests timings. We also remove all the other apt commands performed in `before_install` because we don't need it. Note: in order to successfully pass the test `test_pythonpackage_basic.test_virtualenv`, we need to deactivate the system virtualenv See also: https://github.com/travis-ci/travis-ci/issues/8589 --- .travis.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 10dbf7b13a..16af18f65e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,14 +12,6 @@ services: - docker before_install: - - travis_retry sudo apt update -qq - # to successfully send the coveralls reports we need pyOpenSSL - - travis_retry sudo apt install -qq --no-install-recommends - python2.7 python3 python3-venv python3-virtualenv python3-pip - python3-setuptools python3-openssl - # (venv/virtualenv are both used by tests/test_pythonpackage.py) - - sudo pip install tox>=2.0 - - sudo pip3 install coveralls # https://github.com/travis-ci/travis-ci/issues/6069#issuecomment-266546552 - git remote set-branches --add origin master - git fetch @@ -35,6 +27,19 @@ jobs: name: "Tox tests and coverage" language: python python: 3.7 + before_script: + # We need to escape virtualenv for `test_pythonpackage_basic.test_virtualenv` + # See also: https://github.com/travis-ci/travis-ci/issues/8589 + - type -t deactivate && deactivate || true + - export PATH=/opt/python/3.7/bin:$PATH + # Install tox & virtualenv + # Note: venv/virtualenv are both used by tests/test_pythonpackage.py + - pip3.7 install -U virtualenv + - pip3.7 install tox>=2.0 + # Install coveralls & dependencies + # Note: pyOpenSSL needed to send the coveralls reports + - pip3.7 install pyOpenSSL + - pip3.7 install coveralls script: # we want to fail fast on tox errors without having to `docker build` first - tox -- tests/ --ignore tests/test_pythonpackage.py