mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-11-14 11:00:10 +01:00
18f3acc32e
There is no longer any reason for these to be intertwined. This deliberately avoids touching some files as much as possible because they are super tangled and due to be replaced. Those files are: * fdroidserver/build.py * fdroidserver/update.py # Conflicts: # tests/testcommon.py # Conflicts: # fdroidserver/btlog.py # fdroidserver/import_subcommand.py
1121 lines
43 KiB
Python
Executable File
1121 lines
43 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
|
||
# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
|
||
|
||
import inspect
|
||
import logging
|
||
import os
|
||
import shutil
|
||
import sys
|
||
import tempfile
|
||
import textwrap
|
||
import unittest
|
||
import yaml
|
||
from pathlib import Path
|
||
from unittest import mock
|
||
|
||
localmodule = os.path.realpath(
|
||
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..')
|
||
)
|
||
print('localmodule: ' + localmodule)
|
||
if localmodule not in sys.path:
|
||
sys.path.insert(0, localmodule)
|
||
|
||
from testcommon import TmpCwd
|
||
|
||
import fdroidserver.build
|
||
import fdroidserver.common
|
||
import fdroidserver.metadata
|
||
import fdroidserver.scanner
|
||
import fdroidserver.vmtools
|
||
from testcommon import mkdtemp, parse_args_for_test
|
||
|
||
|
||
class FakeProcess:
|
||
output = 'fake output'
|
||
returncode = 0
|
||
|
||
def __init__(self, args, **kwargs):
|
||
print('FakeFDroidPopen', args, kwargs)
|
||
|
||
|
||
class Options:
|
||
keep_when_not_allowed = False
|
||
|
||
|
||
class BuildTest(unittest.TestCase):
|
||
'''fdroidserver/build.py'''
|
||
|
||
def setUp(self):
|
||
logging.basicConfig(level=logging.DEBUG)
|
||
self.basedir = os.path.join(localmodule, 'tests')
|
||
os.chdir(self.basedir)
|
||
fdroidserver.common.config = None
|
||
fdroidserver.build.config = None
|
||
fdroidserver.build.options = None
|
||
self._td = mkdtemp()
|
||
self.testdir = self._td.name
|
||
|
||
def tearDown(self):
|
||
os.chdir(self.basedir)
|
||
self._td.cleanup()
|
||
|
||
def create_fake_android_home(self, d):
|
||
os.makedirs(os.path.join(d, 'build-tools'), exist_ok=True)
|
||
os.makedirs(os.path.join(d, 'platform-tools'), exist_ok=True)
|
||
os.makedirs(os.path.join(d, 'tools'), exist_ok=True)
|
||
|
||
def test_get_apk_metadata(self):
|
||
config = dict()
|
||
fdroidserver.common.fill_config_defaults(config)
|
||
fdroidserver.common.config = config
|
||
fdroidserver.build.config = config
|
||
try:
|
||
config['aapt'] = fdroidserver.common.find_sdk_tools_cmd('aapt')
|
||
except fdroidserver.exception.FDroidException:
|
||
pass # aapt is not required if androguard is present
|
||
|
||
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,
|
||
),
|
||
(
|
||
'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-badcert.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-unsigned.apk',
|
||
'info.guardianproject.urzip',
|
||
100,
|
||
'0.1',
|
||
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_5.apk', 'com.politedroid', 5, '1.4', 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/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:
|
||
app = fdroidserver.metadata.App()
|
||
app.id = appid
|
||
build = fdroidserver.metadata.Build()
|
||
build.buildjni = ['yes'] if nativecode else build.buildjni
|
||
build.versionCode = versionCode
|
||
build.versionName = versionName
|
||
vc, vn = fdroidserver.build.get_metadata_from_apk(app, build, apkfilename)
|
||
self.assertEqual(versionCode, vc)
|
||
self.assertEqual(versionName, vn)
|
||
|
||
@mock.patch('fdroidserver.common.get_apk_id')
|
||
@mock.patch('fdroidserver.build.FDroidPopen')
|
||
@mock.patch('fdroidserver.common.is_debuggable_or_testOnly', lambda f: False)
|
||
@mock.patch('fdroidserver.common.get_native_code', lambda f: 'x86')
|
||
def test_build_local_maven(self, fake_FDroidPopen, fake_get_apk_id):
|
||
"""Test build_local() with a maven project"""
|
||
|
||
# pylint: disable=unused-argument
|
||
def _side_effect(cmd, cwd=None):
|
||
p = mock.MagicMock()
|
||
p.output = '[INFO] fake apkbuilder target/no.apk'
|
||
with open(os.path.join(self.testdir, 'target', 'no.apk'), 'w') as fp:
|
||
fp.write('placeholder')
|
||
p.returncode = 0
|
||
return p
|
||
|
||
fake_FDroidPopen.side_effect = _side_effect
|
||
os.chdir(self.testdir)
|
||
os.mkdir('target')
|
||
config = dict()
|
||
fdroidserver.common.fill_config_defaults(config)
|
||
fdroidserver.common.config = config
|
||
fdroidserver.build.config = config
|
||
fdroidserver.build.options = mock.Mock()
|
||
fdroidserver.build.options.scan_binary = False
|
||
fdroidserver.build.options.notarball = True
|
||
fdroidserver.build.options.skipscan = False
|
||
|
||
app = fdroidserver.metadata.App()
|
||
app.id = 'mocked.app.id'
|
||
build = fdroidserver.metadata.Build()
|
||
build.commit = '1.0'
|
||
build.versionCode = 1
|
||
build.versionName = '1.0'
|
||
fake_get_apk_id.side_effect = lambda f: (
|
||
app.id,
|
||
build.versionCode,
|
||
build.versionName,
|
||
)
|
||
vcs = mock.Mock()
|
||
|
||
build.maven = 'yes@..'
|
||
fdroidserver.build.build_local(
|
||
app,
|
||
build,
|
||
vcs,
|
||
build_dir=self.testdir,
|
||
output_dir=self.testdir,
|
||
log_dir=os.getcwd(),
|
||
srclib_dir=None,
|
||
extlib_dir=None,
|
||
tmp_dir=None,
|
||
force=False,
|
||
onserver=True,
|
||
refresh=False,
|
||
)
|
||
|
||
build.maven = 'yes'
|
||
fdroidserver.build.build_local(
|
||
app,
|
||
build,
|
||
vcs,
|
||
build_dir=self.testdir,
|
||
output_dir=self.testdir,
|
||
log_dir=os.getcwd(),
|
||
srclib_dir=None,
|
||
extlib_dir=None,
|
||
tmp_dir=None,
|
||
force=False,
|
||
onserver=True,
|
||
refresh=False,
|
||
)
|
||
|
||
@mock.patch('sdkmanager.build_package_list', lambda use_net: None)
|
||
def test_build_local_ndk(self):
|
||
"""Test if `fdroid build` detects installed NDKs and auto-installs when missing"""
|
||
with tempfile.TemporaryDirectory() as testdir, TmpCwd(
|
||
testdir
|
||
), tempfile.TemporaryDirectory() as sdk_path:
|
||
config = {'ndk_paths': {}, 'sdk_path': sdk_path}
|
||
fdroidserver.common.config = config
|
||
fdroidserver.build.config = config
|
||
fdroidserver.build.options = mock.Mock()
|
||
fdroidserver.build.options.scan_binary = False
|
||
fdroidserver.build.options.notarball = True
|
||
fdroidserver.build.options.skipscan = True
|
||
|
||
app = fdroidserver.metadata.App()
|
||
app.id = 'mocked.app.id'
|
||
build = fdroidserver.metadata.Build()
|
||
build.commit = '1.0'
|
||
build.output = app.id + '.apk'
|
||
build.versionCode = 1
|
||
build.versionName = '1.0'
|
||
build.ndk = 'r21e' # aka 21.4.7075529
|
||
ndk_version = '21.4.7075529'
|
||
ndk_dir = Path(config['sdk_path']) / 'ndk' / ndk_version
|
||
vcs = mock.Mock()
|
||
|
||
def make_fake_apk(output, build):
|
||
with open(build.output, 'w') as fp:
|
||
fp.write('APK PLACEHOLDER')
|
||
return output
|
||
|
||
# pylint: disable=unused-argument
|
||
def fake_sdkmanager_install(to_install, android_home=None):
|
||
ndk_dir.mkdir(parents=True)
|
||
self.assertNotEqual(ndk_version, to_install) # converts r21e to version
|
||
with (ndk_dir / 'source.properties').open('w') as fp:
|
||
fp.write('Pkg.Revision = %s\n' % ndk_version)
|
||
|
||
# use "as _ignored" just to make a pretty layout
|
||
with mock.patch(
|
||
'fdroidserver.common.replace_build_vars', wraps=make_fake_apk
|
||
) as _ignored, mock.patch(
|
||
'fdroidserver.common.get_native_code', return_value='x86'
|
||
) as _ignored, mock.patch(
|
||
'fdroidserver.common.get_apk_id',
|
||
return_value=(app.id, build.versionCode, build.versionName),
|
||
) as _ignored, mock.patch(
|
||
'fdroidserver.common.sha256sum',
|
||
return_value='ad7ce5467e18d40050dc51b8e7affc3e635c85bd8c59be62de32352328ed467e',
|
||
) as _ignored, mock.patch(
|
||
'fdroidserver.common.is_debuggable_or_testOnly',
|
||
return_value=False,
|
||
) as _ignored, mock.patch(
|
||
'fdroidserver.build.FDroidPopen', FakeProcess
|
||
) as _ignored, mock.patch(
|
||
'sdkmanager.install', wraps=fake_sdkmanager_install
|
||
) as _ignored:
|
||
_ignored # silence the linters
|
||
with self.assertRaises(
|
||
fdroidserver.exception.FDroidException,
|
||
msg="No NDK setup, `fdroid build` should fail with error",
|
||
):
|
||
fdroidserver.build.build_local(
|
||
app,
|
||
build,
|
||
vcs,
|
||
build_dir=testdir,
|
||
output_dir=testdir,
|
||
log_dir=None,
|
||
srclib_dir=None,
|
||
extlib_dir=None,
|
||
tmp_dir=None,
|
||
force=False,
|
||
onserver=False,
|
||
refresh=False,
|
||
)
|
||
# now run `fdroid build --onserver`
|
||
print('now run `fdroid build --onserver`')
|
||
self.assertFalse(ndk_dir.exists())
|
||
self.assertFalse('r21e' in config['ndk_paths'])
|
||
self.assertFalse(ndk_version in config['ndk_paths'])
|
||
fdroidserver.build.build_local(
|
||
app,
|
||
build,
|
||
vcs,
|
||
build_dir=testdir,
|
||
output_dir=testdir,
|
||
log_dir=os.getcwd(),
|
||
srclib_dir=None,
|
||
extlib_dir=None,
|
||
tmp_dir=None,
|
||
force=False,
|
||
onserver=True,
|
||
refresh=False,
|
||
)
|
||
self.assertTrue(ndk_dir.exists())
|
||
self.assertTrue(os.path.exists(config['ndk_paths'][ndk_version]))
|
||
# All paths in the config must be strings, never pathlib.Path instances
|
||
self.assertIsInstance(config['ndk_paths'][ndk_version], str)
|
||
|
||
@mock.patch('sdkmanager.build_package_list', lambda use_net: None)
|
||
@mock.patch('fdroidserver.build.FDroidPopen', FakeProcess)
|
||
@mock.patch('fdroidserver.common.get_native_code', lambda _ignored: 'x86')
|
||
@mock.patch('fdroidserver.common.is_debuggable_or_testOnly', lambda _ignored: False)
|
||
@mock.patch(
|
||
'fdroidserver.common.sha256sum',
|
||
lambda f: 'ad7ce5467e18d40050dc51b8e7affc3e635c85bd8c59be62de32352328ed467e',
|
||
)
|
||
def test_build_local_ndk_some_installed(self):
|
||
"""Test if `fdroid build` detects installed NDKs and auto-installs when missing"""
|
||
with tempfile.TemporaryDirectory() as testdir, TmpCwd(
|
||
testdir
|
||
), tempfile.TemporaryDirectory() as sdk_path:
|
||
ndk_r24 = os.path.join(sdk_path, 'ndk', '24.0.8215888')
|
||
os.makedirs(ndk_r24)
|
||
with open(os.path.join(ndk_r24, 'source.properties'), 'w') as fp:
|
||
fp.write('Pkg.Revision = 24.0.8215888\n')
|
||
config = {'ndk_paths': {'r24': ndk_r24}, 'sdk_path': sdk_path}
|
||
fdroidserver.common.config = config
|
||
fdroidserver.build.config = config
|
||
fdroidserver.build.options = mock.Mock()
|
||
fdroidserver.build.options.scan_binary = False
|
||
fdroidserver.build.options.notarball = True
|
||
fdroidserver.build.options.skipscan = True
|
||
|
||
app = fdroidserver.metadata.App()
|
||
app.id = 'mocked.app.id'
|
||
build = fdroidserver.metadata.Build()
|
||
build.commit = '1.0'
|
||
build.output = app.id + '.apk'
|
||
build.versionCode = 1
|
||
build.versionName = '1.0'
|
||
build.ndk = 'r21e' # aka 21.4.7075529
|
||
ndk_version = '21.4.7075529'
|
||
ndk_dir = Path(config['sdk_path']) / 'ndk' / ndk_version
|
||
vcs = mock.Mock()
|
||
|
||
def make_fake_apk(output, build):
|
||
with open(build.output, 'w') as fp:
|
||
fp.write('APK PLACEHOLDER')
|
||
return output
|
||
|
||
# pylint: disable=unused-argument
|
||
def fake_sdkmanager_install(to_install, android_home=None):
|
||
ndk_dir.mkdir(parents=True)
|
||
self.assertNotEqual(ndk_version, to_install) # converts r21e to version
|
||
with (ndk_dir / 'source.properties').open('w') as fp:
|
||
fp.write('Pkg.Revision = %s\n' % ndk_version)
|
||
|
||
# use "as _ignored" just to make a pretty layout
|
||
with mock.patch(
|
||
'fdroidserver.common.replace_build_vars', wraps=make_fake_apk
|
||
) as _ignored, mock.patch(
|
||
'fdroidserver.common.get_apk_id',
|
||
return_value=(app.id, build.versionCode, build.versionName),
|
||
) as _ignored, mock.patch(
|
||
'sdkmanager.install', wraps=fake_sdkmanager_install
|
||
) as _ignored:
|
||
_ignored # silence the linters
|
||
self.assertFalse(ndk_dir.exists())
|
||
self.assertFalse('r21e' in config['ndk_paths'])
|
||
self.assertFalse(ndk_version in config['ndk_paths'])
|
||
fdroidserver.build.build_local(
|
||
app,
|
||
build,
|
||
vcs,
|
||
build_dir=testdir,
|
||
output_dir=testdir,
|
||
log_dir=os.getcwd(),
|
||
srclib_dir=None,
|
||
extlib_dir=None,
|
||
tmp_dir=None,
|
||
force=False,
|
||
onserver=True,
|
||
refresh=False,
|
||
)
|
||
self.assertTrue(ndk_dir.exists())
|
||
self.assertTrue(os.path.exists(config['ndk_paths'][ndk_version]))
|
||
|
||
def test_build_local_clean(self):
|
||
"""Test if `fdroid build` cleans ant and gradle build products"""
|
||
os.chdir(self.testdir)
|
||
config = dict()
|
||
fdroidserver.common.fill_config_defaults(config)
|
||
fdroidserver.common.config = config
|
||
fdroidserver.build.config = config
|
||
fdroidserver.build.options = mock.Mock()
|
||
fdroidserver.build.options.scan_binary = False
|
||
fdroidserver.build.options.notarball = True
|
||
fdroidserver.build.options.skipscan = False
|
||
|
||
app = fdroidserver.metadata.App()
|
||
app.id = 'mocked.app.id'
|
||
build = fdroidserver.metadata.Build()
|
||
build.commit = '1.0'
|
||
build.output = app.id + '.apk'
|
||
build.scandelete = ['baz.so']
|
||
build.scanignore = ['foo.aar']
|
||
build.versionCode = 1
|
||
build.versionName = '1.0'
|
||
vcs = mock.Mock()
|
||
|
||
os.mkdir('reports')
|
||
os.mkdir('target')
|
||
|
||
for f in ('baz.so', 'foo.aar', 'gradle-wrapper.jar'):
|
||
with open(f, 'w') as fp:
|
||
fp.write('placeholder')
|
||
self.assertTrue(os.path.exists(f))
|
||
|
||
os.mkdir('build')
|
||
os.mkdir('build/reports')
|
||
with open('build.gradle', 'w', encoding='utf-8') as fp:
|
||
fp.write('// placeholder')
|
||
|
||
os.mkdir('bin')
|
||
os.mkdir('gen')
|
||
with open('build.xml', 'w', encoding='utf-8') as fp:
|
||
fp.write(
|
||
textwrap.dedent(
|
||
"""<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||
<project basedir="." default="clean" name="mockapp">
|
||
<target name="release"/>
|
||
<target name="clean"/>
|
||
</project>"""
|
||
)
|
||
)
|
||
|
||
def make_fake_apk(output, build):
|
||
with open(build.output, 'w') as fp:
|
||
fp.write('APK PLACEHOLDER')
|
||
return output
|
||
|
||
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_apk_id',
|
||
return_value=(app.id, build.versionCode, build.versionName),
|
||
):
|
||
with mock.patch(
|
||
'fdroidserver.common.is_debuggable_or_testOnly',
|
||
return_value=False,
|
||
):
|
||
fdroidserver.build.build_local(
|
||
app,
|
||
build,
|
||
vcs,
|
||
build_dir=self.testdir,
|
||
output_dir=self.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.isdir('build'))
|
||
self.assertTrue(os.path.isdir('reports'))
|
||
self.assertTrue(os.path.isdir('target'))
|
||
self.assertFalse(os.path.exists('baz.so'))
|
||
self.assertFalse(os.path.exists('bin'))
|
||
self.assertFalse(os.path.exists('build/reports'))
|
||
self.assertFalse(os.path.exists('gen'))
|
||
self.assertFalse(os.path.exists('gradle-wrapper.jar'))
|
||
|
||
def test_scan_with_extlib(self):
|
||
os.chdir(self.testdir)
|
||
os.mkdir("build")
|
||
|
||
config = fdroidserver.common.read_config()
|
||
config['sdk_path'] = os.getenv('ANDROID_HOME')
|
||
config['ndk_paths'] = {'r10d': os.getenv('ANDROID_NDK_HOME')}
|
||
fdroidserver.common.config = config
|
||
app = fdroidserver.metadata.App()
|
||
app.id = 'com.gpl.rpg.AndorsTrail'
|
||
build = fdroidserver.metadata.Build()
|
||
build.commit = 'master'
|
||
build.androidupdate = ['no']
|
||
os.makedirs("extlib/android")
|
||
# write a fake binary jar file the scanner should definitely error on
|
||
with open('extlib/android/android-support-v4r11.jar', 'wb') as file:
|
||
file.write(
|
||
b'PK\x03\x04\x14\x00\x08\x00\x08\x00-\x0eiA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x04\x00META-INF/\xfe\xca\x00\x00\x03\x00PK\x07\x08\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00'
|
||
)
|
||
|
||
class FakeVcs:
|
||
# no need to change to the correct commit here
|
||
def gotorevision(self, rev, refresh=True):
|
||
pass
|
||
|
||
def getsrclib(self):
|
||
return None
|
||
|
||
def deinitsubmodules(self):
|
||
pass
|
||
|
||
# Test we trigger a scanner error without extlibs
|
||
build.extlibs = []
|
||
os.makedirs('build/libs')
|
||
shutil.copy('extlib/android/android-support-v4r11.jar', 'build/libs')
|
||
fdroidserver.common.prepare_source(
|
||
FakeVcs(), app, build, "build", "ignore", "extlib"
|
||
)
|
||
count = fdroidserver.scanner.scan_source("build", build)
|
||
self.assertEqual(1, count, "Should produce a scanner error without extlib")
|
||
|
||
# Now try again as an extlib
|
||
build.extlibs = ['android/android-support-v4r11.jar']
|
||
fdroidserver.common.prepare_source(
|
||
FakeVcs(), app, build, "build", "ignore", "extlib"
|
||
)
|
||
count = fdroidserver.scanner.scan_source("build", build)
|
||
self.assertEqual(0, count, "Shouldn't error on jar from extlib")
|
||
|
||
def test_failed_verifies_are_not_in_unsigned(self):
|
||
os.chdir(self.testdir)
|
||
sdk_path = os.path.join(self.testdir, 'android-sdk')
|
||
self.create_fake_android_home(sdk_path)
|
||
with open('config.yml', 'w') as fp:
|
||
yaml.dump({'sdk_path': sdk_path, 'keep_when_not_allowed': True}, fp)
|
||
os.chmod('config.yml', 0o600)
|
||
fdroidserver.build.config = fdroidserver.common.read_config()
|
||
|
||
os.mkdir('metadata')
|
||
appid = 'info.guardianproject.checkey'
|
||
metadata_file = os.path.join('metadata', appid + '.yml')
|
||
shutil.copy(os.path.join(self.basedir, metadata_file), 'metadata')
|
||
with open(metadata_file) as fp:
|
||
app = fdroidserver.metadata.App(yaml.safe_load(fp))
|
||
app['RepoType'] = 'git'
|
||
app[
|
||
'Binaries'
|
||
] = 'https://example.com/fdroid/repo/info.guardianproject.checkey_%v.apk'
|
||
build = fdroidserver.metadata.Build(
|
||
{
|
||
'versionCode': 123,
|
||
'versionName': '1.2.3',
|
||
'commit': '1.2.3',
|
||
'disable': False,
|
||
}
|
||
)
|
||
app['Builds'] = [build]
|
||
fdroidserver.metadata.write_metadata(metadata_file, app)
|
||
|
||
os.makedirs(os.path.join('unsigned', 'binaries'))
|
||
production_result = os.path.join(
|
||
'unsigned', '%s_%d.apk' % (appid, build['versionCode'])
|
||
)
|
||
production_compare_file = os.path.join(
|
||
'unsigned', 'binaries', '%s_%d.binary.apk' % (appid, build['versionCode'])
|
||
)
|
||
os.makedirs(os.path.join('tmp', 'binaries'))
|
||
test_result = os.path.join('tmp', '%s_%d.apk' % (appid, build['versionCode']))
|
||
test_compare_file = os.path.join(
|
||
'tmp', 'binaries', '%s_%d.binary.apk' % (appid, build['versionCode'])
|
||
)
|
||
with mock.patch(
|
||
'fdroidserver.common.force_exit', lambda *args: None
|
||
) as a, mock.patch(
|
||
'fdroidserver.common.get_android_tools_version_log', lambda: 'fake'
|
||
) as b, mock.patch(
|
||
'fdroidserver.common.FDroidPopen', FakeProcess
|
||
) as c, mock.patch(
|
||
'fdroidserver.build.FDroidPopen', FakeProcess
|
||
) as d, mock.patch(
|
||
'fdroidserver.build.trybuild', lambda *args: True
|
||
) as e, mock.patch(
|
||
'fdroidserver.net.download_file', lambda *args, **kwargs: None
|
||
) as f:
|
||
a, b, c, d, e, f # silence linters' "unused" warnings
|
||
|
||
with mock.patch('sys.argv', ['fdroid build', appid]):
|
||
# successful comparison
|
||
open(production_result, 'w').close()
|
||
open(production_compare_file, 'w').close()
|
||
with mock.patch('fdroidserver.common.verify_apks', lambda *args: None):
|
||
fdroidserver.build.main()
|
||
self.assertTrue(os.path.exists(production_result))
|
||
self.assertTrue(os.path.exists(production_compare_file))
|
||
# failed comparison
|
||
open(production_result, 'w').close()
|
||
open(production_compare_file, 'w').close()
|
||
with mock.patch(
|
||
'fdroidserver.common.verify_apks', lambda *args: 'failed'
|
||
):
|
||
fdroidserver.build.main()
|
||
self.assertFalse(os.path.exists(production_result))
|
||
self.assertFalse(os.path.exists(production_compare_file))
|
||
|
||
with mock.patch('sys.argv', ['fdroid build', '--test', appid]):
|
||
# successful comparison
|
||
open(test_result, 'w').close()
|
||
open(test_compare_file, 'w').close()
|
||
with mock.patch('fdroidserver.common.verify_apks', lambda *args: None):
|
||
fdroidserver.build.main()
|
||
self.assertTrue(os.path.exists(test_result))
|
||
self.assertTrue(os.path.exists(test_compare_file))
|
||
self.assertFalse(os.path.exists(production_result))
|
||
self.assertFalse(os.path.exists(production_compare_file))
|
||
# failed comparison
|
||
open(test_result, 'w').close()
|
||
open(test_compare_file, 'w').close()
|
||
with mock.patch(
|
||
'fdroidserver.common.verify_apks', lambda *args: 'failed'
|
||
):
|
||
fdroidserver.build.main()
|
||
self.assertTrue(os.path.exists(test_result))
|
||
self.assertFalse(os.path.exists(test_compare_file))
|
||
self.assertFalse(os.path.exists(production_result))
|
||
self.assertFalse(os.path.exists(production_compare_file))
|
||
|
||
def test_failed_allowedapksigningkeys_are_not_in_unsigned(self):
|
||
os.chdir(self.testdir)
|
||
os.mkdir('metadata')
|
||
appid = 'info.guardianproject.checkey'
|
||
metadata_file = os.path.join('metadata', appid + '.yml')
|
||
shutil.copy(os.path.join(self.basedir, metadata_file), 'metadata')
|
||
with open(metadata_file) as fp:
|
||
app = fdroidserver.metadata.App(yaml.safe_load(fp))
|
||
app['RepoType'] = 'git'
|
||
app[
|
||
'Binaries'
|
||
] = 'https://example.com/fdroid/repo/info.guardianproject.checkey_%v.apk'
|
||
build = fdroidserver.metadata.Build(
|
||
{
|
||
'versionCode': 123,
|
||
'versionName': '1.2.3',
|
||
'commit': '1.2.3',
|
||
'disable': False,
|
||
}
|
||
)
|
||
app['Builds'] = [build]
|
||
expected_key = 'a' * 64
|
||
bogus_key = 'b' * 64
|
||
app['AllowedAPKSigningKeys'] = [expected_key]
|
||
fdroidserver.metadata.write_metadata(metadata_file, app)
|
||
|
||
os.makedirs(os.path.join('unsigned', 'binaries'))
|
||
production_result = os.path.join(
|
||
'unsigned', '%s_%d.apk' % (appid, build['versionCode'])
|
||
)
|
||
production_compare_file = os.path.join(
|
||
'unsigned', 'binaries', '%s_%d.binary.apk' % (appid, build['versionCode'])
|
||
)
|
||
os.makedirs(os.path.join('tmp', 'binaries'))
|
||
test_result = os.path.join('tmp', '%s_%d.apk' % (appid, build['versionCode']))
|
||
test_compare_file = os.path.join(
|
||
'tmp', 'binaries', '%s_%d.binary.apk' % (appid, build['versionCode'])
|
||
)
|
||
with mock.patch(
|
||
'fdroidserver.common.force_exit', lambda *args: None
|
||
) as a, mock.patch(
|
||
'fdroidserver.common.get_android_tools_version_log', lambda: 'fake'
|
||
) as b, mock.patch(
|
||
'fdroidserver.common.FDroidPopen', FakeProcess
|
||
) as c, mock.patch(
|
||
'fdroidserver.build.FDroidPopen', FakeProcess
|
||
) as d, mock.patch(
|
||
'fdroidserver.build.trybuild', lambda *args: True
|
||
) as e, mock.patch(
|
||
'fdroidserver.net.download_file', lambda *args, **kwargs: None
|
||
) as f:
|
||
a, b, c, d, e, f # silence linters' "unused" warnings
|
||
|
||
with mock.patch('sys.argv', ['fdroid build', appid]):
|
||
# successful comparison, successful signer
|
||
open(production_result, 'w').close()
|
||
open(production_compare_file, 'w').close()
|
||
with mock.patch(
|
||
'fdroidserver.common.verify_apks', lambda *args: None
|
||
) as g, mock.patch(
|
||
'fdroidserver.common.apk_signer_fingerprint',
|
||
lambda *args: expected_key,
|
||
) as h:
|
||
g, h
|
||
fdroidserver.build.main()
|
||
self.assertTrue(os.path.exists(production_result))
|
||
self.assertTrue(os.path.exists(production_compare_file))
|
||
# successful comparison, failed signer
|
||
open(production_result, 'w').close()
|
||
open(production_compare_file, 'w').close()
|
||
with mock.patch(
|
||
'fdroidserver.common.verify_apks', lambda *args: None
|
||
) as g, mock.patch(
|
||
'fdroidserver.common.apk_signer_fingerprint',
|
||
lambda *args: bogus_key,
|
||
) as h:
|
||
g, h
|
||
fdroidserver.build.main()
|
||
self.assertFalse(os.path.exists(production_result))
|
||
self.assertFalse(os.path.exists(production_compare_file))
|
||
# failed comparison
|
||
open(production_result, 'w').close()
|
||
open(production_compare_file, 'w').close()
|
||
with mock.patch(
|
||
'fdroidserver.common.verify_apks', lambda *args: 'failed'
|
||
):
|
||
fdroidserver.build.main()
|
||
self.assertFalse(os.path.exists(production_result))
|
||
self.assertFalse(os.path.exists(production_compare_file))
|
||
|
||
with mock.patch('sys.argv', ['fdroid build', '--test', appid]):
|
||
# successful comparison, successful signer
|
||
open(test_result, 'w').close()
|
||
open(test_compare_file, 'w').close()
|
||
with mock.patch(
|
||
'fdroidserver.common.verify_apks', lambda *args: None
|
||
) as g, mock.patch(
|
||
'fdroidserver.common.apk_signer_fingerprint',
|
||
lambda *args: expected_key,
|
||
) as h:
|
||
g, h
|
||
fdroidserver.build.main()
|
||
self.assertTrue(os.path.exists(test_result))
|
||
self.assertTrue(os.path.exists(test_compare_file))
|
||
self.assertFalse(os.path.exists(production_result))
|
||
self.assertFalse(os.path.exists(production_compare_file))
|
||
# successful comparison, failed signer
|
||
open(test_result, 'w').close()
|
||
open(test_compare_file, 'w').close()
|
||
with mock.patch(
|
||
'fdroidserver.common.verify_apks', lambda *args: None
|
||
) as g, mock.patch(
|
||
'fdroidserver.common.apk_signer_fingerprint',
|
||
lambda *args: bogus_key,
|
||
) as h:
|
||
g, h
|
||
fdroidserver.build.main()
|
||
self.assertTrue(os.path.exists(test_result))
|
||
self.assertFalse(os.path.exists(test_compare_file))
|
||
self.assertFalse(os.path.exists(production_result))
|
||
self.assertFalse(os.path.exists(production_compare_file))
|
||
# failed comparison
|
||
open(test_result, 'w').close()
|
||
open(test_compare_file, 'w').close()
|
||
with mock.patch(
|
||
'fdroidserver.common.verify_apks', lambda *args: 'failed'
|
||
):
|
||
fdroidserver.build.main()
|
||
self.assertTrue(os.path.exists(test_result))
|
||
self.assertFalse(os.path.exists(test_compare_file))
|
||
self.assertFalse(os.path.exists(production_result))
|
||
self.assertFalse(os.path.exists(production_compare_file))
|
||
|
||
@mock.patch('fdroidserver.vmtools.get_build_vm')
|
||
@mock.patch('fdroidserver.vmtools.get_clean_builder')
|
||
@mock.patch('paramiko.SSHClient')
|
||
@mock.patch('subprocess.check_output')
|
||
def test_build_server_cmdline(
|
||
self,
|
||
subprocess_check_output,
|
||
paramiko_SSHClient,
|
||
fdroidserver_vmtools_get_clean_builder,
|
||
fdroidserver_vmtools_get_build_vm,
|
||
):
|
||
"""Test command line flags passed to the buildserver"""
|
||
global cmdline_args
|
||
test_flag = ['', False]
|
||
|
||
def _exec_command(args):
|
||
flag = test_flag[0]
|
||
if test_flag[1]:
|
||
self.assertTrue(flag in args, flag + ' should be present')
|
||
else:
|
||
self.assertFalse(flag in args, flag + ' should not be present')
|
||
|
||
os.chdir(self.testdir)
|
||
os.mkdir('tmp')
|
||
|
||
chan = mock.MagicMock()
|
||
chan.exec_command = _exec_command
|
||
chan.recv_exit_status = lambda: 0
|
||
transport = mock.MagicMock()
|
||
transport.open_session = mock.Mock(return_value=chan)
|
||
sshs = mock.MagicMock()
|
||
sshs.get_transport = mock.Mock(return_value=transport)
|
||
paramiko_SSHClient.return_value = sshs
|
||
subprocess_check_output.return_value = (
|
||
b'0123456789abcdef0123456789abcdefcafebabe'
|
||
)
|
||
fdroidserver_vmtools_get_clean_builder.side_effect = lambda s: {
|
||
'hostname': 'example.com',
|
||
'idfile': '/path/to/id/file',
|
||
'port': 123,
|
||
'user': 'fake',
|
||
}
|
||
fdroidserver.common.config = {'sdk_path': '/fake/android/sdk/path'}
|
||
fdroidserver.build.options = mock.MagicMock()
|
||
vcs = mock.Mock()
|
||
vcs.getsrclib = mock.Mock(return_value=None)
|
||
app = fdroidserver.metadata.App()
|
||
app['metadatapath'] = 'metadata/fake.id.yml'
|
||
app['id'] = 'fake.id'
|
||
app['RepoType'] = 'git'
|
||
build = fdroidserver.metadata.Build(
|
||
{
|
||
'versionCode': 123,
|
||
'versionName': '1.2.3',
|
||
'commit': '1.2.3',
|
||
'disable': False,
|
||
}
|
||
)
|
||
app['Builds'] = [build]
|
||
|
||
test_flag = ('--on-server', True)
|
||
fdroidserver.build.build_server(app, build, vcs, '', '', '', False)
|
||
self.assertTrue(fdroidserver_vmtools_get_build_vm.called)
|
||
|
||
for force in (True, False):
|
||
test_flag = ('--force', force)
|
||
fdroidserver.build.build_server(app, build, vcs, '', '', '', force)
|
||
|
||
fdroidserver.build.options.notarball = True
|
||
test_flag = ('--no-tarball', True)
|
||
fdroidserver.build.build_server(app, build, vcs, '', '', '', False)
|
||
fdroidserver.build.options.notarball = False
|
||
test_flag = ('--no-tarball', False)
|
||
fdroidserver.build.build_server(app, build, vcs, '', '', '', False)
|
||
|
||
fdroidserver.build.options.skipscan = False
|
||
test_flag = ('--scan-binary', True)
|
||
fdroidserver.build.build_server(app, build, vcs, '', '', '', False)
|
||
fdroidserver.build.options.skipscan = True
|
||
test_flag = ('--scan-binary', False)
|
||
fdroidserver.build.build_server(app, build, vcs, '', '', '', False)
|
||
test_flag = ('--skip-scan', True)
|
||
fdroidserver.build.build_server(app, build, vcs, '', '', '', False)
|
||
|
||
@mock.patch('fdroidserver.vmtools.get_build_vm')
|
||
@mock.patch('fdroidserver.vmtools.get_clean_builder')
|
||
@mock.patch('paramiko.SSHClient')
|
||
@mock.patch('subprocess.check_output')
|
||
@mock.patch('fdroidserver.common.getsrclib')
|
||
@mock.patch('fdroidserver.common.prepare_source')
|
||
@mock.patch('fdroidserver.build.build_local')
|
||
@mock.patch('fdroidserver.common.get_android_tools_version_log', lambda: 'versions')
|
||
@mock.patch('fdroidserver.common.deploy_build_log_with_rsync', lambda a, b, c: None)
|
||
def test_build_server_no_local_prepare(
|
||
self,
|
||
build_build_local,
|
||
common_prepare_source,
|
||
common_getsrclib,
|
||
subprocess_check_output,
|
||
paramiko_SSHClient,
|
||
fdroidserver_vmtools_get_clean_builder,
|
||
fdroidserver_vmtools_get_build_vm,
|
||
):
|
||
"""srclibs Prepare: should only be executed in the buildserver"""
|
||
|
||
def _exec_command(args):
|
||
print('chan.exec_command', args)
|
||
|
||
def _getsrclib(
|
||
spec,
|
||
srclib_dir,
|
||
basepath=False,
|
||
raw=False,
|
||
prepare=True,
|
||
preponly=False,
|
||
refresh=True,
|
||
build=None,
|
||
):
|
||
name, ref = spec.split('@')
|
||
libdir = os.path.join(srclib_dir, name)
|
||
os.mkdir(libdir)
|
||
self.assertFalse(prepare, 'Prepare: scripts should never run on host')
|
||
return name, None, libdir # TODO
|
||
|
||
os.chdir(self.testdir)
|
||
|
||
chan = mock.MagicMock()
|
||
chan.exec_command = _exec_command
|
||
chan.recv_exit_status = lambda: 0
|
||
transport = mock.MagicMock()
|
||
transport.open_session = mock.Mock(return_value=chan)
|
||
sshs = mock.MagicMock()
|
||
sshs.get_transport = mock.Mock(return_value=transport)
|
||
paramiko_SSHClient.return_value = sshs
|
||
subprocess_check_output.return_value = (
|
||
b'0123456789abcdef0123456789abcdefcafebabe'
|
||
)
|
||
fdroidserver_vmtools_get_clean_builder.side_effect = lambda s: {
|
||
'hostname': 'example.com',
|
||
'idfile': '/path/to/id/file',
|
||
'port': 123,
|
||
'user': 'fake',
|
||
}
|
||
|
||
fdroidserver.metadata.srclibs = {
|
||
'flutter': {
|
||
'RepoType': 'git',
|
||
'Repo': 'https://github.com/flutter/flutter',
|
||
}
|
||
}
|
||
os.mkdir('srclibs')
|
||
with open('srclibs/flutter.yml', 'w') as fp:
|
||
yaml.dump(fdroidserver.metadata.srclibs, fp)
|
||
common_getsrclib.side_effect = _getsrclib
|
||
|
||
options = mock.MagicMock()
|
||
options.force = False
|
||
options.notarball = True
|
||
options.onserver = False
|
||
options.refresh = False
|
||
options.scan_binary = False
|
||
options.server = True
|
||
options.skipscan = True
|
||
options.test = False
|
||
options.verbose = True
|
||
fdroidserver.build.options = options
|
||
fdroidserver.build.config = {'sdk_path': '/fake/android/sdk/path'}
|
||
|
||
vcs = mock.Mock()
|
||
vcs.getsrclib = mock.Mock(return_value=None)
|
||
app = fdroidserver.metadata.App()
|
||
app['metadatapath'] = 'metadata/fake.id.yml'
|
||
app['id'] = 'fake.id'
|
||
app['RepoType'] = 'git'
|
||
spec = 'flutter@v1.7.8'
|
||
build = fdroidserver.metadata.Build(
|
||
{
|
||
'versionCode': 123,
|
||
'versionName': '1.2.3',
|
||
'commit': '1.2.3',
|
||
'disable': False,
|
||
'srclibs': [spec],
|
||
}
|
||
)
|
||
app['Builds'] = [build]
|
||
|
||
build_dir = 'build'
|
||
srclib_dir = os.path.join(build_dir, 'srclib')
|
||
extlib_dir = os.path.join(build_dir, 'extlib')
|
||
os.mkdir('tmp')
|
||
os.mkdir(build_dir)
|
||
os.mkdir(srclib_dir)
|
||
|
||
fdroidserver.build.trybuild(
|
||
app,
|
||
build,
|
||
build_dir,
|
||
'unsigned',
|
||
'logs',
|
||
None,
|
||
srclib_dir,
|
||
extlib_dir,
|
||
'tmp',
|
||
'repo',
|
||
vcs,
|
||
options.test,
|
||
options.server,
|
||
options.force,
|
||
options.onserver,
|
||
options.refresh,
|
||
)
|
||
|
||
common_getsrclib.assert_called_once_with(
|
||
spec, srclib_dir, basepath=True, prepare=False
|
||
)
|
||
common_prepare_source.assert_not_called()
|
||
build_build_local.assert_not_called()
|
||
|
||
def test_keep_when_not_allowed_default(self):
|
||
self.assertFalse(fdroidserver.build.keep_when_not_allowed())
|
||
|
||
def test_keep_when_not_allowed_config_true(self):
|
||
fdroidserver.build.config = {'keep_when_not_allowed': True}
|
||
self.assertTrue(fdroidserver.build.keep_when_not_allowed())
|
||
|
||
def test_keep_when_not_allowed_config_false(self):
|
||
fdroidserver.build.config = {'keep_when_not_allowed': False}
|
||
self.assertFalse(fdroidserver.build.keep_when_not_allowed())
|
||
|
||
def test_keep_when_not_allowed_options_true(self):
|
||
fdroidserver.build.options = Options
|
||
fdroidserver.build.options.keep_when_not_allowed = True
|
||
self.assertTrue(fdroidserver.build.keep_when_not_allowed())
|
||
|
||
def test_keep_when_not_allowed_options_false(self):
|
||
fdroidserver.build.options = Options
|
||
fdroidserver.build.options.keep_when_not_allowed = False
|
||
self.assertFalse(fdroidserver.build.keep_when_not_allowed())
|
||
|
||
def test_keep_when_not_allowed_options_true_override_config(self):
|
||
fdroidserver.build.options = Options
|
||
fdroidserver.build.options.keep_when_not_allowed = True
|
||
fdroidserver.build.config = {'keep_when_not_allowed': False}
|
||
self.assertTrue(fdroidserver.build.keep_when_not_allowed())
|
||
|
||
def test_keep_when_not_allowed_options_default_does_not_override(self):
|
||
fdroidserver.build.options = Options
|
||
fdroidserver.build.options.keep_when_not_allowed = False
|
||
fdroidserver.build.config = {'keep_when_not_allowed': True}
|
||
self.assertTrue(fdroidserver.build.keep_when_not_allowed())
|
||
|
||
def test_keep_when_not_allowed_all_true(self):
|
||
fdroidserver.build.options = Options
|
||
fdroidserver.build.options.keep_when_not_allowed = True
|
||
fdroidserver.build.config = {'keep_when_not_allowed': True}
|
||
self.assertTrue(fdroidserver.build.keep_when_not_allowed())
|
||
|
||
def test_keep_when_not_allowed_all_false(self):
|
||
fdroidserver.build.options = Options
|
||
fdroidserver.build.options.keep_when_not_allowed = False
|
||
fdroidserver.build.config = {'keep_when_not_allowed': False}
|
||
self.assertFalse(fdroidserver.build.keep_when_not_allowed())
|
||
|
||
|
||
if __name__ == "__main__":
|
||
os.chdir(os.path.dirname(__file__))
|
||
|
||
import argparse
|
||
|
||
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(BuildTest))
|
||
unittest.main(failfast=False)
|