1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-09-19 03:30:12 +02:00

Merge branch 'metadata' into 'master'

metadata.py: use pathlib and support Windows

See merge request fdroid/fdroidserver!939
This commit is contained in:
Hans-Christoph Steiner 2021-06-08 15:02:54 +00:00
commit 8b17fbf703
8 changed files with 766 additions and 510 deletions

View File

@ -669,8 +669,8 @@ def get_metadata_files(vercodes):
found_invalid = False found_invalid = False
metadatafiles = [] metadatafiles = []
for appid in vercodes.keys(): for appid in vercodes.keys():
f = os.path.join('metadata', '%s.yml' % appid) f = Path('metadata') / ('%s.yml' % appid)
if os.path.exists(f): if f.exists():
metadatafiles.append(f) metadatafiles.append(f)
else: else:
found_invalid = True found_invalid = True
@ -795,9 +795,9 @@ def get_build_dir(app):
'''get the dir that this app will be built in''' '''get the dir that this app will be built in'''
if app.RepoType == 'srclib': if app.RepoType == 'srclib':
return os.path.join('build', 'srclib', app.Repo) return Path('build/srclib') / app.Repo
return os.path.join('build', app.id) return Path('build') / app.id
class Encoder(json.JSONEncoder): class Encoder(json.JSONEncoder):
@ -869,6 +869,8 @@ def get_head_commit_id(git_repo):
def setup_vcs(app): def setup_vcs(app):
'''checkout code from VCS and return instance of vcs and the build dir''' '''checkout code from VCS and return instance of vcs and the build dir'''
build_dir = get_build_dir(app) build_dir = get_build_dir(app)
# TODO: Remove this
build_dir = str(build_dir)
# Set up vcs interface and make sure we have the latest code... # Set up vcs interface and make sure we have the latest code...
logging.debug("Getting {0} vcs interface for {1}" logging.debug("Getting {0} vcs interface for {1}"
@ -3982,6 +3984,8 @@ YAML_LINT_CONFIG = {'extends': 'default',
def run_yamllint(path, indent=0): def run_yamllint(path, indent=0):
# TODO: Remove this
path = str(path)
try: try:
import yamllint.config import yamllint.config
import yamllint.linter import yamllint.linter

View File

@ -19,9 +19,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import git import git
import os from pathlib import Path, PurePosixPath
import platform
import re import re
import glob
import logging import logging
import yaml import yaml
try: try:
@ -31,9 +31,9 @@ except ImportError:
import importlib import importlib
from collections import OrderedDict from collections import OrderedDict
import fdroidserver.common from . import common
from fdroidserver import _ from . import _
from fdroidserver.exception import MetaDataException, FDroidException from .exception import MetaDataException, FDroidException
srclibs = None srclibs = None
warnings_action = None warnings_action = None
@ -330,7 +330,7 @@ class Build(dict):
ndk = self.ndk ndk = self.ndk
if isinstance(ndk, list): if isinstance(ndk, list):
ndk = self.ndk[0] ndk = self.ndk[0]
return fdroidserver.common.config['ndk_paths'].get(ndk, '') return common.config['ndk_paths'].get(ndk, '')
flagtypes = { flagtypes = {
@ -470,15 +470,22 @@ def parse_yaml_srclib(metadatapath):
'Subdir': None, 'Subdir': None,
'Prepare': None} 'Prepare': None}
if not os.path.exists(metadatapath): if not metadatapath.exists():
_warn_or_exception(_("Invalid scrlib metadata: '{file}' " _warn_or_exception(_("Invalid scrlib metadata: '{file}' "
"does not exist" "does not exist"
.format(file=metadatapath))) .format(file=metadatapath)))
return thisinfo return thisinfo
with open(metadatapath, "r", encoding="utf-8") as f: with metadatapath.open("r", encoding="utf-8") as f:
try: try:
data = yaml.load(f, Loader=SafeLoader) data = yaml.load(f, Loader=SafeLoader)
if type(data) is not dict:
if platform.system() == 'Windows':
# Handle symlink on Windows
symlink = metadatapath.parent / metadatapath.read_text()
if symlink.is_file():
with symlink.open("r", encoding="utf-8") as s:
data = yaml.load(s, Loader=SafeLoader)
if type(data) is not dict: if type(data) is not dict:
raise yaml.error.YAMLError(_('{file} is blank or corrupt!') raise yaml.error.YAMLError(_('{file} is blank or corrupt!')
.format(file=metadatapath)) .format(file=metadatapath))
@ -486,8 +493,7 @@ def parse_yaml_srclib(metadatapath):
_warn_or_exception(_("Invalid srclib metadata: could not " _warn_or_exception(_("Invalid srclib metadata: could not "
"parse '{file}'") "parse '{file}'")
.format(file=metadatapath) + '\n' .format(file=metadatapath) + '\n'
+ fdroidserver.common.run_yamllint(metadatapath, + common.run_yamllint(metadatapath, indent=4),
indent=4),
cause=e) cause=e)
return thisinfo return thisinfo
@ -531,13 +537,11 @@ def read_srclibs():
srclibs = {} srclibs = {}
srcdir = 'srclibs' srcdir = Path('srclibs')
if not os.path.exists(srcdir): srcdir.mkdir(exist_ok=True)
os.makedirs(srcdir)
for metadatapath in sorted(glob.glob(os.path.join(srcdir, '*.yml'))): for metadatapath in sorted(srcdir.glob('*.yml')):
srclibname = os.path.basename(metadatapath[:-4]) srclibs[metadatapath.stem] = parse_yaml_srclib(metadatapath)
srclibs[srclibname] = parse_yaml_srclib(metadatapath)
def read_metadata(appids={}, sort_by_time=False): def read_metadata(appids={}, sort_by_time=False):
@ -559,18 +563,17 @@ def read_metadata(appids={}, sort_by_time=False):
apps = OrderedDict() apps = OrderedDict()
for basedir in ('metadata', 'tmp'): for basedir in ('metadata', 'tmp'):
if not os.path.exists(basedir): Path(basedir).mkdir(exist_ok=True)
os.makedirs(basedir)
if appids: if appids:
vercodes = fdroidserver.common.read_pkg_args(appids) vercodes = common.read_pkg_args(appids)
metadatafiles = fdroidserver.common.get_metadata_files(vercodes) metadatafiles = common.get_metadata_files(vercodes)
else: else:
metadatafiles = (glob.glob(os.path.join('metadata', '*.yml')) metadatafiles = list(Path('metadata').glob('*.yml')) + list(
+ glob.glob('.fdroid.yml')) Path('.').glob('.fdroid.yml'))
if sort_by_time: if sort_by_time:
entries = ((os.stat(path).st_mtime, path) for path in metadatafiles) entries = ((path.stat().st_mtime, path) for path in metadatafiles)
metadatafiles = [] metadatafiles = []
for _ignored, path in sorted(entries, reverse=True): for _ignored, path in sorted(entries, reverse=True):
metadatafiles.append(path) metadatafiles.append(path)
@ -579,8 +582,8 @@ def read_metadata(appids={}, sort_by_time=False):
metadatafiles = sorted(metadatafiles) metadatafiles = sorted(metadatafiles)
for metadatapath in metadatafiles: for metadatapath in metadatafiles:
appid, _ignored = fdroidserver.common.get_extension(os.path.basename(metadatapath)) appid = metadatapath.stem
if appid != '.fdroid' and not fdroidserver.common.is_valid_package_name(appid): if appid != '.fdroid' and not common.is_valid_package_name(appid):
_warn_or_exception(_("{appid} from {path} is not a valid Java Package Name!") _warn_or_exception(_("{appid} from {path} is not a valid Java Package Name!")
.format(appid=appid, path=metadatapath)) .format(appid=appid, path=metadatapath))
if appid in apps: if appid in apps:
@ -684,7 +687,7 @@ def post_metadata_parse(app):
# Parse metadata for a single application. # Parse metadata for a single application.
# #
# 'metadatapath' - the filename to read. The "Application ID" aka # 'metadatapath' - the file path to read. The "Application ID" aka
# "Package Name" for the application comes from this # "Package Name" for the application comes from this
# filename. Pass None to get a blank entry. # filename. Pass None to get a blank entry.
# #
@ -729,27 +732,27 @@ def parse_metadata(metadatapath):
the source code. the source code.
""" """
metadatapath = Path(metadatapath)
app = App() app = App()
app.metadatapath = metadatapath app.metadatapath = str(PurePosixPath(metadatapath))
metadata_file = os.path.basename(metadatapath) name = metadatapath.stem
name, _ignored = fdroidserver.common.get_extension(metadata_file)
if name != '.fdroid': if name != '.fdroid':
app.id = name app.id = name
if metadatapath.endswith('.yml'): if metadatapath.suffix == '.yml':
with open(metadatapath, 'r') as mf: with metadatapath.open('r') as mf:
parse_yaml_metadata(mf, app) parse_yaml_metadata(mf, app)
else: else:
_warn_or_exception(_('Unknown metadata format: {path} (use: *.yml)') _warn_or_exception(_('Unknown metadata format: {path} (use: *.yml)')
.format(path=metadatapath)) .format(path=metadatapath))
if metadata_file != '.fdroid.yml' and app.Repo: if metadatapath.name != '.fdroid.yml' and app.Repo:
build_dir = fdroidserver.common.get_build_dir(app) build_dir = common.get_build_dir(app)
metadata_in_repo = os.path.join(build_dir, '.fdroid.yml') metadata_in_repo = build_dir / '.fdroid.yml'
if os.path.isfile(metadata_in_repo): if metadata_in_repo.is_file():
try: try:
commit_id = fdroidserver.common.get_head_commit_id(git.repo.Repo(build_dir)) # TODO: Python3.6: Should accept path-like
commit_id = common.get_head_commit_id(git.Repo(str(build_dir)))
logging.debug(_('Including metadata from %s@%s') % (metadata_in_repo, commit_id)) logging.debug(_('Including metadata from %s@%s') % (metadata_in_repo, commit_id))
except git.exc.InvalidGitRepositoryError: except git.exc.InvalidGitRepositoryError:
logging.debug(_('Including metadata from {path}').format(metadata_in_repo)) logging.debug(_('Including metadata from {path}').format(metadata_in_repo))
@ -764,11 +767,11 @@ def parse_metadata(metadatapath):
if app.get('Builds'): if app.get('Builds'):
build = app['Builds'][-1] build = app['Builds'][-1]
if build.subdir: if build.subdir:
root_dir = build.subdir root_dir = Path(build.subdir)
else: else:
root_dir = '.' root_dir = Path('.')
paths = fdroidserver.common.manifest_paths(root_dir, build.gradle) paths = common.manifest_paths(root_dir, build.gradle)
_ignored, _ignored, app.id = fdroidserver.common.parse_androidmanifests(paths, app) _ignored, _ignored, app.id = common.parse_androidmanifests(paths, app)
return app return app
@ -790,8 +793,7 @@ def parse_yaml_metadata(mf, app):
except yaml.YAMLError as e: except yaml.YAMLError as e:
_warn_or_exception(_("could not parse '{path}'") _warn_or_exception(_("could not parse '{path}'")
.format(path=mf.name) + '\n' .format(path=mf.name) + '\n'
+ fdroidserver.common.run_yamllint(mf.name, + common.run_yamllint(mf.name, indent=4),
indent=4),
cause=e) cause=e)
deprecated_in_yaml = ['Provides'] deprecated_in_yaml = ['Provides']
@ -801,7 +803,7 @@ def parse_yaml_metadata(mf, app):
if field not in yaml_app_fields + deprecated_in_yaml: if field not in yaml_app_fields + deprecated_in_yaml:
msg = (_("Unrecognised app field '{fieldname}' in '{path}'") msg = (_("Unrecognised app field '{fieldname}' in '{path}'")
.format(fieldname=field, path=mf.name)) .format(fieldname=field, path=mf.name))
if os.path.basename(mf.name) == '.fdroid.yml': if Path(mf.name).name == '.fdroid.yml':
logging.error(msg) logging.error(msg)
del yamldata[field] del yamldata[field]
else: else:
@ -978,11 +980,10 @@ build_cont = re.compile(r'^[ \t]')
def write_metadata(metadatapath, app): def write_metadata(metadatapath, app):
# TODO: Remove this metadatapath = Path(metadatapath)
metadatapath = str(metadatapath) if metadatapath.suffix == '.yml':
if metadatapath.endswith('.yml'):
if importlib.util.find_spec('ruamel.yaml'): if importlib.util.find_spec('ruamel.yaml'):
with open(metadatapath, 'w') as mf: with metadatapath.open('w') as mf:
return write_yaml(mf, app) return write_yaml(mf, app)
else: else:
raise FDroidException(_('ruamel.yaml not installed, can not write metadata.')) raise FDroidException(_('ruamel.yaml not installed, can not write metadata.'))

View File

@ -18,11 +18,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from argparse import ArgumentParser from argparse import ArgumentParser
import os
import logging import logging
import io import io
import tempfile import tempfile
import shutil import shutil
from pathlib import Path
from . import _ from . import _
from . import common from . import common
@ -36,9 +36,8 @@ def proper_format(app):
s = io.StringIO() s = io.StringIO()
# TODO: currently reading entire file again, should reuse first # TODO: currently reading entire file again, should reuse first
# read in metadata.py # read in metadata.py
with open(app.metadatapath, 'r') as f: cur_content = Path(app.metadatapath).read_text()
cur_content = f.read() if Path(app.metadatapath).suffix == '.yml':
if app.metadatapath.endswith('.yml'):
metadata.write_yaml(s, app) metadata.write_yaml(s, app)
content = s.getvalue() content = s.getvalue()
s.close() s.close()
@ -65,8 +64,8 @@ def main():
apps = common.read_app_args(options.appid, allapps, False) apps = common.read_app_args(options.appid, allapps, False)
for appid, app in apps.items(): for appid, app in apps.items():
path = app.metadatapath path = Path(app.metadatapath)
if path.endswith('.yml'): if path.suffix == '.yml':
logging.info(_("Rewriting '{appid}'").format(appid=appid)) logging.info(_("Rewriting '{appid}'").format(appid=appid))
else: else:
logging.warning(_('Cannot rewrite "{path}"').format(path=path)) logging.warning(_('Cannot rewrite "{path}"').format(path=path))
@ -91,9 +90,10 @@ def main():
# rewrite to temporary file before overwriting existsing # rewrite to temporary file before overwriting existsing
# file in case there's a bug in write_metadata # file in case there's a bug in write_metadata
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
tmp_path = os.path.join(tmpdir, os.path.basename(path)) tmp_path = Path(tmpdir) / path.name
metadata.write_metadata(tmp_path, app) metadata.write_metadata(tmp_path, app)
shutil.move(tmp_path, path) # TODO: Python3.6: Accept path-lik
shutil.move(str(tmp_path), str(path))
logging.debug(_("Finished")) logging.debug(_("Finished"))

View File

@ -67,31 +67,134 @@ class BuildTest(unittest.TestCase):
pass # aapt is not required if androguard is present pass # aapt is not required if androguard is present
testcases = [ testcases = [
('repo/obb.main.twoversions_1101613.apk', 'obb.main.twoversions', '1101613', '0.1', None), (
('org.bitbucket.tickytacky.mirrormirror_1.apk', 'org.bitbucket.tickytacky.mirrormirror', '1', '1.0', None), 'repo/obb.main.twoversions_1101613.apk',
('org.bitbucket.tickytacky.mirrormirror_2.apk', 'org.bitbucket.tickytacky.mirrormirror', '2', '1.0.1', None), 'obb.main.twoversions',
('org.bitbucket.tickytacky.mirrormirror_3.apk', 'org.bitbucket.tickytacky.mirrormirror', '3', '1.0.2', None), '1101613',
('org.bitbucket.tickytacky.mirrormirror_4.apk', 'org.bitbucket.tickytacky.mirrormirror', '4', '1.0.3', None), '0.1',
('org.dyndns.fules.ck_20.apk', 'org.dyndns.fules.ck', '20', 'v1.6pre2', None,
['arm64-v8a', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'x86', 'x86_64']), ),
(
'org.bitbucket.tickytacky.mirrormirror_1.apk',
'org.bitbucket.tickytacky.mirrormirror',
'1',
'1.0',
None,
),
(
'org.bitbucket.tickytacky.mirrormirror_2.apk',
'org.bitbucket.tickytacky.mirrormirror',
'2',
'1.0.1',
None,
),
(
'org.bitbucket.tickytacky.mirrormirror_3.apk',
'org.bitbucket.tickytacky.mirrormirror',
'3',
'1.0.2',
None,
),
(
'org.bitbucket.tickytacky.mirrormirror_4.apk',
'org.bitbucket.tickytacky.mirrormirror',
'4',
'1.0.3',
None,
),
(
'org.dyndns.fules.ck_20.apk',
'org.dyndns.fules.ck',
'20',
'v1.6pre2',
[
'arm64-v8a',
'armeabi',
'armeabi-v7a',
'mips',
'mips64',
'x86',
'x86_64',
],
),
('urzip.apk', 'info.guardianproject.urzip', '100', '0.1', None), ('urzip.apk', 'info.guardianproject.urzip', '100', '0.1', None),
('urzip-badcert.apk', 'info.guardianproject.urzip', '100', '0.1', None), ('urzip-badcert.apk', 'info.guardianproject.urzip', '100', '0.1', None),
('urzip-badsig.apk', 'info.guardianproject.urzip', '100', '0.1', None), ('urzip-badsig.apk', 'info.guardianproject.urzip', '100', '0.1', None),
('urzip-release.apk', 'info.guardianproject.urzip', '100', '0.1', None), ('urzip-release.apk', 'info.guardianproject.urzip', '100', '0.1', None),
('urzip-release-unsigned.apk', 'info.guardianproject.urzip', '100', '0.1', None), (
'urzip-release-unsigned.apk',
'info.guardianproject.urzip',
'100',
'0.1',
None,
),
('repo/com.politedroid_3.apk', 'com.politedroid', '3', '1.2', None), ('repo/com.politedroid_3.apk', 'com.politedroid', '3', '1.2', None),
('repo/com.politedroid_4.apk', 'com.politedroid', '4', '1.3', None), ('repo/com.politedroid_4.apk', 'com.politedroid', '4', '1.3', None),
('repo/com.politedroid_5.apk', 'com.politedroid', '5', '1.4', None), ('repo/com.politedroid_5.apk', 'com.politedroid', '5', '1.4', None),
('repo/com.politedroid_6.apk', 'com.politedroid', '6', '1.5', None), ('repo/com.politedroid_6.apk', 'com.politedroid', '6', '1.5', None),
('repo/duplicate.permisssions_9999999.apk', 'duplicate.permisssions', '9999999', '', None), (
('repo/info.zwanenburg.caffeinetile_4.apk', 'info.zwanenburg.caffeinetile', '4', '1.3', None), 'repo/duplicate.permisssions_9999999.apk',
('repo/obb.main.oldversion_1444412523.apk', 'obb.main.oldversion', '1444412523', '0.1', None), 'duplicate.permisssions',
('repo/obb.mainpatch.current_1619_another-release-key.apk', 'obb.mainpatch.current', '1619', '0.1', None), '9999999',
('repo/obb.mainpatch.current_1619.apk', 'obb.mainpatch.current', '1619', '0.1', None), '',
('repo/obb.main.twoversions_1101613.apk', 'obb.main.twoversions', '1101613', '0.1', None), None,
('repo/obb.main.twoversions_1101615.apk', 'obb.main.twoversions', '1101615', '0.1', None), ),
('repo/obb.main.twoversions_1101617.apk', 'obb.main.twoversions', '1101617', '0.1', None), (
('repo/urzip-; Рахма́, [rɐxˈmanʲɪnəf] سيرجي_رخمانينوف 谢·.apk', 'info.guardianproject.urzip', '100', '0.1', None), 'repo/info.zwanenburg.caffeinetile_4.apk',
'info.zwanenburg.caffeinetile',
'4',
'1.3',
None,
),
(
'repo/obb.main.oldversion_1444412523.apk',
'obb.main.oldversion',
'1444412523',
'0.1',
None,
),
(
'repo/obb.mainpatch.current_1619_another-release-key.apk',
'obb.mainpatch.current',
'1619',
'0.1',
None,
),
(
'repo/obb.mainpatch.current_1619.apk',
'obb.mainpatch.current',
'1619',
'0.1',
None,
),
(
'repo/obb.main.twoversions_1101613.apk',
'obb.main.twoversions',
'1101613',
'0.1',
None,
),
(
'repo/obb.main.twoversions_1101615.apk',
'obb.main.twoversions',
'1101615',
'0.1',
None,
),
(
'repo/obb.main.twoversions_1101617.apk',
'obb.main.twoversions',
'1101617',
'0.1',
None,
),
(
'repo/urzip-; Рахма́, [rɐxˈmanʲɪnəf] سيرجي_رخمانينوف 谢·.apk',
'info.guardianproject.urzip',
'100',
'0.1',
None,
),
] ]
for apkfilename, appid, versionCode, versionName, nativecode in testcases: for apkfilename, appid, versionCode, versionName, nativecode in testcases:
app = fdroidserver.metadata.App() app = fdroidserver.metadata.App()
@ -259,14 +362,26 @@ class BuildTest(unittest.TestCase):
with mock.patch('fdroidserver.common.replace_build_vars', wraps=make_fake_apk): with mock.patch('fdroidserver.common.replace_build_vars', wraps=make_fake_apk):
with mock.patch('fdroidserver.common.get_native_code', return_value='x86'): with mock.patch('fdroidserver.common.get_native_code', return_value='x86'):
with mock.patch('fdroidserver.common.get_apk_id', with mock.patch(
return_value=(app.id, build.versionCode, build.versionName)): 'fdroidserver.common.get_apk_id',
with mock.patch('fdroidserver.common.is_apk_and_debuggable', return_value=False): return_value=(app.id, build.versionCode, build.versionName),
):
with mock.patch(
'fdroidserver.common.is_apk_and_debuggable', return_value=False
):
fdroidserver.build.build_local( fdroidserver.build.build_local(
app, build, vcs, app,
build_dir=testdir, output_dir=testdir, build,
log_dir=None, srclib_dir=None, extlib_dir=None, tmp_dir=None, vcs,
force=False, onserver=False, refresh=False build_dir=testdir,
output_dir=testdir,
log_dir=None,
srclib_dir=None,
extlib_dir=None,
tmp_dir=None,
force=False,
onserver=False,
refresh=False,
) )
self.assertTrue(os.path.exists('foo.aar')) self.assertTrue(os.path.exists('foo.aar'))

View File

@ -220,9 +220,6 @@ class CheckupdatesTest(unittest.TestCase):
if __name__ == "__main__": if __name__ == "__main__":
# TODO: Python3.6: Accept path-like object.
os.chdir(str(Path(__file__).parent))
parser = optparse.OptionParser() parser = optparse.OptionParser()
parser.add_option( parser.add_option(
"-v", "-v",

View File

@ -121,9 +121,6 @@ class ImportTest(unittest.TestCase):
if __name__ == "__main__": if __name__ == "__main__":
# TODO: Python3.6: Support added to accept objects implementing the os.PathLike interface.
os.chdir(str(Path(__file__).parent))
parser = optparse.OptionParser() parser = optparse.OptionParser()
parser.add_option( parser.add_option(
"-v", "-v",

View File

@ -1,10 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
import io import io
import glob
import inspect
import logging import logging
import optparse import optparse
import os import os
@ -12,25 +8,27 @@ import random
import shutil import shutil
import sys import sys
import unittest import unittest
import yaml from unittest import mock
import tempfile import tempfile
import textwrap import textwrap
from collections import OrderedDict from collections import OrderedDict
from unittest import mock from pathlib import Path
import yaml
from testcommon import TmpCwd
try: try:
from yaml import CSafeLoader as SafeLoader from yaml import CSafeLoader as SafeLoader
except ImportError: except ImportError:
from yaml import SafeLoader from yaml import SafeLoader
localmodule = os.path.realpath( localmodule = Path(__file__).resolve().parent.parent
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..') print('localmodule: ' + str(localmodule))
)
print('localmodule: ' + localmodule)
if localmodule not in sys.path: if localmodule not in sys.path:
sys.path.insert(0, localmodule) sys.path.insert(0, str(localmodule))
from testcommon import TmpCwd
import fdroidserver.common import fdroidserver.common
import fdroidserver.metadata import fdroidserver.metadata
@ -42,11 +40,11 @@ class MetadataTest(unittest.TestCase):
def setUp(self): def setUp(self):
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
self.basedir = os.path.join(localmodule, 'tests') self.basedir = localmodule / 'tests'
self.tmpdir = os.path.abspath(os.path.join(self.basedir, '..', '.testfiles')) self.tmpdir = localmodule / '.testfiles'
if not os.path.exists(self.tmpdir): self.tmpdir.mkdir(exist_ok=True)
os.makedirs(self.tmpdir) # TODO: Python3.6: Accepts a path-like object.
os.chdir(self.basedir) os.chdir(str(self.basedir))
def test_FieldValidator_BitcoinAddress(self): def test_FieldValidator_BitcoinAddress(self):
validator = None validator = None
@ -59,23 +57,53 @@ class MetadataTest(unittest.TestCase):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
# some valid addresses (P2PKH, P2SH, Bech32) # some valid addresses (P2PKH, P2SH, Bech32)
self.assertIsNone(validator.check('1BrrrrErsrWetrTrnrrrrm4GFg7xJaNVN2', 'fake.app.id')) self.assertIsNone(
self.assertIsNone(validator.check('3JrrrrWrEZr3rNrrvrecrnyirrnqRhWNLy', 'fake.app.id')) validator.check('1BrrrrErsrWetrTrnrrrrm4GFg7xJaNVN2', 'fake.app.id')
self.assertIsNone(validator.check('bc1qar0srrr7xrkvr5lr43lrdnwrre5rgtrzrf5rrq', 'fake.app.id')) )
self.assertIsNone(
validator.check('3JrrrrWrEZr3rNrrvrecrnyirrnqRhWNLy', 'fake.app.id')
)
self.assertIsNone(
validator.check('bc1qar0srrr7xrkvr5lr43lrdnwrre5rgtrzrf5rrq', 'fake.app.id')
)
# some invalid addresses # some invalid addresses
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, self.assertRaises(
'21BvMrSYsrWrtrrlL5A10mlGFr7rrarrN2', 'fake.app.id') fdroidserver.exception.MetaDataException,
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, validator.check,
'5Hrgr3ur5rGLrfKrrrrrrHSrqJrroGrrzrQrrrrrrLNrsrDrrrA', 'fake.app.id') '21BvMrSYsrWrtrrlL5A10mlGFr7rrarrN2',
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, 'fake.app.id',
'92rr46rUrgTrrromrVrirW6r1rrrdrerrdbJrrrhrCsYrrrrrrc', 'fake.app.id') )
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, self.assertRaises(
'K1BvMrSYsrWrtrrrn5Au4m4GFr7rrarrN2', 'fake.app.id') fdroidserver.exception.MetaDataException,
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, validator.check,
'L1BvMrSYsrWrtrrrn5Au4m4GFr7rrarrN2', 'fake.app.id') '5Hrgr3ur5rGLrfKrrrrrrHSrqJrroGrrzrQrrrrrrLNrsrDrrrA',
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, 'fake.app.id',
'tb1qw5r8drrejxrrg4y5rrrrrraryrrrrwrkxrjrsx', 'fake.app.id') )
self.assertRaises(
fdroidserver.exception.MetaDataException,
validator.check,
'92rr46rUrgTrrromrVrirW6r1rrrdrerrdbJrrrhrCsYrrrrrrc',
'fake.app.id',
)
self.assertRaises(
fdroidserver.exception.MetaDataException,
validator.check,
'K1BvMrSYsrWrtrrrn5Au4m4GFr7rrarrN2',
'fake.app.id',
)
self.assertRaises(
fdroidserver.exception.MetaDataException,
validator.check,
'L1BvMrSYsrWrtrrrn5Au4m4GFr7rrarrN2',
'fake.app.id',
)
self.assertRaises(
fdroidserver.exception.MetaDataException,
validator.check,
'tb1qw5r8drrejxrrg4y5rrrrrraryrrrrwrkxrjrsx',
'fake.app.id',
)
def test_FieldValidator_LitecoinAddress(self): def test_FieldValidator_LitecoinAddress(self):
validator = None validator = None
@ -88,27 +116,55 @@ class MetadataTest(unittest.TestCase):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
# some valid addresses (L, M, 3) # some valid addresses (L, M, 3)
self.assertIsNone(validator.check('LgeGrrrrJAxyXprrPrrBrrX5Qrrrrrrrrd', 'fake.app.id')) self.assertIsNone(
self.assertIsNone(validator.check('MrrrrrrrJAxyXpanPtrrRAX5QHxvUJo8id', 'fake.app.id')) validator.check('LgeGrrrrJAxyXprrPrrBrrX5Qrrrrrrrrd', 'fake.app.id')
)
self.assertIsNone(
validator.check('MrrrrrrrJAxyXpanPtrrRAX5QHxvUJo8id', 'fake.app.id')
)
self.assertIsNone(validator.check('3rereVr9rAryrranrrrrrAXrrHx', 'fake.app.id')) self.assertIsNone(validator.check('3rereVr9rAryrranrrrrrAXrrHx', 'fake.app.id'))
# some invalid addresses (various special use/testnet addresses, invalid chars) # some invalid addresses (various special use/testnet addresses, invalid chars)
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, self.assertRaises(
'21BvMrSYsrWrtrrrn5Au4l4GFr7rrarrN2', 'fake.app.id') fdroidserver.exception.MetaDataException,
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, validator.check,
'5Hrgr3ur5rGLrfKrrrrrr1SrqJrroGrrzrQrrrrrrLNrsrDrrrA', 'fake.app.id') '21BvMrSYsrWrtrrrn5Au4l4GFr7rrarrN2',
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, 'fake.app.id',
'92rr46rUrgTrrromrVrirW6r1rrrdrerrdbJrrrhrCsYrrrrrrc', 'fake.app.id') )
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, self.assertRaises(
'K1BvMrSYsrWrtrrrn5Au4m4GFr7rrarrN2', 'fake.app.id') fdroidserver.exception.MetaDataException,
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, validator.check,
'L0000rSYsrWrtrrrn5Au4m4GFr7rrarrN2', 'fake.app.id') '5Hrgr3ur5rGLrfKrrrrrr1SrqJrroGrrzrQrrrrrrLNrsrDrrrA',
self.assertRaises(fdroidserver.exception.MetaDataException, validator.check, 'fake.app.id',
'tb1qw5r8drrejxrrg4y5rrrrrraryrrrrwrkxrjrsx', 'fake.app.id') )
self.assertRaises(
fdroidserver.exception.MetaDataException,
validator.check,
'92rr46rUrgTrrromrVrirW6r1rrrdrerrdbJrrrhrCsYrrrrrrc',
'fake.app.id',
)
self.assertRaises(
fdroidserver.exception.MetaDataException,
validator.check,
'K1BvMrSYsrWrtrrrn5Au4m4GFr7rrarrN2',
'fake.app.id',
)
self.assertRaises(
fdroidserver.exception.MetaDataException,
validator.check,
'L0000rSYsrWrtrrrn5Au4m4GFr7rrarrN2',
'fake.app.id',
)
self.assertRaises(
fdroidserver.exception.MetaDataException,
validator.check,
'tb1qw5r8drrejxrrg4y5rrrrrraryrrrrwrkxrjrsx',
'fake.app.id',
)
def test_valid_funding_yml_regex(self): def test_valid_funding_yml_regex(self):
"""Check the regex can find all the cases""" """Check the regex can find all the cases"""
with open(os.path.join(self.basedir, 'funding-usernames.yaml')) as fp: with (self.basedir / 'funding-usernames.yaml').open() as fp:
data = yaml.load(fp, Loader=SafeLoader) data = yaml.load(fp, Loader=SafeLoader)
for k, entries in data.items(): for k, entries in data.items():
@ -117,12 +173,15 @@ class MetadataTest(unittest.TestCase):
if k == 'custom': if k == 'custom':
pass pass
elif k == 'bad': elif k == 'bad':
self.assertIsNone(m, 'this is an invalid %s username: {%s}' % (k, entry)) self.assertIsNone(
m, 'this is an invalid %s username: {%s}' % (k, entry)
)
else: else:
self.assertIsNotNone(m, 'this is a valid %s username: {%s}' % (k, entry)) self.assertIsNotNone(
m, 'this is a valid %s username: {%s}' % (k, entry)
)
def test_read_metadata(self): def test_read_metadata(self):
def _build_yaml_representer(dumper, data): def _build_yaml_representer(dumper, data):
'''Creates a YAML representation of a Build instance''' '''Creates a YAML representation of a Build instance'''
return dumper.represent_dict(data) return dumper.represent_dict(data)
@ -141,22 +200,22 @@ class MetadataTest(unittest.TestCase):
'org.videolan.vlc', 'org.videolan.vlc',
'com.politedroid', 'com.politedroid',
): ):
savepath = os.path.join('metadata', 'dump', appid + '.yaml') savepath = Path('metadata/dump') / (appid + '.yaml')
frommeta = dict(apps[appid]) frommeta = dict(apps[appid])
self.assertTrue(appid in apps) self.assertTrue(appid in apps)
with open(savepath, 'r') as f: with savepath.open('r') as f:
from_yaml = yaml.load(f, Loader=SafeLoader) from_yaml = yaml.load(f, Loader=SafeLoader)
self.assertEqual(frommeta, from_yaml) self.assertEqual(frommeta, from_yaml)
# comment above assert and uncomment below to update test # comment above assert and uncomment below to update test
# files when new metadata fields are added # files when new metadata fields are added
# with open(savepath, 'w') as f: # with savepath.open('w') as f:
# yaml.add_representer(fdroidserver.metadata.Build, _build_yaml_representer) # yaml.add_representer(fdroidserver.metadata.Build, _build_yaml_representer)
# yaml.dump(frommeta, f, default_flow_style=False) # yaml.dump(frommeta, f, default_flow_style=False)
def test_rewrite_yaml_fakeotaupdate(self): def test_rewrite_yaml_fakeotaupdate(self):
testdir = tempfile.mkdtemp( # TODO: Pytohn3.6: The dir parameter now accepts a path-like object.
prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir with tempfile.TemporaryDirectory(dir=str(self.tmpdir)) as testdir:
) testdir = Path(testdir)
fdroidserver.common.config = {'accepted_formats': ['yml']} fdroidserver.common.config = {'accepted_formats': ['yml']}
fdroidserver.metadata.warnings_action = None fdroidserver.metadata.warnings_action = None
@ -165,19 +224,21 @@ class MetadataTest(unittest.TestCase):
for appid, app in allapps.items(): for appid, app in allapps.items():
if appid == 'fake.ota.update': if appid == 'fake.ota.update':
fdroidserver.metadata.write_metadata( fdroidserver.metadata.write_metadata(
os.path.join(testdir, appid + '.yml'), app testdir / (appid + '.yml'), app
) )
# assert rewrite result # assert rewrite result
with open(os.path.join(testdir, 'fake.ota.update.yml'), 'r') as result:
with open('metadata-rewrite-yml/fake.ota.update.yml', 'r') as orig:
self.maxDiff = None self.maxDiff = None
self.assertEqual(result.read(), orig.read()) file_name = 'fake.ota.update.yml'
self.assertEqual(
(testdir / file_name).read_text(),
(Path('metadata-rewrite-yml') / file_name).read_text(),
)
def test_rewrite_yaml_fdroidclient(self): def test_rewrite_yaml_fdroidclient(self):
testdir = tempfile.mkdtemp( # TODO: Pytohn3.6: The dir parameter now accepts a path-like object.
prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir with tempfile.TemporaryDirectory(dir=str(self.tmpdir)) as testdir:
) testdir = Path(testdir)
fdroidserver.common.config = {'accepted_formats': ['yml']} fdroidserver.common.config = {'accepted_formats': ['yml']}
# rewrite metadata # rewrite metadata
@ -185,33 +246,37 @@ class MetadataTest(unittest.TestCase):
for appid, app in allapps.items(): for appid, app in allapps.items():
if appid == 'org.fdroid.fdroid': if appid == 'org.fdroid.fdroid':
fdroidserver.metadata.write_metadata( fdroidserver.metadata.write_metadata(
os.path.join(testdir, appid + '.yml'), app testdir / (appid + '.yml'), app
) )
# assert rewrite result # assert rewrite result
with open(os.path.join(testdir, 'org.fdroid.fdroid.yml'), 'r') as result:
with open('metadata-rewrite-yml/org.fdroid.fdroid.yml', 'r') as orig:
self.maxDiff = None self.maxDiff = None
self.assertEqual(result.read(), orig.read()) file_name = 'org.fdroid.fdroid.yml'
self.assertEqual(
(testdir / file_name).read_text(),
(Path('metadata-rewrite-yml') / file_name).read_text(),
)
def test_rewrite_yaml_special_build_params(self): def test_rewrite_yaml_special_build_params(self):
testdir = tempfile.mkdtemp( # TODO: Pytohn3.6: The dir parameter now accepts a path-like object.
prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir with tempfile.TemporaryDirectory(dir=str(self.tmpdir)) as testdir:
) testdir = Path(testdir)
# rewrite metadata # rewrite metadata
allapps = fdroidserver.metadata.read_metadata() allapps = fdroidserver.metadata.read_metadata()
for appid, app in allapps.items(): for appid, app in allapps.items():
if appid == 'app.with.special.build.params': if appid == 'app.with.special.build.params':
fdroidserver.metadata.write_metadata( fdroidserver.metadata.write_metadata(
os.path.join(testdir, appid + '.yml'), app testdir / (appid + '.yml'), app
) )
# assert rewrite result # assert rewrite result
with open(os.path.join(testdir, 'app.with.special.build.params.yml'), 'r') as result:
with open('metadata-rewrite-yml/app.with.special.build.params.yml', 'r') as orig:
self.maxDiff = None self.maxDiff = None
self.assertEqual(result.read(), orig.read()) file_name = 'app.with.special.build.params.yml'
self.assertEqual(
(testdir / file_name).read_text(),
(Path('metadata-rewrite-yml') / file_name).read_text(),
)
def test_post_parse_yaml_metadata(self): def test_post_parse_yaml_metadata(self):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
@ -222,12 +287,18 @@ class MetadataTest(unittest.TestCase):
builds.append(build) builds.append(build)
build['versionCode'] = 1.1 build['versionCode'] = 1.1
self.assertRaises(fdroidserver.exception.MetaDataException, self.assertRaises(
fdroidserver.metadata.post_parse_yaml_metadata, yamldata) fdroidserver.exception.MetaDataException,
fdroidserver.metadata.post_parse_yaml_metadata,
yamldata,
)
build['versionCode'] = '1' build['versionCode'] = '1'
self.assertRaises(fdroidserver.exception.MetaDataException, self.assertRaises(
fdroidserver.metadata.post_parse_yaml_metadata, yamldata) fdroidserver.exception.MetaDataException,
fdroidserver.metadata.post_parse_yaml_metadata,
yamldata,
)
build['versionCode'] = 1 build['versionCode'] = 1
build['versionName'] = 1 build['versionName'] = 1
@ -258,25 +329,28 @@ class MetadataTest(unittest.TestCase):
self.assertEqual('1234567890', yamldata['Builds'][0]['commit']) self.assertEqual('1234567890', yamldata['Builds'][0]['commit'])
def test_read_metadata_sort_by_time(self): def test_read_metadata_sort_by_time(self):
testdir = tempfile.mkdtemp( # TODO: Pytohn3.6: The dir parameter now accepts a path-like object.
prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir with tempfile.TemporaryDirectory(dir=str(self.tmpdir)) as testdir, TmpCwd(
) testdir
metadatadir = os.path.join(testdir, 'metadata') ):
os.makedirs(metadatadir) testdir = Path(testdir)
metadatadir = testdir / 'metadata'
metadatadir.mkdir()
randomlist = [] randomlist = []
randomapps = glob.glob(os.path.join(self.basedir, 'metadata', '*.yml')) randomapps = list((self.basedir / 'metadata').glob('*.yml'))
random.shuffle(randomapps) random.shuffle(randomapps)
i = 1 i = 1
for f in randomapps: for f in randomapps:
shutil.copy(f, metadatadir) # TODO: Pytohn3.6: The parameter now accepts a path-like object.
new = os.path.join(metadatadir, os.path.basename(f)) shutil.copy(str(f), str(metadatadir))
stat = os.stat(new) new = metadatadir / f.name
os.utime(new, (stat.st_ctime, stat.st_mtime + i)) stat = new.stat()
# TODO: Changed in version 3.6: Accepts a path-like object.
os.utime(str(new), (stat.st_ctime, stat.st_mtime + i))
# prepend new item so newest is always first # prepend new item so newest is always first
randomlist = [os.path.basename(f)[:-4]] + randomlist randomlist = [f.stem] + randomlist
i += 1 i += 1
os.chdir(testdir)
allapps = fdroidserver.metadata.read_metadata(sort_by_time=True) allapps = fdroidserver.metadata.read_metadata(sort_by_time=True)
allappids = [] allappids = []
for appid, app in allapps.items(): for appid, app in allapps.items():
@ -314,12 +388,12 @@ class MetadataTest(unittest.TestCase):
fdroidserver.metadata.parse_yaml_metadata(mf, {}) fdroidserver.metadata.parse_yaml_metadata(mf, {})
def test_parse_yaml_srclib_corrupt_file(self): def test_parse_yaml_srclib_corrupt_file(self):
testdir = tempfile.mkdtemp( # TODO: Pytohn3.6: The dir parameter now accepts a path-like object.
prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir with tempfile.TemporaryDirectory(dir=str(self.tmpdir)) as testdir:
) testdir = Path(testdir)
srclibfile = os.path.join(testdir, 'srclib', 'mock.yml') srclibfile = testdir / 'srclib/mock.yml'
os.mkdir(os.path.dirname(srclibfile)) srclibfile.parent.mkdir()
with open(srclibfile, 'w') as fp: with srclibfile.open('w') as fp:
fp.write( fp.write(
textwrap.dedent( textwrap.dedent(
""" """
@ -413,9 +487,14 @@ class MetadataTest(unittest.TestCase):
with mock.patch('fdroidserver.metadata.warnings_action', 'error'): with mock.patch('fdroidserver.metadata.warnings_action', 'error'):
fdroidserver.metadata.parse_yaml_metadata(mf, result) fdroidserver.metadata.parse_yaml_metadata(mf, result)
self.maxDiff = None self.maxDiff = None
self.assertDictEqual(result, {'AutoName': 'F-Droid', self.assertDictEqual(
result,
{
'AutoName': 'F-Droid',
'RepoType': 'git', 'RepoType': 'git',
'Builds': [{'versionCode': 1, 'Builds': [
{
'versionCode': 1,
'versionName': 'v0.1.0', 'versionName': 'v0.1.0',
'sudo': "apt-get update && " 'sudo': "apt-get update && "
"apt-get install -y whatever && " "apt-get install -y whatever && "
@ -425,7 +504,11 @@ class MetadataTest(unittest.TestCase):
'prebuild': "npm something && echo 'important setting' >> /a/file", 'prebuild': "npm something && echo 'important setting' >> /a/file",
'build': "./gradlew someSpecialTask && " 'build': "./gradlew someSpecialTask && "
"sed -i 'd/that wrong config/' gradle.properties && " "sed -i 'd/that wrong config/' gradle.properties && "
"./gradlew compile"}]}) "./gradlew compile",
}
],
},
)
def test_parse_yaml_metadata_prebuild_strings(self): def test_parse_yaml_metadata_prebuild_strings(self):
mf = io.StringIO( mf = io.StringIO(
@ -451,9 +534,14 @@ class MetadataTest(unittest.TestCase):
with mock.patch('fdroidserver.metadata.warnings_action', 'error'): with mock.patch('fdroidserver.metadata.warnings_action', 'error'):
fdroidserver.metadata.parse_yaml_metadata(mf, result) fdroidserver.metadata.parse_yaml_metadata(mf, result)
self.maxDiff = None self.maxDiff = None
self.assertDictEqual(result, {'AutoName': 'F-Droid', self.assertDictEqual(
result,
{
'AutoName': 'F-Droid',
'RepoType': 'git', 'RepoType': 'git',
'Builds': [{'versionCode': 1, 'Builds': [
{
'versionCode': 1,
'versionName': 'v0.1.0', 'versionName': 'v0.1.0',
'sudo': "apt-get update && " 'sudo': "apt-get update && "
"apt-get install -y whatever && " "apt-get install -y whatever && "
@ -463,7 +551,11 @@ class MetadataTest(unittest.TestCase):
'prebuild': "npm something && echo 'important setting' >> /a/file", 'prebuild': "npm something && echo 'important setting' >> /a/file",
'build': "./gradlew someSpecialTask && " 'build': "./gradlew someSpecialTask && "
"sed -i 'd/that wrong config/' gradle.properties && " "sed -i 'd/that wrong config/' gradle.properties && "
"./gradlew compile"}]}) "./gradlew compile",
}
],
},
)
def test_parse_yaml_metadata_prebuild_string(self): def test_parse_yaml_metadata_prebuild_string(self):
mf = io.StringIO( mf = io.StringIO(
@ -484,12 +576,20 @@ class MetadataTest(unittest.TestCase):
result = {} result = {}
with mock.patch('fdroidserver.metadata.warnings_action', 'error'): with mock.patch('fdroidserver.metadata.warnings_action', 'error'):
fdroidserver.metadata.parse_yaml_metadata(mf, result) fdroidserver.metadata.parse_yaml_metadata(mf, result)
self.assertDictEqual(result, {'AutoName': 'F-Droid', self.assertDictEqual(
result,
{
'AutoName': 'F-Droid',
'RepoType': 'git', 'RepoType': 'git',
'Builds': [{'versionCode': 1, 'Builds': [
{
'versionCode': 1,
'versionName': 'v0.1.0', 'versionName': 'v0.1.0',
'prebuild': "a && b && " 'prebuild': "a && b && " "sed -i 's,a,b,'",
"sed -i 's,a,b,'"}]}) }
],
},
)
def test_parse_yaml_provides_should_be_ignored(self): def test_parse_yaml_provides_should_be_ignored(self):
mf = io.StringIO( mf = io.StringIO(
@ -596,16 +696,21 @@ class MetadataTest(unittest.TestCase):
build = fdroidserver.metadata.Build() build = fdroidserver.metadata.Build()
build.versionCode = 102030 build.versionCode = 102030
build.versionName = 'v1.2.3' build.versionName = 'v1.2.3'
build.sudo = ["apt-get update", build.sudo = [
"apt-get update",
"apt-get install -y whatever", "apt-get install -y whatever",
"sed -i -e 's/<that attr=\"bad\"/<that attr=\"good\"/' ~/.whatever/config.xml"] "sed -i -e 's/<that attr=\"bad\"/<that attr=\"good\"/' ~/.whatever/config.xml",
build.init = ["bash generate_some_file.sh", ]
"sed -i -e 'g/what/ever/' /some/file"] build.init = [
build.prebuild = ["npm something", "bash generate_some_file.sh",
"echo 'important setting' >> /a/file"] "sed -i -e 'g/what/ever/' /some/file",
build.build = ["./gradlew someSpecialTask", ]
build.prebuild = ["npm something", "echo 'important setting' >> /a/file"]
build.build = [
"./gradlew someSpecialTask",
"sed -i 'd/that wrong config/' gradle.properties", "sed -i 'd/that wrong config/' gradle.properties",
"./gradlew compile"] "./gradlew compile",
]
app['Builds'].append(build) app['Builds'].append(build)
fdroidserver.metadata.write_yaml(mf, app) fdroidserver.metadata.write_yaml(mf, app)
mf.seek(0) mf.seek(0)
@ -724,46 +829,62 @@ class MetadataTest(unittest.TestCase):
def test_parse_yaml_srclib_unknown_key(self): def test_parse_yaml_srclib_unknown_key(self):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
with open('test.yml', 'w', encoding='utf-8') as f: with Path('test.yml').open('w', encoding='utf-8') as f:
f.write(textwrap.dedent('''\ f.write(
textwrap.dedent(
'''\
RepoType: git RepoType: git
Repo: https://example.com/test.git Repo: https://example.com/test.git
Evil: I should not be here. Evil: I should not be here.
''')) '''
with self.assertRaisesRegex(MetaDataException, )
"Invalid srclib metadata: " )
"unknown key 'Evil' in " with self.assertRaisesRegex(
"'test.yml'"): MetaDataException,
fdroidserver.metadata.parse_yaml_srclib('test.yml') "Invalid srclib metadata: " "unknown key 'Evil' in " "'test.yml'",
):
fdroidserver.metadata.parse_yaml_srclib(Path('test.yml'))
def test_parse_yaml_srclib_does_not_exists(self): def test_parse_yaml_srclib_does_not_exists(self):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
with self.assertRaisesRegex(MetaDataException, with self.assertRaisesRegex(
MetaDataException,
"Invalid scrlib metadata: " "Invalid scrlib metadata: "
"'non/existent-test-srclib.yml' " r"'non(/|\\)existent-test-srclib.yml' "
"does not exist"): "does not exist",
fdroidserver.metadata.parse_yaml_srclib('non/existent-test-srclib.yml') ):
fdroidserver.metadata.parse_yaml_srclib(
Path('non/existent-test-srclib.yml')
)
def test_parse_yaml_srclib_simple(self): def test_parse_yaml_srclib_simple(self):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
with open('simple.yml', 'w', encoding='utf-8') as f: with Path('simple.yml').open('w', encoding='utf-8') as f:
f.write(textwrap.dedent('''\ f.write(
textwrap.dedent(
'''\
# this should be simple # this should be simple
RepoType: git RepoType: git
Repo: https://git.host/repo.git Repo: https://git.host/repo.git
''')) '''
srclib = fdroidserver.metadata.parse_yaml_srclib('simple.yml') )
self.assertDictEqual({'Repo': 'https://git.host/repo.git', )
srclib = fdroidserver.metadata.parse_yaml_srclib(Path('simple.yml'))
self.assertDictEqual(
{
'Repo': 'https://git.host/repo.git',
'RepoType': 'git', 'RepoType': 'git',
'Subdir': None, 'Subdir': None,
'Prepare': None}, 'Prepare': None,
srclib) },
srclib,
)
def test_parse_yaml_srclib_simple_with_blanks(self): def test_parse_yaml_srclib_simple_with_blanks(self):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
with open('simple.yml', 'w', encoding='utf-8') as f: with Path('simple.yml').open('w', encoding='utf-8') as f:
f.write( f.write(
textwrap.dedent( textwrap.dedent(
'''\ '''\
@ -779,17 +900,21 @@ class MetadataTest(unittest.TestCase):
''' '''
) )
) )
srclib = fdroidserver.metadata.parse_yaml_srclib('simple.yml') srclib = fdroidserver.metadata.parse_yaml_srclib(Path('simple.yml'))
self.assertDictEqual({'Repo': 'https://git.host/repo.git', self.assertDictEqual(
{
'Repo': 'https://git.host/repo.git',
'RepoType': 'git', 'RepoType': 'git',
'Subdir': [''], 'Subdir': [''],
'Prepare': ''}, 'Prepare': '',
srclib) },
srclib,
)
def test_parse_yaml_srclib_Changelog_cketti(self): def test_parse_yaml_srclib_Changelog_cketti(self):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
with open('Changelog-cketti.yml', 'w', encoding='utf-8') as f: with Path('Changelog-cketti.yml').open('w', encoding='utf-8') as f:
f.write( f.write(
textwrap.dedent( textwrap.dedent(
'''\ '''\
@ -801,21 +926,27 @@ class MetadataTest(unittest.TestCase):
''' '''
) )
) )
srclib = fdroidserver.metadata.parse_yaml_srclib('Changelog-cketti.yml') srclib = fdroidserver.metadata.parse_yaml_srclib(
self.assertDictEqual(srclib, Path('Changelog-cketti.yml')
{'Repo': 'https://github.com/cketti/ckChangeLog', )
self.assertDictEqual(
srclib,
{
'Repo': 'https://github.com/cketti/ckChangeLog',
'RepoType': 'git', 'RepoType': 'git',
'Subdir': ['library', 'ckChangeLog/src/main'], 'Subdir': ['library', 'ckChangeLog/src/main'],
'Prepare': "[ -f project.properties ] || echo 'source.dir=java' > " 'Prepare': "[ -f project.properties ] || echo 'source.dir=java' > "
"ant.properties && echo -e " "ant.properties && echo -e "
"'android.library=true\\ntarget=android-19' > project.properties"}) "'android.library=true\\ntarget=android-19' > project.properties",
},
)
def test_read_srclibs_yml_subdir_list(self): def test_read_srclibs_yml_subdir_list(self):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
fdroidserver.metadata.srclibs = None fdroidserver.metadata.srclibs = None
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
os.mkdir('srclibs') Path('srclibs').mkdir()
with open('srclibs/with-list.yml', 'w', encoding='utf-8') as f: with Path('srclibs/with-list.yml').open('w', encoding='utf-8') as f:
f.write( f.write(
textwrap.dedent( textwrap.dedent(
'''\ '''\
@ -839,25 +970,33 @@ class MetadataTest(unittest.TestCase):
) )
fdroidserver.metadata.read_srclibs() fdroidserver.metadata.read_srclibs()
self.maxDiff = None self.maxDiff = None
self.assertDictEqual(fdroidserver.metadata.srclibs, self.assertDictEqual(
{'with-list': {'RepoType': 'git', fdroidserver.metadata.srclibs,
{
'with-list': {
'RepoType': 'git',
'Repo': 'https://git.host/repo.git', 'Repo': 'https://git.host/repo.git',
'Subdir': ['This is your last chance.', 'Subdir': [
'This is your last chance.',
'After this, there is no turning back.', 'After this, there is no turning back.',
'You take the blue pill—the story ends,', 'You take the blue pill—the story ends,',
'you wake up in your bed', 'you wake up in your bed',
'and believe whatever you want to believe.', 'and believe whatever you want to believe.',
'You take the red pill—you stay in Wonderland', 'You take the red pill—you stay in Wonderland',
'and I show you how deep the rabbit-hole goes.'], 'and I show you how deep the rabbit-hole goes.',
],
'Prepare': 'There is a difference between knowing the path ' 'Prepare': 'There is a difference between knowing the path '
'and walking the path.'}}) 'and walking the path.',
}
},
)
def test_read_srclibs_yml_prepare_list(self): def test_read_srclibs_yml_prepare_list(self):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
fdroidserver.metadata.srclibs = None fdroidserver.metadata.srclibs = None
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
os.mkdir('srclibs') Path('srclibs').mkdir()
with open('srclibs/with-list.yml', 'w', encoding='utf-8') as f: with Path('srclibs/with-list.yml').open('w', encoding='utf-8') as f:
f.write( f.write(
textwrap.dedent( textwrap.dedent(
'''\ '''\
@ -880,8 +1019,11 @@ class MetadataTest(unittest.TestCase):
) )
fdroidserver.metadata.read_srclibs() fdroidserver.metadata.read_srclibs()
self.maxDiff = None self.maxDiff = None
self.assertDictEqual(fdroidserver.metadata.srclibs, self.assertDictEqual(
{'with-list': {'RepoType': 'git', fdroidserver.metadata.srclibs,
{
'with-list': {
'RepoType': 'git',
'Repo': 'https://git.host/repo.git', 'Repo': 'https://git.host/repo.git',
'Subdir': [''], 'Subdir': [''],
'Prepare': 'The Matrix is a system, Neo. && ' 'Prepare': 'The Matrix is a system, Neo. && '
@ -891,14 +1033,17 @@ class MetadataTest(unittest.TestCase):
'The very minds of the people we are trying to save. && ' 'The very minds of the people we are trying to save. && '
'But until we do, these people are still a part of that system and that makes them our enemy. && ' 'But until we do, these people are still a part of that system and that makes them our enemy. && '
'You have to understand, most of these people are not ready to be unplugged. && ' 'You have to understand, most of these people are not ready to be unplugged. && '
'And many of them are so inert, so hopelessly dependent on the system that they will fight to protect it.'}}) 'And many of them are so inert, so hopelessly dependent on the system that they will fight to protect it.',
}
},
)
def test_read_srclibs(self): def test_read_srclibs(self):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
fdroidserver.metadata.srclibs = None fdroidserver.metadata.srclibs = None
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
os.mkdir('srclibs') Path('srclibs').mkdir()
with open('srclibs/simple.yml', 'w', encoding='utf-8') as f: with Path('srclibs/simple.yml').open('w', encoding='utf-8') as f:
f.write( f.write(
textwrap.dedent( textwrap.dedent(
'''\ '''\
@ -907,7 +1052,7 @@ class MetadataTest(unittest.TestCase):
''' '''
) )
) )
with open('srclibs/simple-wb.yml', 'w', encoding='utf-8') as f: with Path('srclibs/simple-wb.yml').open('w', encoding='utf-8') as f:
f.write( f.write(
textwrap.dedent( textwrap.dedent(
'''\ '''\
@ -921,20 +1066,26 @@ class MetadataTest(unittest.TestCase):
) )
) )
fdroidserver.metadata.read_srclibs() fdroidserver.metadata.read_srclibs()
self.assertDictEqual(fdroidserver.metadata.srclibs, self.assertDictEqual(
{'simple-wb': {'RepoType': 'git', fdroidserver.metadata.srclibs,
{
'simple-wb': {
'RepoType': 'git',
'Repo': 'https://git.host/repo.git', 'Repo': 'https://git.host/repo.git',
'Subdir': [''], 'Subdir': [''],
'Prepare': ''}, 'Prepare': '',
'simple': {'RepoType': 'git', },
'simple': {
'RepoType': 'git',
'Repo': 'https://git.host/repo.git', 'Repo': 'https://git.host/repo.git',
'Subdir': None, 'Subdir': None,
'Prepare': None}}) 'Prepare': None,
},
},
)
if __name__ == "__main__": if __name__ == "__main__":
os.chdir(os.path.dirname(__file__))
parser = optparse.OptionParser() parser = optparse.OptionParser()
parser.add_option( parser.add_option(
"-v", "-v",

View File

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import inspect
import logging import logging
import optparse import optparse
import os import os
@ -9,14 +8,15 @@ import unittest
import tempfile import tempfile
import textwrap import textwrap
from unittest import mock from unittest import mock
from pathlib import Path
from testcommon import TmpCwd from testcommon import TmpCwd
localmodule = os.path.realpath( localmodule = Path(__file__).resolve().parent.parent
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..') print('localmodule: ' + str(localmodule))
)
print('localmodule: ' + localmodule)
if localmodule not in sys.path: if localmodule not in sys.path:
sys.path.insert(0, localmodule) sys.path.insert(0, str(localmodule))
from fdroidserver import common from fdroidserver import common
from fdroidserver import rewritemeta from fdroidserver import rewritemeta
@ -28,28 +28,27 @@ class RewriteMetaTest(unittest.TestCase):
def setUp(self): def setUp(self):
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
self.basedir = os.path.join(localmodule, 'tests') self.basedir = localmodule / 'tests'
self.tmpdir = os.path.abspath(os.path.join(self.basedir, '..', '.testfiles')) self.tmpdir = localmodule / '.testfiles'
if not os.path.exists(self.tmpdir): self.tmpdir.mkdir(exist_ok=True)
os.makedirs(self.tmpdir) # TODO: Python3.6: Accepts a path-like object.
os.chdir(self.basedir) os.chdir(str(self.basedir))
def test_rewrite_scenario_trivial(self): def test_rewrite_scenario_trivial(self):
sys.argv = ['rewritemeta', 'a', 'b'] sys.argv = ['rewritemeta', 'a', 'b']
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
os.mkdir('metadata') Path('metadata').mkdir()
with open('metadata/a.yml', 'w') as f: with Path('metadata/a.yml').open('w') as f:
f.write('AutoName: a') f.write('AutoName: a')
with open('metadata/b.yml', 'w') as f: with Path('metadata/b.yml').open('w') as f:
f.write('AutoName: b') f.write('AutoName: b')
rewritemeta.main() rewritemeta.main()
with open('metadata/a.yml') as f:
self.assertEqual( self.assertEqual(
f.read(), Path('metadata/a.yml').read_text(),
textwrap.dedent( textwrap.dedent(
'''\ '''\
License: Unknown License: Unknown
@ -62,9 +61,8 @@ class RewriteMetaTest(unittest.TestCase):
), ),
) )
with open('metadata/b.yml') as f:
self.assertEqual( self.assertEqual(
f.read(), Path('metadata/b.yml').read_text(),
textwrap.dedent( textwrap.dedent(
'''\ '''\
License: Unknown License: Unknown
@ -80,8 +78,8 @@ class RewriteMetaTest(unittest.TestCase):
def test_rewrite_scenario_yml_no_ruamel(self): def test_rewrite_scenario_yml_no_ruamel(self):
sys.argv = ['rewritemeta', 'a'] sys.argv = ['rewritemeta', 'a']
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir): with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
os.mkdir('metadata') Path('metadata').mkdir()
with open('metadata/a.yml', 'w') as f: with Path('metadata/a.yml').open('w') as f:
f.write('AutoName: a') f.write('AutoName: a')
def boom(*args): def boom(*args):
@ -91,19 +89,12 @@ class RewriteMetaTest(unittest.TestCase):
with self.assertRaises(FDroidException): with self.assertRaises(FDroidException):
rewritemeta.main() rewritemeta.main()
with open('metadata/a.yml') as f:
self.assertEqual( self.assertEqual(
f.read(), Path('metadata/a.yml').read_text(), 'AutoName: a'
textwrap.dedent(
'''\
AutoName: a'''
),
) )
if __name__ == "__main__": if __name__ == "__main__":
os.chdir(os.path.dirname(__file__))
parser = optparse.OptionParser() parser = optparse.OptionParser()
parser.add_option( parser.add_option(
"-v", "-v",