mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-11-15 03:20:10 +01:00
484 lines
19 KiB
Python
Executable File
484 lines
19 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
|
|
|
|
import git
|
|
import logging
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
import unittest
|
|
from unittest import mock
|
|
from pathlib import Path
|
|
|
|
|
|
localmodule = Path(__file__).resolve().parent.parent
|
|
print('localmodule: ' + str(localmodule))
|
|
if localmodule not in sys.path:
|
|
sys.path.insert(0, str(localmodule))
|
|
|
|
import fdroidserver.checkupdates
|
|
import fdroidserver.metadata
|
|
from fdroidserver.exception import FDroidException
|
|
|
|
|
|
class CheckupdatesTest(unittest.TestCase):
|
|
'''fdroidserver/checkupdates.py'''
|
|
|
|
def setUp(self):
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
self.basedir = localmodule / 'tests'
|
|
os.chdir(self.basedir)
|
|
self.testdir = tempfile.TemporaryDirectory(
|
|
str(time.time()), self._testMethodName + '_'
|
|
)
|
|
|
|
def tearDown(self):
|
|
self.testdir.cleanup()
|
|
|
|
def test_autoupdatemode_no_suffix(self):
|
|
fdroidserver.checkupdates.config = {}
|
|
|
|
app = fdroidserver.metadata.App()
|
|
app.id = 'loop.starts.shooting'
|
|
app.metadatapath = 'metadata/' + app.id + '.yml'
|
|
app.CurrentVersion = '1.1.8-fdroid'
|
|
app.CurrentVersionCode = 10108
|
|
app.UpdateCheckMode = 'HTTP'
|
|
app.AutoUpdateMode = 'Version %v'
|
|
|
|
build = fdroidserver.metadata.Build()
|
|
build.versionCode = app.CurrentVersionCode
|
|
build.versionName = app.CurrentVersion
|
|
app['Builds'].append(build)
|
|
|
|
with mock.patch(
|
|
'fdroidserver.checkupdates.check_http', lambda app: ('1.1.9', 10109)
|
|
):
|
|
with mock.patch('fdroidserver.metadata.write_metadata', mock.Mock()):
|
|
with mock.patch('subprocess.call', lambda cmd: 0):
|
|
fdroidserver.checkupdates.checkupdates_app(app, auto=True)
|
|
|
|
build = app['Builds'][-1]
|
|
self.assertEqual(build.versionName, '1.1.9')
|
|
self.assertEqual(build.commit, '1.1.9')
|
|
|
|
with mock.patch(
|
|
'fdroidserver.checkupdates.check_http', lambda app: ('1.7.9', 10107)
|
|
):
|
|
with mock.patch('fdroidserver.metadata.write_metadata', mock.Mock()):
|
|
with mock.patch('subprocess.call', lambda cmd: 0):
|
|
with self.assertRaises(FDroidException):
|
|
fdroidserver.checkupdates.checkupdates_app(app, auto=True)
|
|
|
|
build = app['Builds'][-1]
|
|
self.assertEqual(build.versionName, '1.1.9')
|
|
self.assertEqual(build.commit, '1.1.9')
|
|
|
|
def test_autoupdatemode_suffix(self):
|
|
fdroidserver.checkupdates.config = {}
|
|
|
|
app = fdroidserver.metadata.App()
|
|
app.id = 'loop.starts.shooting'
|
|
app.metadatapath = 'metadata/' + app.id + '.yml'
|
|
app.CurrentVersion = '1.1.8-fdroid'
|
|
app.CurrentVersionCode = 10108
|
|
app.UpdateCheckMode = 'HTTP'
|
|
app.AutoUpdateMode = r'Version +.%c-fdroid v%v_%c'
|
|
|
|
build = fdroidserver.metadata.Build()
|
|
build.versionCode = app.CurrentVersionCode
|
|
build.versionName = app.CurrentVersion
|
|
app['Builds'].append(build)
|
|
|
|
with mock.patch(
|
|
'fdroidserver.checkupdates.check_http', lambda app: ('1.1.9', 10109)
|
|
):
|
|
with mock.patch('fdroidserver.metadata.write_metadata', mock.Mock()):
|
|
with mock.patch('subprocess.call', lambda cmd: 0):
|
|
fdroidserver.checkupdates.checkupdates_app(app, auto=True)
|
|
|
|
build = app['Builds'][-1]
|
|
self.assertEqual(build.versionName, '1.1.9.10109-fdroid')
|
|
self.assertEqual(build.commit, 'v1.1.9_10109')
|
|
|
|
def test_autoupdate_multi_variants(self):
|
|
fdroidserver.checkupdates.config = {}
|
|
|
|
app = fdroidserver.metadata.App()
|
|
app.id = 'loop.starts.shooting'
|
|
app.metadatapath = 'metadata/' + app.id + '.yml'
|
|
app.CurrentVersion = '1.1.8'
|
|
app.CurrentVersionCode = 101083
|
|
app.UpdateCheckMode = 'Tags'
|
|
app.AutoUpdateMode = r'Version'
|
|
app.VercodeOperation = [
|
|
"10*%c+1",
|
|
"10*%c+3",
|
|
]
|
|
|
|
build = fdroidserver.metadata.Build()
|
|
build.versionCode = app.CurrentVersionCode - 2
|
|
build.versionName = app.CurrentVersion
|
|
build.gradle = ["arm"]
|
|
app['Builds'].append(build)
|
|
|
|
build = fdroidserver.metadata.Build()
|
|
build.versionCode = app.CurrentVersionCode
|
|
build.versionName = app.CurrentVersion
|
|
build.gradle = ["x86"]
|
|
app['Builds'].append(build)
|
|
|
|
with mock.patch(
|
|
'fdroidserver.checkupdates.check_tags',
|
|
lambda app, pattern: ('1.1.9', 10109, 'v1.1.9'),
|
|
):
|
|
with mock.patch('fdroidserver.metadata.write_metadata', mock.Mock()):
|
|
with mock.patch('subprocess.call', lambda cmd: 0):
|
|
fdroidserver.checkupdates.checkupdates_app(app, auto=True)
|
|
|
|
build = app['Builds'][-2]
|
|
self.assertEqual(build.versionName, '1.1.9')
|
|
self.assertEqual(build.versionCode, 101091)
|
|
self.assertEqual(build.gradle, ["arm"])
|
|
|
|
build = app['Builds'][-1]
|
|
self.assertEqual(build.versionName, '1.1.9')
|
|
self.assertEqual(build.versionCode, 101093)
|
|
self.assertEqual(build.gradle, ["x86"])
|
|
|
|
self.assertEqual(app.CurrentVersion, '1.1.9')
|
|
self.assertEqual(app.CurrentVersionCode, 101093)
|
|
|
|
def test_checkupdates_app_http(self):
|
|
fdroidserver.checkupdates.config = {}
|
|
|
|
app = fdroidserver.metadata.App()
|
|
app.id = 'loop.starts.shooting'
|
|
app.metadatapath = 'metadata/' + app.id + '.yml'
|
|
app.CurrentVersionCode = 10108
|
|
app.UpdateCheckMode = 'HTTP'
|
|
app.UpdateCheckData = 'mock'
|
|
|
|
with mock.patch(
|
|
'fdroidserver.checkupdates.check_http', lambda app: (None, 'bla')
|
|
):
|
|
with self.assertRaises(FDroidException):
|
|
fdroidserver.checkupdates.checkupdates_app(app, auto=True)
|
|
|
|
with mock.patch(
|
|
'fdroidserver.checkupdates.check_http', lambda app: ('1.1.9', 10109)
|
|
):
|
|
with mock.patch(
|
|
'fdroidserver.metadata.write_metadata', mock.Mock()
|
|
) as wrmock:
|
|
with mock.patch('subprocess.call', lambda cmd: 0):
|
|
fdroidserver.checkupdates.checkupdates_app(app, auto=True)
|
|
wrmock.assert_called_with(app.metadatapath, app)
|
|
|
|
def test_checkupdates_app_tags(self):
|
|
fdroidserver.checkupdates.config = {}
|
|
|
|
app = fdroidserver.metadata.App()
|
|
app.id = 'loop.starts.shooting'
|
|
app.metadatapath = 'metadata/' + app.id + '.yml'
|
|
app.CurrentVersion = '1.1.8'
|
|
app.CurrentVersionCode = 10108
|
|
app.UpdateCheckMode = 'Tags'
|
|
app.AutoUpdateMode = 'Version'
|
|
|
|
build = fdroidserver.metadata.Build()
|
|
build.versionCode = app.CurrentVersionCode
|
|
build.versionName = app.CurrentVersion
|
|
app['Builds'].append(build)
|
|
|
|
with mock.patch(
|
|
'fdroidserver.checkupdates.check_tags',
|
|
lambda app, pattern: (None, 'bla', None),
|
|
):
|
|
with self.assertRaises(FDroidException):
|
|
fdroidserver.checkupdates.checkupdates_app(app, auto=True)
|
|
|
|
with mock.patch(
|
|
'fdroidserver.checkupdates.check_tags',
|
|
lambda app, pattern: ('1.1.9', 10109, 'v1.1.9'),
|
|
):
|
|
with mock.patch('fdroidserver.metadata.write_metadata', mock.Mock()):
|
|
with mock.patch('subprocess.call', lambda cmd: 0):
|
|
fdroidserver.checkupdates.checkupdates_app(app, auto=True)
|
|
|
|
build = app['Builds'][-1]
|
|
self.assertEqual(build.versionName, '1.1.9')
|
|
self.assertEqual(build.commit, 'v1.1.9')
|
|
|
|
def test_check_http(self):
|
|
app = fdroidserver.metadata.App()
|
|
app.id = 'loop.starts.shooting'
|
|
app.metadatapath = 'metadata/' + app.id + '.yml'
|
|
app.CurrentVersionCode = 10108
|
|
app.UpdateCheckMode = 'HTTP'
|
|
app.UpdateCheckData = r'https://a.net/b.txt|c(.*)|https://d.net/e.txt|v(.*)'
|
|
app.UpdateCheckIgnore = 'beta'
|
|
|
|
respmock = mock.Mock()
|
|
respmock.read = lambda: 'v1.1.9\nc10109'.encode('utf-8')
|
|
with mock.patch('urllib.request.urlopen', lambda a, b, c: respmock):
|
|
vername, vercode = fdroidserver.checkupdates.check_http(app)
|
|
self.assertEqual(vername, '1.1.9')
|
|
self.assertEqual(vercode, 10109)
|
|
|
|
def test_check_http_blocks_unknown_schemes(self):
|
|
app = fdroidserver.metadata.App()
|
|
for scheme in ('file', 'ssh', 'http', ';pwn'):
|
|
app.id = scheme
|
|
faked = scheme + '://fake.url/for/testing/scheme'
|
|
app.UpdateCheckData = faked + '|ignored|' + faked + '|ignored'
|
|
app.metadatapath = 'metadata/' + app.id + '.yml'
|
|
with self.assertRaises(FDroidException):
|
|
fdroidserver.checkupdates.check_http(app)
|
|
|
|
def test_check_http_ignore(self):
|
|
app = fdroidserver.metadata.App()
|
|
app.id = 'loop.starts.shooting'
|
|
app.metadatapath = 'metadata/' + app.id + '.yml'
|
|
app.CurrentVersionCode = 10108
|
|
app.UpdateCheckMode = 'HTTP'
|
|
app.UpdateCheckData = r'https://a.net/b.txt|c(.*)|https://d.net/e.txt|v(.*)'
|
|
app.UpdateCheckIgnore = 'beta'
|
|
|
|
respmock = mock.Mock()
|
|
respmock.read = lambda: 'v1.1.9-beta\nc10109'.encode('utf-8')
|
|
with mock.patch('urllib.request.urlopen', lambda a, b, c: respmock):
|
|
vername, vercode = fdroidserver.checkupdates.check_http(app)
|
|
self.assertEqual(vername, None)
|
|
|
|
def test_check_tags_data(self):
|
|
app = fdroidserver.metadata.App()
|
|
app.id = 'loop.starts.shooting'
|
|
app.metadatapath = 'metadata/' + app.id + '.yml'
|
|
app.RepoType = 'git'
|
|
app.CurrentVersionCode = 10108
|
|
app.UpdateCheckMode = 'Tags'
|
|
app.UpdateCheckData = r'b.txt|c(.*)|e.txt|v(.*)'
|
|
|
|
vcs = mock.Mock()
|
|
vcs.latesttags.return_value = ['1.1.9', '1.1.8']
|
|
with mock.patch(
|
|
'pathlib.Path.read_text', lambda a: 'v1.1.9\nc10109'
|
|
) as _ignored, mock.patch.object(Path, 'is_file') as mock_path, mock.patch(
|
|
'fdroidserver.common.getvcs', return_value=vcs
|
|
):
|
|
_ignored # silence the linters
|
|
mock_path.is_file.return_falue = True
|
|
vername, vercode, _tag = fdroidserver.checkupdates.check_tags(app, None)
|
|
self.assertEqual(vername, '1.1.9')
|
|
self.assertEqual(vercode, 10109)
|
|
|
|
app.UpdateCheckData = r'b.txt|c(.*)|.|v(.*)'
|
|
with mock.patch(
|
|
'pathlib.Path.read_text', lambda a: 'v1.1.0\nc10109'
|
|
) as _ignored, mock.patch.object(Path, 'is_file') as mock_path, mock.patch(
|
|
'fdroidserver.common.getvcs', return_value=vcs
|
|
):
|
|
_ignored # silence the linters
|
|
mock_path.is_file.return_falue = True
|
|
vername, vercode, _tag = fdroidserver.checkupdates.check_tags(app, None)
|
|
self.assertEqual(vername, '1.1.0')
|
|
self.assertEqual(vercode, 10109)
|
|
|
|
app.UpdateCheckData = r'b.txt|c(.*)||'
|
|
with mock.patch(
|
|
'pathlib.Path.read_text', lambda a: 'v1.1.9\nc10109'
|
|
) as _ignored, mock.patch.object(Path, 'is_file') as mock_path, mock.patch(
|
|
'fdroidserver.common.getvcs', return_value=vcs
|
|
):
|
|
_ignored # silence the linters
|
|
mock_path.is_file.return_falue = True
|
|
vername, vercode, _tag = fdroidserver.checkupdates.check_tags(app, None)
|
|
self.assertEqual(vername, '1.1.9')
|
|
self.assertEqual(vercode, 10109)
|
|
|
|
vcs.latesttags.return_value = ['Android-1.1.0', '1.1.8']
|
|
app.UpdateCheckData = r'b.txt|c(.*)||Android-([\d.]+)'
|
|
with mock.patch(
|
|
'pathlib.Path.read_text', lambda a: 'v1.1.9\nc10109'
|
|
) as _ignored, mock.patch.object(Path, 'is_file') as mock_path, mock.patch(
|
|
'fdroidserver.common.getvcs', return_value=vcs
|
|
):
|
|
_ignored # silence the linters
|
|
mock_path.is_file.return_falue = True
|
|
vername, vercode, _tag = fdroidserver.checkupdates.check_tags(app, None)
|
|
self.assertEqual(vername, '1.1.0')
|
|
self.assertEqual(vercode, 10109)
|
|
|
|
app.UpdateCheckData = r'|\+(\d+)||Android-([\d.]+)'
|
|
vcs.latesttags.return_value = ['Android-1.1.0+1']
|
|
with mock.patch('fdroidserver.common.getvcs', return_value=vcs):
|
|
vername, vercode, _tag = fdroidserver.checkupdates.check_tags(app, None)
|
|
self.assertEqual(vername, '1.1.0')
|
|
self.assertEqual(vercode, 1)
|
|
|
|
app.UpdateCheckData = '|||'
|
|
vcs.latesttags.return_value = ['2']
|
|
with mock.patch('fdroidserver.common.getvcs', return_value=vcs):
|
|
vername, vercode, _tag = fdroidserver.checkupdates.check_tags(app, None)
|
|
self.assertEqual(vername, '2')
|
|
self.assertEqual(vercode, 2)
|
|
|
|
def _get_test_git_repos(self):
|
|
testdir = self.testdir.name
|
|
os.chdir(testdir)
|
|
os.mkdir('metadata')
|
|
for f in (self.basedir / 'metadata').glob('*.yml'):
|
|
shutil.copy(f, 'metadata')
|
|
git_repo = git.Repo.init(testdir)
|
|
git_repo.git.add(all=True)
|
|
git_repo.index.commit("all metadata files")
|
|
|
|
git_remote_upstream = os.path.join(testdir, 'git_remote_upstream')
|
|
upstream_repo = git.Repo.init(git_remote_upstream, bare=True)
|
|
with upstream_repo.config_writer() as cw:
|
|
cw.set_value('receive', 'advertisePushOptions', True)
|
|
git_repo.create_remote('upstream', 'file://' + git_remote_upstream)
|
|
|
|
git_remote_origin = os.path.join(testdir, 'git_remote_origin')
|
|
origin_repo = git.Repo.init(git_remote_origin, bare=True)
|
|
with origin_repo.config_writer() as cw:
|
|
cw.set_value('receive', 'advertisePushOptions', True)
|
|
git_repo.create_remote('origin', 'file://' + git_remote_origin)
|
|
|
|
return git_repo, origin_repo, upstream_repo
|
|
|
|
def test_push_commits(self):
|
|
git_repo, origin_repo, upstream_repo = self._get_test_git_repos()
|
|
for remote in git_repo.remotes:
|
|
remote.push(git_repo.active_branch)
|
|
self.assertEqual(git_repo.head, upstream_repo.head)
|
|
self.assertEqual(origin_repo.head, upstream_repo.head)
|
|
# pretend that checkupdates ran but didn't create any new commits
|
|
fdroidserver.checkupdates.push_commits()
|
|
|
|
appid = 'org.adaway'
|
|
self.assertNotIn(appid, git_repo.branches)
|
|
self.assertNotIn(appid, origin_repo.branches)
|
|
self.assertNotIn(appid, upstream_repo.branches)
|
|
self.assertNotIn('checkupdates', git_repo.branches)
|
|
|
|
# now make commit
|
|
app = fdroidserver.metadata.read_metadata({appid: -1})[appid]
|
|
build = fdroidserver.metadata.Build()
|
|
build.versionName = 'fake'
|
|
build.versionCode = 999999999
|
|
app.Builds.append(build)
|
|
metadata_file = 'metadata/%s.yml' % appid
|
|
fdroidserver.metadata.write_metadata(metadata_file, app)
|
|
git_repo.index.add(metadata_file)
|
|
git_repo.index.commit('changed ' + appid)
|
|
|
|
# and push the new commit to the dynamic branch
|
|
fdroidserver.checkupdates.push_commits()
|
|
self.assertIn(appid, git_repo.branches)
|
|
self.assertIn(appid, git_repo.remotes.origin.refs)
|
|
self.assertNotIn('checkupdates', git_repo.branches)
|
|
self.assertNotIn(appid, git_repo.remotes.upstream.refs)
|
|
|
|
def test_push_commits_verbose(self):
|
|
class Options:
|
|
verbose = True
|
|
|
|
fdroidserver.checkupdates.options = Options
|
|
repos = self._get_test_git_repos()
|
|
git_repo = repos[0]
|
|
git_repo.remotes.origin.push(git_repo.active_branch)
|
|
git_repo.remotes.upstream.push(git_repo.active_branch)
|
|
|
|
# make commit
|
|
appid = 'org.adaway'
|
|
app = fdroidserver.metadata.read_metadata({appid: -1})[appid]
|
|
build = fdroidserver.metadata.Build()
|
|
build.versionName = 'fake'
|
|
build.versionCode = 999999999
|
|
app.Builds.append(build)
|
|
metadata_file = 'metadata/%s.yml' % appid
|
|
fdroidserver.metadata.write_metadata(metadata_file, app)
|
|
git_repo.index.add(metadata_file)
|
|
git_repo.index.commit('changed ' + appid)
|
|
|
|
# and push the new commit to the dynamic branch
|
|
fdroidserver.checkupdates.push_commits()
|
|
self.assertIn(appid, git_repo.branches)
|
|
self.assertIn(appid, git_repo.remotes.origin.refs)
|
|
|
|
def test_prune_empty_appid_branches(self):
|
|
git_repo, origin_repo, upstream_repo = self._get_test_git_repos()
|
|
for remote in git_repo.remotes:
|
|
remote.push(git_repo.active_branch)
|
|
self.assertEqual(git_repo.head, upstream_repo.head)
|
|
self.assertEqual(origin_repo.head, upstream_repo.head)
|
|
|
|
appid = 'org.adaway'
|
|
git_repo.create_head(appid, force=True)
|
|
git_repo.remotes.origin.push(appid, force=True)
|
|
self.assertIn(appid, git_repo.branches)
|
|
self.assertIn(appid, origin_repo.branches)
|
|
self.assertIn(appid, git_repo.remotes.origin.refs)
|
|
self.assertNotIn(appid, git_repo.remotes.upstream.refs)
|
|
fdroidserver.checkupdates.prune_empty_appid_branches()
|
|
self.assertNotIn(appid, origin_repo.branches)
|
|
self.assertNotIn(appid, git_repo.remotes.origin.refs)
|
|
self.assertNotIn(appid, git_repo.remotes.upstream.refs)
|
|
|
|
@mock.patch('sys.exit')
|
|
@mock.patch('fdroidserver.metadata.read_metadata')
|
|
def test_merge_requests_flag(self, read_metadata, sys_exit):
|
|
def _sys_exit(return_code=0):
|
|
assert return_code != 0
|
|
raise fdroidserver.exception.FDroidException('sys.exit() ran')
|
|
|
|
def _read_metadata(a=None, b=None):
|
|
raise StopIteration('read_metadata() ran, test is successful')
|
|
|
|
appid = 'com.example'
|
|
# read_metadata.return_value = dict() # {appid: dict()}
|
|
read_metadata.side_effect = _read_metadata
|
|
sys_exit.side_effect = _sys_exit
|
|
|
|
# set up clean git repo
|
|
os.chdir(self.testdir.name)
|
|
git_repo = git.Repo.init()
|
|
open('foo', 'w').close()
|
|
git_repo.git.add(all=True)
|
|
git_repo.index.commit("all files")
|
|
|
|
with mock.patch('sys.argv', ['fdroid checkupdates', '--merge-request']):
|
|
with self.assertRaises(fdroidserver.exception.FDroidException):
|
|
fdroidserver.checkupdates.main()
|
|
sys_exit.assert_called()
|
|
|
|
sys_exit.reset_mock()
|
|
with mock.patch('sys.argv', ['fdroid checkupdates', '--merge-request', appid]):
|
|
with self.assertRaises(StopIteration):
|
|
fdroidserver.checkupdates.main()
|
|
sys_exit.assert_not_called()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
from testcommon import parse_args_for_test
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
"-v",
|
|
"--verbose",
|
|
action="store_true",
|
|
default=False,
|
|
help="Spew out even more information than normal",
|
|
)
|
|
parse_args_for_test(parser, sys.argv)
|
|
|
|
newSuite = unittest.TestSuite()
|
|
newSuite.addTest(unittest.makeSuite(CheckupdatesTest))
|
|
unittest.main(failfast=False)
|