2016-01-04 21:17:58 +01:00
|
|
|
|
#!/usr/bin/env python3
|
2014-08-30 17:07:29 +02:00
|
|
|
|
|
|
|
|
|
# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
|
|
|
|
|
|
2020-01-13 15:43:42 +01:00
|
|
|
|
import copy
|
2017-04-19 10:04:32 +02:00
|
|
|
|
import git
|
2018-09-03 18:07:40 +02:00
|
|
|
|
import glob
|
2024-04-01 11:42:23 +02:00
|
|
|
|
import hashlib
|
2014-08-30 17:07:29 +02:00
|
|
|
|
import inspect
|
2021-03-19 15:44:43 +01:00
|
|
|
|
import json
|
2015-07-22 08:19:56 +02:00
|
|
|
|
import logging
|
2014-08-30 17:07:29 +02:00
|
|
|
|
import os
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
import random
|
2017-04-19 10:04:32 +02:00
|
|
|
|
import shutil
|
update: handle large, corrupt, or inaccessible fastlane/triple-t files
```
Traceback (most recent call last):
File "../fdroid", line 22, in <module>
fdroidserver.__main__.main()
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/__main__.py", line 227, in main
raise e
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/__main__.py", line 208, in main
mod.main()
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2340, in main
repoapps = prepare_apps(apps, apks, repodirs[0])
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2176, in prepare_apps
copy_triple_t_store_metadata(apps_with_packages)
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 1076, in copy_triple_t_store_metadata
_set_author_entry(app, 'authorWebSite', os.path.join(root, f))
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 784, in _set_author_entry
with open(f, errors='replace') as fp:
FileNotFoundError: [Errno 2] No such file or directory: 'build/player.efis.cfd/pfd/src/main/play/contact-website.txt'
```
2021-02-11 17:29:48 +01:00
|
|
|
|
import string
|
2017-09-13 16:00:51 +02:00
|
|
|
|
import subprocess
|
2014-08-30 17:07:29 +02:00
|
|
|
|
import sys
|
2017-04-19 10:04:32 +02:00
|
|
|
|
import tempfile
|
2014-08-30 17:07:29 +02:00
|
|
|
|
import unittest
|
2017-04-19 10:04:32 +02:00
|
|
|
|
import yaml
|
2019-01-30 16:48:35 +01:00
|
|
|
|
import zipfile
|
2019-07-22 01:34:55 +02:00
|
|
|
|
import textwrap
|
2024-04-01 11:42:23 +02:00
|
|
|
|
from binascii import hexlify
|
2020-11-10 16:24:19 +01:00
|
|
|
|
from datetime import datetime
|
2023-05-30 22:42:22 +02:00
|
|
|
|
from pathlib import Path
|
2021-03-19 15:44:43 +01:00
|
|
|
|
from unittest import mock
|
2014-08-30 17:07:29 +02:00
|
|
|
|
|
2024-04-01 11:42:23 +02:00
|
|
|
|
try:
|
|
|
|
|
# these were moved in androguard 4.0
|
|
|
|
|
from androguard.core.apk import APK
|
|
|
|
|
except ImportError:
|
|
|
|
|
from androguard.core.bytecodes.apk import APK
|
|
|
|
|
|
2020-09-10 02:37:05 +02:00
|
|
|
|
try:
|
|
|
|
|
from yaml import CSafeLoader as SafeLoader
|
|
|
|
|
except ImportError:
|
|
|
|
|
from yaml import SafeLoader
|
|
|
|
|
|
2020-09-10 15:51:47 +02:00
|
|
|
|
try:
|
|
|
|
|
from yaml import CFullLoader as FullLoader
|
|
|
|
|
except ImportError:
|
2020-09-10 17:10:43 +02:00
|
|
|
|
try:
|
|
|
|
|
# FullLoader is available from PyYaml 5.1+, as we don't load user
|
|
|
|
|
# controlled data here, it's okay to fall back the unsafe older
|
|
|
|
|
# Loader
|
|
|
|
|
from yaml import FullLoader
|
|
|
|
|
except ImportError:
|
|
|
|
|
from yaml import Loader as FullLoader
|
2020-09-10 15:51:47 +02:00
|
|
|
|
|
2015-07-22 08:19:56 +02:00
|
|
|
|
localmodule = os.path.realpath(
|
2021-06-07 11:49:21 +02:00
|
|
|
|
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..')
|
|
|
|
|
)
|
2014-08-30 17:07:29 +02:00
|
|
|
|
print('localmodule: ' + localmodule)
|
|
|
|
|
if localmodule not in sys.path:
|
2015-07-22 08:19:56 +02:00
|
|
|
|
sys.path.insert(0, localmodule)
|
2014-08-30 17:07:29 +02:00
|
|
|
|
|
|
|
|
|
import fdroidserver.common
|
2017-06-14 16:12:25 +02:00
|
|
|
|
import fdroidserver.exception
|
2017-04-19 10:04:32 +02:00
|
|
|
|
import fdroidserver.metadata
|
2014-08-30 17:07:29 +02:00
|
|
|
|
import fdroidserver.update
|
2023-06-07 13:36:02 +02:00
|
|
|
|
from fdroidserver.common import CATEGORIES_CONFIG_NAME
|
2023-12-08 09:48:11 +01:00
|
|
|
|
from fdroidserver.looseversion import LooseVersion
|
2024-05-08 16:26:46 +02:00
|
|
|
|
from testcommon import TmpCwd, mkdtemp, parse_args_for_test
|
2014-08-30 17:07:29 +02:00
|
|
|
|
|
2015-07-22 08:19:56 +02:00
|
|
|
|
|
2021-06-07 11:49:21 +02:00
|
|
|
|
DONATION_FIELDS = ('Donate', 'Liberapay', 'OpenCollective')
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
|
|
|
|
|
|
2020-11-10 16:24:19 +01:00
|
|
|
|
class Options:
|
|
|
|
|
allow_disabled_algorithms = False
|
|
|
|
|
clean = False
|
2021-07-23 19:16:56 +02:00
|
|
|
|
delete_unknown = False
|
2021-07-20 22:31:27 +02:00
|
|
|
|
nosign = False
|
2021-03-19 15:44:43 +01:00
|
|
|
|
pretty = True
|
2020-11-10 16:24:19 +01:00
|
|
|
|
rename_apks = False
|
2021-07-23 19:16:56 +02:00
|
|
|
|
verbose = False
|
2020-11-10 16:24:19 +01:00
|
|
|
|
|
|
|
|
|
|
2014-08-30 17:07:29 +02:00
|
|
|
|
class UpdateTest(unittest.TestCase):
|
|
|
|
|
'''fdroid update'''
|
|
|
|
|
|
2017-12-14 11:06:22 +01:00
|
|
|
|
def setUp(self):
|
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
2020-01-13 15:43:42 +01:00
|
|
|
|
from PIL import PngImagePlugin
|
2021-06-07 11:49:21 +02:00
|
|
|
|
|
2020-01-13 15:43:42 +01:00
|
|
|
|
logging.getLogger(PngImagePlugin.__name__).setLevel(logging.INFO)
|
2017-12-14 11:06:22 +01:00
|
|
|
|
self.basedir = os.path.join(localmodule, 'tests')
|
|
|
|
|
os.chdir(self.basedir)
|
2022-11-22 17:17:45 +01:00
|
|
|
|
self._td = mkdtemp()
|
|
|
|
|
self.testdir = self._td.name
|
2017-12-14 11:06:22 +01:00
|
|
|
|
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.config = None
|
|
|
|
|
fdroidserver.common.options = None
|
|
|
|
|
|
2022-11-22 17:17:45 +01:00
|
|
|
|
def tearDown(self):
|
|
|
|
|
os.chdir(self.basedir)
|
|
|
|
|
self._td.cleanup()
|
|
|
|
|
|
2022-09-05 20:46:32 +02:00
|
|
|
|
def test_insert_store_metadata(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2022-09-05 20:46:32 +02:00
|
|
|
|
|
2017-04-13 23:36:46 +02:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.update.config = config
|
2017-11-12 16:32:42 +01:00
|
|
|
|
|
2022-09-05 20:46:32 +02:00
|
|
|
|
repo_dir = os.path.join(self.basedir, 'repo')
|
|
|
|
|
os.mkdir('metadata')
|
|
|
|
|
for packageName in (
|
2022-10-31 09:54:45 +01:00
|
|
|
|
'obb.mainpatch.current',
|
|
|
|
|
'org.videolan.vlc',
|
2022-09-05 20:46:32 +02:00
|
|
|
|
):
|
|
|
|
|
shutil.copytree(
|
2022-10-31 09:54:45 +01:00
|
|
|
|
os.path.join(repo_dir, packageName), os.path.join('repo', packageName)
|
2022-09-05 20:46:32 +02:00
|
|
|
|
)
|
|
|
|
|
for packageName in (
|
2022-10-31 09:54:45 +01:00
|
|
|
|
'info.guardianproject.checkey',
|
|
|
|
|
'info.guardianproject.urzip',
|
|
|
|
|
'org.smssecure.smssecure',
|
2022-09-05 20:46:32 +02:00
|
|
|
|
):
|
|
|
|
|
if not os.path.exists('metadata'):
|
|
|
|
|
os.mkdir('metadata')
|
|
|
|
|
shutil.copytree(
|
|
|
|
|
os.path.join(self.basedir, 'metadata', packageName),
|
2022-10-31 09:54:45 +01:00
|
|
|
|
os.path.join('metadata', packageName),
|
2022-09-05 20:46:32 +02:00
|
|
|
|
)
|
|
|
|
|
for packageName in (
|
2022-10-31 09:54:45 +01:00
|
|
|
|
'com.nextcloud.client',
|
|
|
|
|
'com.nextcloud.client.dev',
|
|
|
|
|
'eu.siacs.conversations',
|
2022-09-05 20:46:32 +02:00
|
|
|
|
):
|
|
|
|
|
shutil.copytree(
|
|
|
|
|
os.path.join(self.basedir, 'source-files', packageName),
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.path.join(self.testdir, 'build', packageName),
|
2022-09-05 20:46:32 +02:00
|
|
|
|
)
|
2017-12-14 16:52:02 +01:00
|
|
|
|
|
2019-08-28 13:42:40 +02:00
|
|
|
|
testfilename = 'icon_yAfSvPRJukZzMMfUzvbYqwaD1XmHXNtiPBtuPVHW-6s=.png'
|
2022-09-05 20:46:32 +02:00
|
|
|
|
testfile = os.path.join(repo_dir, 'org.videolan.vlc', 'en-US', 'icon.png')
|
2019-08-28 12:25:53 +02:00
|
|
|
|
cpdir = os.path.join('metadata', 'org.videolan.vlc', 'en-US')
|
2019-08-28 13:42:40 +02:00
|
|
|
|
cpfile = os.path.join(cpdir, testfilename)
|
2019-08-28 12:25:53 +02:00
|
|
|
|
os.makedirs(cpdir, exist_ok=True)
|
|
|
|
|
shutil.copy(testfile, cpfile)
|
|
|
|
|
shutil.copystat(testfile, cpfile)
|
|
|
|
|
|
2017-04-13 23:36:46 +02:00
|
|
|
|
apps = dict()
|
2021-06-07 11:49:21 +02:00
|
|
|
|
for packageName in (
|
|
|
|
|
'info.guardianproject.urzip',
|
|
|
|
|
'org.videolan.vlc',
|
|
|
|
|
'obb.mainpatch.current',
|
|
|
|
|
'com.nextcloud.client',
|
|
|
|
|
'com.nextcloud.client.dev',
|
|
|
|
|
'eu.siacs.conversations',
|
|
|
|
|
):
|
2017-11-12 16:32:42 +01:00
|
|
|
|
apps[packageName] = fdroidserver.metadata.App()
|
2017-04-13 23:36:46 +02:00
|
|
|
|
apps[packageName]['id'] = packageName
|
2021-06-07 11:49:21 +02:00
|
|
|
|
apps[packageName]['CurrentVersionCode'] = 0xCAFEBEEF
|
2017-11-12 16:32:42 +01:00
|
|
|
|
|
2017-04-13 23:36:46 +02:00
|
|
|
|
apps['info.guardianproject.urzip']['CurrentVersionCode'] = 100
|
2017-11-12 16:32:42 +01:00
|
|
|
|
|
|
|
|
|
buildnextcloudclient = fdroidserver.metadata.Build()
|
|
|
|
|
buildnextcloudclient.gradle = ['generic']
|
eliminate app.builds everywhere, it should be app['Builds']
The .txt format was the last place where the lowercase "builds" was used,
this converts references everywhere to be "Builds". This makes it possible
to load metadata YAML files with any YAML parser, then have it possible to
use fdroidserver methods on that data, like metadata.write_metadata().
The test files in tests/metadata/dump/*.yaml were manually edited by cutting
the builds: block and putting it the sort order for Builds: so the contents
should be unchanged.
```
sed -i \
-e 's/app\.builds/app.get('Builds', \[\])/g' \
-e "s/app\.get(Builds, \[\]) =/app\['Builds'] =/g" \
-e "s/app\.get(Builds, \[\]) =/app\['Builds'] =/g" \
-e "s/app\.get(Builds, \[\])/app.get('Builds', \[\])/g" \
-e "s/app\.get('Builds', \[\])\.append/app\['Builds'\].append/g" \
-e "s/app\['builds'\]/app.get('Builds', [])/g" \
*/*.*
```
2020-12-09 16:01:21 +01:00
|
|
|
|
apps['com.nextcloud.client']['Builds'] = [buildnextcloudclient]
|
2017-11-12 16:32:42 +01:00
|
|
|
|
|
|
|
|
|
buildnextclouddevclient = fdroidserver.metadata.Build()
|
|
|
|
|
buildnextclouddevclient.gradle = ['versionDev']
|
eliminate app.builds everywhere, it should be app['Builds']
The .txt format was the last place where the lowercase "builds" was used,
this converts references everywhere to be "Builds". This makes it possible
to load metadata YAML files with any YAML parser, then have it possible to
use fdroidserver methods on that data, like metadata.write_metadata().
The test files in tests/metadata/dump/*.yaml were manually edited by cutting
the builds: block and putting it the sort order for Builds: so the contents
should be unchanged.
```
sed -i \
-e 's/app\.builds/app.get('Builds', \[\])/g' \
-e "s/app\.get(Builds, \[\]) =/app\['Builds'] =/g" \
-e "s/app\.get(Builds, \[\]) =/app\['Builds'] =/g" \
-e "s/app\.get(Builds, \[\])/app.get('Builds', \[\])/g" \
-e "s/app\.get('Builds', \[\])\.append/app\['Builds'\].append/g" \
-e "s/app\['builds'\]/app.get('Builds', [])/g" \
*/*.*
```
2020-12-09 16:01:21 +01:00
|
|
|
|
apps['com.nextcloud.client.dev']['Builds'] = [buildnextclouddevclient]
|
2017-11-12 16:32:42 +01:00
|
|
|
|
|
2017-12-14 16:52:02 +01:00
|
|
|
|
build_conversations = fdroidserver.metadata.Build()
|
|
|
|
|
build_conversations.gradle = ['free']
|
eliminate app.builds everywhere, it should be app['Builds']
The .txt format was the last place where the lowercase "builds" was used,
this converts references everywhere to be "Builds". This makes it possible
to load metadata YAML files with any YAML parser, then have it possible to
use fdroidserver methods on that data, like metadata.write_metadata().
The test files in tests/metadata/dump/*.yaml were manually edited by cutting
the builds: block and putting it the sort order for Builds: so the contents
should be unchanged.
```
sed -i \
-e 's/app\.builds/app.get('Builds', \[\])/g' \
-e "s/app\.get(Builds, \[\]) =/app\['Builds'] =/g" \
-e "s/app\.get(Builds, \[\]) =/app\['Builds'] =/g" \
-e "s/app\.get(Builds, \[\])/app.get('Builds', \[\])/g" \
-e "s/app\.get('Builds', \[\])\.append/app\['Builds'\].append/g" \
-e "s/app\['builds'\]/app.get('Builds', [])/g" \
*/*.*
```
2020-12-09 16:01:21 +01:00
|
|
|
|
apps['eu.siacs.conversations']['Builds'] = [build_conversations]
|
2017-12-14 16:52:02 +01:00
|
|
|
|
|
2017-04-13 23:36:46 +02:00
|
|
|
|
fdroidserver.update.insert_localized_app_metadata(apps)
|
2024-02-12 00:55:16 +01:00
|
|
|
|
fdroidserver.update.ingest_screenshots_from_repo_dir(apps)
|
2017-04-13 23:36:46 +02:00
|
|
|
|
|
2017-05-16 12:25:42 +02:00
|
|
|
|
appdir = os.path.join('repo', 'info.guardianproject.urzip', 'en-US')
|
2024-01-11 15:45:54 +01:00
|
|
|
|
self.assertTrue(
|
|
|
|
|
os.path.isfile(
|
|
|
|
|
os.path.join(
|
|
|
|
|
appdir, 'icon_NJXNzMcyf-v9i5a1ElJi0j9X1LvllibCa48xXYPlOqQ=.png'
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
self.assertTrue(
|
|
|
|
|
os.path.isfile(
|
|
|
|
|
os.path.join(
|
|
|
|
|
appdir,
|
|
|
|
|
'featureGraphic_GFRT5BovZsENGpJq1HqPODGWBRPWQsx25B95Ol5w_wU=.png',
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
2017-05-16 12:25:42 +02:00
|
|
|
|
|
2017-12-14 16:52:02 +01:00
|
|
|
|
self.assertEqual(6, len(apps))
|
2017-04-13 23:36:46 +02:00
|
|
|
|
for packageName, app in apps.items():
|
2022-09-05 20:46:32 +02:00
|
|
|
|
self.assertTrue('localized' in app, packageName)
|
2017-04-13 23:36:46 +02:00
|
|
|
|
self.assertTrue('en-US' in app['localized'])
|
|
|
|
|
self.assertEqual(1, len(app['localized']))
|
|
|
|
|
if packageName == 'info.guardianproject.urzip':
|
2017-05-16 12:25:42 +02:00
|
|
|
|
self.assertEqual(7, len(app['localized']['en-US']))
|
2017-04-27 21:12:49 +02:00
|
|
|
|
self.assertEqual('full description\n', app['localized']['en-US']['description'])
|
2020-01-13 18:54:28 +01:00
|
|
|
|
self.assertEqual('title', app['localized']['en-US']['name'])
|
|
|
|
|
self.assertEqual('short description', app['localized']['en-US']['summary'])
|
|
|
|
|
self.assertEqual('video', app['localized']['en-US']['video'])
|
2019-08-28 13:42:40 +02:00
|
|
|
|
self.assertEqual('icon_NJXNzMcyf-v9i5a1ElJi0j9X1LvllibCa48xXYPlOqQ=.png',
|
|
|
|
|
app['localized']['en-US']['icon'])
|
|
|
|
|
self.assertEqual('featureGraphic_GFRT5BovZsENGpJq1HqPODGWBRPWQsx25B95Ol5w_wU=.png',
|
|
|
|
|
app['localized']['en-US']['featureGraphic'])
|
2017-04-27 21:12:49 +02:00
|
|
|
|
self.assertEqual('100\n', app['localized']['en-US']['whatsNew'])
|
2017-04-13 23:36:46 +02:00
|
|
|
|
elif packageName == 'org.videolan.vlc':
|
2019-08-28 13:42:40 +02:00
|
|
|
|
self.assertEqual(testfilename, app['localized']['en-US']['icon'])
|
2017-04-13 23:36:46 +02:00
|
|
|
|
self.assertEqual(9, len(app['localized']['en-US']['phoneScreenshots']))
|
|
|
|
|
self.assertEqual(15, len(app['localized']['en-US']['sevenInchScreenshots']))
|
|
|
|
|
elif packageName == 'obb.mainpatch.current':
|
2019-08-28 13:42:40 +02:00
|
|
|
|
self.assertEqual('icon_WI0pkO3LsklrsTAnRr-OQSxkkoMY41lYe2-fAvXLiLg=.png',
|
|
|
|
|
app['localized']['en-US']['icon'])
|
|
|
|
|
self.assertEqual('featureGraphic_ffhLaojxbGAfu9ROe1MJgK5ux8d0OVc6b65nmvOBaTk=.png',
|
|
|
|
|
app['localized']['en-US']['featureGraphic'])
|
2017-04-13 23:36:46 +02:00
|
|
|
|
self.assertEqual(1, len(app['localized']['en-US']['phoneScreenshots']))
|
|
|
|
|
self.assertEqual(1, len(app['localized']['en-US']['sevenInchScreenshots']))
|
2017-11-12 16:32:42 +01:00
|
|
|
|
elif packageName == 'com.nextcloud.client':
|
|
|
|
|
self.assertEqual('Nextcloud', app['localized']['en-US']['name'])
|
|
|
|
|
self.assertEqual(1073, len(app['localized']['en-US']['description']))
|
|
|
|
|
self.assertEqual(78, len(app['localized']['en-US']['summary']))
|
|
|
|
|
elif packageName == 'com.nextcloud.client.dev':
|
|
|
|
|
self.assertEqual('Nextcloud Dev', app['localized']['en-US']['name'])
|
|
|
|
|
self.assertEqual(586, len(app['localized']['en-US']['description']))
|
2020-01-13 18:54:28 +01:00
|
|
|
|
self.assertEqual(78, len(app['localized']['en-US']['summary']))
|
2017-12-14 16:52:02 +01:00
|
|
|
|
elif packageName == 'eu.siacs.conversations':
|
|
|
|
|
self.assertEqual('Conversations', app['localized']['en-US']['name'])
|
2017-04-13 23:36:46 +02:00
|
|
|
|
|
2022-10-31 09:23:09 +01:00
|
|
|
|
def test_insert_fastlane_default_txt_changelog(self):
|
|
|
|
|
"""Test that Fastlane's default.txt is handled properly
|
|
|
|
|
|
|
|
|
|
https://docs.fastlane.tools/actions/supply/#changelogs-whats-new
|
|
|
|
|
"""
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2022-10-31 09:23:09 +01:00
|
|
|
|
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
|
|
|
|
app = fdroidserver.metadata.App()
|
|
|
|
|
app.id = 'com.example.app'
|
|
|
|
|
changelogs_dir = 'build/%s/metadata/en-US/changelogs' % app.id
|
|
|
|
|
os.makedirs(changelogs_dir)
|
|
|
|
|
with open(os.path.join(changelogs_dir, 'default.txt'), 'w') as fp:
|
|
|
|
|
fp.write('default')
|
|
|
|
|
with open(os.path.join(changelogs_dir, '42.txt'), 'w') as fp:
|
|
|
|
|
fp.write('42')
|
|
|
|
|
apps = {app.id: app}
|
|
|
|
|
build = fdroidserver.metadata.Build()
|
|
|
|
|
build.versionCode = 42
|
|
|
|
|
app['Builds'] = [build]
|
|
|
|
|
|
|
|
|
|
fdroidserver.update.insert_localized_app_metadata(apps)
|
|
|
|
|
self.assertEqual('default', apps[app.id]['localized']['en-US']['whatsNew'])
|
|
|
|
|
|
|
|
|
|
app.CurrentVersionCode = 1
|
|
|
|
|
fdroidserver.update.insert_localized_app_metadata(apps)
|
|
|
|
|
self.assertEqual('default', apps[app.id]['localized']['en-US']['whatsNew'])
|
|
|
|
|
|
|
|
|
|
app.CurrentVersionCode = 10000
|
|
|
|
|
fdroidserver.update.insert_localized_app_metadata(apps)
|
|
|
|
|
self.assertEqual('default', apps[app.id]['localized']['en-US']['whatsNew'])
|
|
|
|
|
|
|
|
|
|
app.CurrentVersionCode = 42
|
|
|
|
|
fdroidserver.update.insert_localized_app_metadata(apps)
|
|
|
|
|
self.assertEqual('42', apps[app.id]['localized']['en-US']['whatsNew'])
|
|
|
|
|
|
2020-01-13 15:43:42 +01:00
|
|
|
|
def test_name_title_scraping(self):
|
|
|
|
|
"""metadata file --> fdroiddata localized files --> fastlane/triple-t in app source --> APK"""
|
2022-11-22 17:17:45 +01:00
|
|
|
|
shutil.copytree(self.basedir, self.testdir, dirs_exist_ok=True)
|
2020-01-13 15:43:42 +01:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2020-01-13 15:43:42 +01:00
|
|
|
|
fdroidserver.update.options.clean = True
|
|
|
|
|
fdroidserver.update.options.delete_unknown = True
|
|
|
|
|
|
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
2022-10-31 09:23:09 +01:00
|
|
|
|
apps['info.guardianproject.urzip']['CurrentVersionCode'] = 100
|
2020-01-13 15:43:42 +01:00
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
|
|
|
|
|
fdroidserver.update.insert_localized_app_metadata(apps)
|
2024-02-12 00:55:16 +01:00
|
|
|
|
fdroidserver.update.ingest_screenshots_from_repo_dir(apps)
|
2020-01-13 15:43:42 +01:00
|
|
|
|
fdroidserver.update.apply_info_from_latest_apk(apps, apks)
|
|
|
|
|
app = apps['info.guardianproject.urzip']
|
|
|
|
|
self.assertIsNone(app.Name)
|
|
|
|
|
self.assertTrue('localized' in app)
|
|
|
|
|
self.assertEqual('title', app['localized']['en-US']['name'])
|
2022-10-31 09:23:09 +01:00
|
|
|
|
self.assertEqual('100\n', app['localized']['en-US']['whatsNew'])
|
2020-01-13 15:43:42 +01:00
|
|
|
|
app = apps['org.videolan.vlc']
|
|
|
|
|
self.assertIsNone(app.Name)
|
|
|
|
|
self.assertTrue('localized' in app)
|
|
|
|
|
self.assertFalse('name' in app['localized']['en-US'])
|
|
|
|
|
app = apps['info.guardianproject.checkey']
|
|
|
|
|
self.assertEqual('Checkey the app!', app.Name)
|
|
|
|
|
self.assertTrue('localized' in app)
|
|
|
|
|
self.assertEqual('Checkey: info on local apps', app['localized']['en-US']['name'])
|
|
|
|
|
self.assertEqual('Checkey: ローカルアプリの情報', app['localized']['ja-JP']['name'])
|
|
|
|
|
app = apps['org.adaway']
|
|
|
|
|
self.assertIsNone(app.Name)
|
|
|
|
|
self.assertFalse('localized' in app)
|
|
|
|
|
app = apps['obb.main.twoversions']
|
|
|
|
|
self.assertIsNone(app.Name)
|
|
|
|
|
self.assertFalse('localized' in app)
|
|
|
|
|
|
|
|
|
|
def test_insert_missing_app_names_from_apks(self):
|
|
|
|
|
"""en-US serves as the final, default, fallback value with index-v1"""
|
|
|
|
|
testvalue = 'TESTVALUE!'
|
|
|
|
|
apps = {
|
|
|
|
|
'none': {},
|
|
|
|
|
'name': {'Name': testvalue},
|
|
|
|
|
'onlyapk': {'Name': None},
|
|
|
|
|
'autoname': {'AutoName': 'autoname', 'Name': None},
|
|
|
|
|
'onlylocalized': {'localized': {'en-US': {'name': testvalue}}},
|
|
|
|
|
'non_en_us_localized': {'localized': {'de-AT': {'name': 'leiwand'}}},
|
|
|
|
|
'apks': {},
|
|
|
|
|
}
|
|
|
|
|
apks = [
|
|
|
|
|
{'packageName': 'none', 'name': '', 'versionCode': 1},
|
|
|
|
|
{'packageName': 'name', 'name': 'fromapk', 'versionCode': 1},
|
|
|
|
|
{'packageName': 'onlyapk', 'name': testvalue, 'versionCode': 1},
|
|
|
|
|
{'packageName': 'autoname', 'name': testvalue, 'versionCode': 1},
|
|
|
|
|
{'packageName': 'onlylocalized', 'name': 'fromapk', 'versionCode': 1},
|
|
|
|
|
{'packageName': 'non_en_us_localized', 'name': testvalue, 'versionCode': 0xcafe},
|
|
|
|
|
{'packageName': 'apks', 'name': 'fromapk1', 'versionCode': 1},
|
|
|
|
|
{'packageName': 'apks', 'name': 'fromapk2', 'versionCode': 2},
|
|
|
|
|
{'packageName': 'apks', 'name': testvalue, 'versionCode': 3},
|
|
|
|
|
]
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
2020-01-13 15:43:42 +01:00
|
|
|
|
fdroidserver.update.insert_missing_app_names_from_apks(apps, apks)
|
|
|
|
|
for appid, app in apps.items():
|
|
|
|
|
if appid == 'none':
|
|
|
|
|
self.assertIsNone(app.get('Name'))
|
|
|
|
|
self.assertIsNone(app.get('localized'))
|
|
|
|
|
elif appid == 'onlyapk':
|
|
|
|
|
self.assertIsNone(app.get('Name'))
|
|
|
|
|
self.assertEqual(testvalue, app['localized']['en-US']['name'])
|
|
|
|
|
elif appid == 'autoname':
|
|
|
|
|
self.assertIsNone(app.get('Name'))
|
|
|
|
|
self.assertEqual(testvalue, app['localized']['en-US']['name'])
|
|
|
|
|
elif appid == 'onlylocalized':
|
|
|
|
|
self.assertIsNone(app.get('Name'))
|
|
|
|
|
self.assertEqual(testvalue, app['localized']['en-US']['name'])
|
|
|
|
|
elif appid == 'non_en_us_localized':
|
|
|
|
|
self.assertIsNone(app.get('Name'))
|
|
|
|
|
self.assertEqual(testvalue, app['localized']['en-US']['name'])
|
|
|
|
|
elif appid == 'name':
|
|
|
|
|
self.assertEqual(testvalue, app['Name'])
|
|
|
|
|
self.assertIsNone(app.get('localized'))
|
|
|
|
|
elif appid == 'apks':
|
|
|
|
|
self.assertIsNone(app.get('Name'))
|
|
|
|
|
self.assertEqual(testvalue, app['localized']['en-US']['name'])
|
|
|
|
|
|
|
|
|
|
def test_insert_missing_app_names_from_apks_from_repo(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
shutil.copytree(self.basedir, self.testdir, dirs_exist_ok=True)
|
2020-01-13 15:43:42 +01:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2020-01-13 15:43:42 +01:00
|
|
|
|
fdroidserver.update.options.clean = True
|
|
|
|
|
fdroidserver.update.options.delete_unknown = True
|
|
|
|
|
|
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
|
|
|
|
|
|
|
|
|
|
appid = 'info.guardianproject.checkey'
|
|
|
|
|
testapps = {appid: copy.copy(apps[appid])}
|
|
|
|
|
self.assertEqual('Checkey the app!', testapps[appid]['Name'])
|
2022-10-31 09:54:45 +01:00
|
|
|
|
del testapps[appid]['Name']
|
2020-01-13 15:43:42 +01:00
|
|
|
|
fdroidserver.update.insert_missing_app_names_from_apks(testapps, apks)
|
|
|
|
|
self.assertIsNone(testapps[appid].get('Name'))
|
|
|
|
|
|
|
|
|
|
repoapps = fdroidserver.update.prepare_apps(apps, apks, 'repo')
|
|
|
|
|
fdroidserver.update.insert_missing_app_names_from_apks(repoapps, apks)
|
|
|
|
|
self.assertIsNone(repoapps['com.politedroid']['Name'])
|
|
|
|
|
self.assertEqual('Polite Droid',
|
|
|
|
|
repoapps['com.politedroid']['localized']['en-US']['name'])
|
|
|
|
|
self.assertEqual('Duplicate Permisssions', repoapps['duplicate.permisssions']['Name'])
|
|
|
|
|
self.assertEqual('Caffeine Tile', repoapps['info.zwanenburg.caffeinetile']['Name'])
|
|
|
|
|
self.assertEqual('No minSdkVersion or targetSdkVersion', repoapps['no.min.target.sdk']['Name'])
|
|
|
|
|
self.assertIsNone(repoapps['obb.main.oldversion'].get('Name'))
|
|
|
|
|
self.assertEqual('OBB Main Old Version',
|
|
|
|
|
repoapps['obb.main.oldversion']['localized']['en-US']['name'])
|
|
|
|
|
self.assertIsNone(repoapps['obb.main.twoversions'].get('Name'))
|
|
|
|
|
self.assertEqual('OBB Main Two Versions',
|
|
|
|
|
repoapps['obb.main.twoversions']['localized']['en-US']['name'])
|
|
|
|
|
self.assertIsNone(repoapps['souch.smsbypass'].get('Name'))
|
|
|
|
|
self.assertEqual('Battery level',
|
|
|
|
|
repoapps['souch.smsbypass']['localized']['en-US']['name'])
|
|
|
|
|
self.assertIsNone(repoapps['info.guardianproject.urzip'].get('Name'))
|
|
|
|
|
self.assertEqual('title',
|
|
|
|
|
repoapps['info.guardianproject.urzip']['localized']['en-US']['name'])
|
|
|
|
|
self.assertIsNone(repoapps['obb.mainpatch.current'].get('Name'))
|
|
|
|
|
|
2022-07-31 09:14:17 +02:00
|
|
|
|
del repoapps['info.guardianproject.urzip']['localized']
|
2020-01-13 15:43:42 +01:00
|
|
|
|
fdroidserver.update.insert_missing_app_names_from_apks(repoapps, apks)
|
|
|
|
|
self.assertEqual('urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234',
|
|
|
|
|
repoapps['info.guardianproject.urzip']['localized']['en-US']['name'])
|
|
|
|
|
|
2017-04-19 10:04:32 +02:00
|
|
|
|
def test_insert_triple_t_metadata(self):
|
2017-12-14 11:06:22 +01:00
|
|
|
|
importer = os.path.join(self.basedir, 'tmp', 'importer')
|
2017-04-19 10:04:32 +02:00
|
|
|
|
packageName = 'org.fdroid.ci.test.app'
|
|
|
|
|
if not os.path.isdir(importer):
|
|
|
|
|
logging.warning('skipping test_insert_triple_t_metadata, import.TestCase must run first!')
|
|
|
|
|
return
|
2022-11-22 17:17:45 +01:00
|
|
|
|
packageDir = os.path.join(self.testdir, 'build', packageName)
|
2017-04-19 10:04:32 +02:00
|
|
|
|
shutil.copytree(importer, packageDir)
|
|
|
|
|
|
|
|
|
|
# always use the same commit so these tests work when ci-test-app.git is updated
|
|
|
|
|
repo = git.Repo(packageDir)
|
|
|
|
|
for remote in repo.remotes:
|
|
|
|
|
remote.fetch()
|
|
|
|
|
repo.git.reset('--hard', 'b9e5d1a0d8d6fc31d4674b2f0514fef10762ed4f')
|
|
|
|
|
repo.git.clean('-fdx')
|
|
|
|
|
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.mkdir(os.path.join(self.testdir, 'metadata'))
|
2017-04-19 10:04:32 +02:00
|
|
|
|
metadata = dict()
|
|
|
|
|
metadata['Description'] = 'This is just a test app'
|
2022-11-22 17:17:45 +01:00
|
|
|
|
with open(os.path.join(self.testdir, 'metadata', packageName + '.yml'), 'w') as fp:
|
2017-04-19 10:04:32 +02:00
|
|
|
|
yaml.dump(metadata, fp)
|
|
|
|
|
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2017-04-19 10:04:32 +02:00
|
|
|
|
|
2020-11-17 14:17:08 +01:00
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
2017-04-19 10:04:32 +02:00
|
|
|
|
fdroidserver.update.copy_triple_t_store_metadata(apps)
|
|
|
|
|
|
|
|
|
|
# TODO ideally, this would compare the whole dict like in metadata.TestCase's test_read_metadata()
|
|
|
|
|
correctlocales = [
|
|
|
|
|
'ar', 'ast_ES', 'az', 'ca', 'ca_ES', 'cs-CZ', 'cs_CZ', 'da',
|
|
|
|
|
'da-DK', 'de', 'de-DE', 'el', 'en-US', 'es', 'es-ES', 'es_ES', 'et',
|
|
|
|
|
'fi', 'fr', 'fr-FR', 'he_IL', 'hi-IN', 'hi_IN', 'hu', 'id', 'it',
|
|
|
|
|
'it-IT', 'it_IT', 'iw-IL', 'ja', 'ja-JP', 'kn_IN', 'ko', 'ko-KR',
|
|
|
|
|
'ko_KR', 'lt', 'nb', 'nb_NO', 'nl', 'nl-NL', 'no', 'pl', 'pl-PL',
|
|
|
|
|
'pl_PL', 'pt', 'pt-BR', 'pt-PT', 'pt_BR', 'ro', 'ro_RO', 'ru-RU',
|
|
|
|
|
'ru_RU', 'sv-SE', 'sv_SE', 'te', 'tr', 'tr-TR', 'uk', 'uk_UA', 'vi',
|
|
|
|
|
'vi_VN', 'zh-CN', 'zh_CN', 'zh_TW',
|
|
|
|
|
]
|
|
|
|
|
locales = sorted(list(apps['org.fdroid.ci.test.app']['localized'].keys()))
|
|
|
|
|
self.assertEqual(correctlocales, locales)
|
|
|
|
|
|
2019-10-04 11:06:42 +02:00
|
|
|
|
def test_insert_triple_t_2_metadata(self):
|
|
|
|
|
packageName = 'org.piwigo.android'
|
2022-11-22 17:17:45 +01:00
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'triple-t-2'), self.testdir, dirs_exist_ok=True)
|
|
|
|
|
os.chdir(self.testdir)
|
2019-10-04 11:06:42 +02:00
|
|
|
|
|
2020-09-10 02:37:30 +02:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
2019-10-04 11:06:42 +02:00
|
|
|
|
|
2020-11-17 14:17:08 +01:00
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
2019-10-04 11:06:42 +02:00
|
|
|
|
self.assertTrue(packageName in apps)
|
|
|
|
|
fdroidserver.update.copy_triple_t_store_metadata(apps)
|
|
|
|
|
correctlocales = ['de-DE', 'en-US', 'fr-FR', 'kn-IN']
|
|
|
|
|
app = apps[packageName]
|
|
|
|
|
self.assertEqual('android@piwigo.org', app['authorEmail'])
|
|
|
|
|
self.assertEqual('https://www.piwigo.org', app['authorWebSite'])
|
|
|
|
|
locales = sorted(list(app['localized'].keys()))
|
|
|
|
|
self.assertEqual(correctlocales, locales)
|
|
|
|
|
kn_IN = app['localized']['kn-IN']
|
|
|
|
|
self.assertTrue('description' in kn_IN)
|
|
|
|
|
self.assertTrue('name' in kn_IN)
|
|
|
|
|
self.assertTrue('summary' in kn_IN)
|
|
|
|
|
en_US = app['localized']['en-US']
|
|
|
|
|
self.assertTrue('whatsNew' in en_US)
|
|
|
|
|
|
|
|
|
|
os.chdir(os.path.join('repo', packageName))
|
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('en-US', 'icon.png')))
|
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('en-US', 'featureGraphic.png')))
|
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('en-US', 'phoneScreenshots', '01_Login.jpg')))
|
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('en-US', 'sevenInchScreenshots', '01_Login.png')))
|
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('de-DE', 'icon.png')))
|
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('de-DE', 'featureGraphic.png')))
|
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('de-DE', 'phoneScreenshots', '01_Login.jpg')))
|
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('de-DE', 'sevenInchScreenshots', '01_Login.png')))
|
|
|
|
|
|
2021-08-04 17:33:38 +02:00
|
|
|
|
def test_insert_triple_t_anysoftkeyboard(self):
|
|
|
|
|
packages = ('com.anysoftkeyboard.languagepack.dutch', 'com.menny.android.anysoftkeyboard')
|
|
|
|
|
names = ('Dutch for AnySoftKeyboard', 'AnySoftKeyboard')
|
|
|
|
|
|
2022-11-22 17:17:45 +01:00
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'triple-t-anysoftkeyboard'), self.testdir, dirs_exist_ok=True)
|
|
|
|
|
os.chdir(self.testdir)
|
2021-08-04 17:33:38 +02:00
|
|
|
|
|
|
|
|
|
for packageName, name in zip(packages, names):
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
|
|
|
|
|
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
|
|
|
|
self.assertTrue(packageName in apps)
|
|
|
|
|
fdroidserver.update.copy_triple_t_store_metadata(apps)
|
|
|
|
|
app = apps[packageName]
|
|
|
|
|
self.assertEqual(app['localized']['en-US']['name'], name)
|
|
|
|
|
|
2021-07-03 21:20:18 +02:00
|
|
|
|
def test_insert_triple_t_multiple_metadata(self):
|
|
|
|
|
namespace = 'ch.admin.bag.covidcertificate.'
|
|
|
|
|
packages = ('verifier', 'wallet')
|
|
|
|
|
names = dict(verifier='COVID Certificate Check', wallet='COVID Certificate')
|
|
|
|
|
|
2022-11-22 17:17:45 +01:00
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'triple-t-multiple'), self.testdir, dirs_exist_ok=True)
|
|
|
|
|
os.chdir(self.testdir)
|
2021-07-03 21:20:18 +02:00
|
|
|
|
|
|
|
|
|
for p in packages:
|
|
|
|
|
packageName = namespace + p
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
|
|
|
|
|
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
|
|
|
|
self.assertTrue(packageName in apps)
|
|
|
|
|
fdroidserver.update.copy_triple_t_store_metadata(apps)
|
|
|
|
|
app = apps[packageName]
|
|
|
|
|
self.assertEqual(app['localized']['en-US']['name'], names[p])
|
|
|
|
|
|
2022-03-06 21:45:21 +01:00
|
|
|
|
def test_insert_triple_t_flutter(self):
|
|
|
|
|
packageName = 'fr.emersion.goguma'
|
|
|
|
|
|
2022-11-22 17:17:45 +01:00
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'triple-t-flutter'), self.testdir, dirs_exist_ok=True)
|
|
|
|
|
os.chdir(self.testdir)
|
2022-03-06 21:45:21 +01:00
|
|
|
|
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
|
|
|
|
|
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
|
|
|
|
self.assertTrue(packageName in apps)
|
|
|
|
|
fdroidserver.update.copy_triple_t_store_metadata(apps)
|
|
|
|
|
app = apps[packageName]
|
|
|
|
|
self.assertEqual(app['authorWebSite'], 'https://emersion.fr')
|
|
|
|
|
self.assertEqual(app['localized']['en-US']['name'], 'Goguma')
|
|
|
|
|
self.assertEqual(app['localized']['en-US']['summary'], 'An IRC client for mobile devices')
|
|
|
|
|
|
2014-08-30 17:07:29 +02:00
|
|
|
|
def testBadGetsig(self):
|
2017-06-01 16:24:31 +02:00
|
|
|
|
"""getsig() should still be able to fetch the fingerprint of bad signatures"""
|
2016-02-11 20:43:55 +01:00
|
|
|
|
# config needed to use jarsigner and keytool
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.update.config = config
|
2017-06-01 16:24:31 +02:00
|
|
|
|
|
2018-08-08 02:36:38 +02:00
|
|
|
|
apkfile = 'urzip-badsig.apk'
|
2017-06-01 16:24:31 +02:00
|
|
|
|
sig = fdroidserver.update.getsig(apkfile)
|
|
|
|
|
self.assertEqual(sig, 'e0ecb5fc2d63088e4a07ae410a127722',
|
|
|
|
|
"python sig should be: " + str(sig))
|
2014-08-30 17:07:29 +02:00
|
|
|
|
|
2018-08-08 02:36:38 +02:00
|
|
|
|
apkfile = 'urzip-badcert.apk'
|
2017-06-01 16:24:31 +02:00
|
|
|
|
sig = fdroidserver.update.getsig(apkfile)
|
|
|
|
|
self.assertEqual(sig, 'e0ecb5fc2d63088e4a07ae410a127722',
|
|
|
|
|
"python sig should be: " + str(sig))
|
2014-08-30 17:07:29 +02:00
|
|
|
|
|
2019-01-30 16:48:35 +01:00
|
|
|
|
def test_getsig(self):
|
|
|
|
|
# config needed to use jarsigner and keytool
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
|
|
|
|
sig = fdroidserver.update.getsig('urzip-release-unsigned.apk')
|
|
|
|
|
self.assertIsNone(sig)
|
|
|
|
|
|
|
|
|
|
good_fingerprint = 'b4964fd759edaa54e65bb476d0276880'
|
|
|
|
|
|
|
|
|
|
apkpath = 'urzip-release.apk' # v1 only
|
|
|
|
|
sig = fdroidserver.update.getsig(apkpath)
|
|
|
|
|
self.assertEqual(good_fingerprint, sig,
|
|
|
|
|
'python sig was: ' + str(sig))
|
|
|
|
|
|
|
|
|
|
apkpath = 'repo/v1.v2.sig_1020.apk'
|
|
|
|
|
sig = fdroidserver.update.getsig(apkpath)
|
|
|
|
|
self.assertEqual(good_fingerprint, sig,
|
|
|
|
|
'python sig was: ' + str(sig))
|
|
|
|
|
# check that v1 and v2 have the same certificate
|
|
|
|
|
apkobject = APK(apkpath)
|
|
|
|
|
cert_encoded = apkobject.get_certificates_der_v2()[0]
|
|
|
|
|
self.assertEqual(good_fingerprint, sig,
|
|
|
|
|
hashlib.md5(hexlify(cert_encoded)).hexdigest()) # nosec just used as ID for signing key
|
|
|
|
|
|
|
|
|
|
filename = 'v2.only.sig_2.apk'
|
|
|
|
|
with zipfile.ZipFile(filename) as z:
|
|
|
|
|
self.assertTrue('META-INF/MANIFEST.MF' in z.namelist(), 'META-INF/MANIFEST.MF required')
|
|
|
|
|
for f in z.namelist():
|
|
|
|
|
# ensure there are no v1 signature files
|
|
|
|
|
self.assertIsNone(fdroidserver.common.SIGNATURE_BLOCK_FILE_REGEX.match(f))
|
|
|
|
|
sig = fdroidserver.update.getsig(filename)
|
|
|
|
|
self.assertEqual(good_fingerprint, sig,
|
|
|
|
|
"python sig was: " + str(sig))
|
|
|
|
|
|
2016-06-20 13:41:30 +02:00
|
|
|
|
def testScanApksAndObbs(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2020-09-10 02:37:30 +02:00
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'repo'), 'repo')
|
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'metadata'), 'metadata')
|
2016-06-14 11:43:07 +02:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
config['ndk_paths'] = dict()
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2016-06-14 11:43:07 +02:00
|
|
|
|
fdroidserver.update.options.clean = True
|
2016-06-20 13:41:30 +02:00
|
|
|
|
fdroidserver.update.options.delete_unknown = True
|
2016-06-14 11:43:07 +02:00
|
|
|
|
|
2020-11-17 14:17:08 +01:00
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
2016-06-14 11:43:07 +02:00
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
2017-06-14 16:12:25 +02:00
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
|
2019-01-30 16:48:35 +01:00
|
|
|
|
self.assertEqual(len(apks), 17)
|
2019-01-09 22:41:59 +01:00
|
|
|
|
apk = apks[1]
|
2017-06-27 23:33:24 +02:00
|
|
|
|
self.assertEqual(apk['packageName'], 'com.politedroid')
|
|
|
|
|
self.assertEqual(apk['versionCode'], 3)
|
2019-02-01 09:34:57 +01:00
|
|
|
|
self.assertEqual(apk['minSdkVersion'], 3)
|
2018-10-10 12:00:41 +02:00
|
|
|
|
self.assertIsNone(apk.get('targetSdkVersion'))
|
2017-06-27 23:33:24 +02:00
|
|
|
|
self.assertFalse('maxSdkVersion' in apk)
|
2019-01-09 22:41:59 +01:00
|
|
|
|
apk = apks[8]
|
2017-06-27 23:33:24 +02:00
|
|
|
|
self.assertEqual(apk['packageName'], 'obb.main.oldversion')
|
|
|
|
|
self.assertEqual(apk['versionCode'], 1444412523)
|
2019-02-01 09:34:57 +01:00
|
|
|
|
self.assertEqual(apk['minSdkVersion'], 4)
|
|
|
|
|
self.assertEqual(apk['targetSdkVersion'], 18)
|
2016-06-14 11:43:07 +02:00
|
|
|
|
self.assertFalse('maxSdkVersion' in apk)
|
|
|
|
|
|
2016-06-20 13:41:30 +02:00
|
|
|
|
fdroidserver.update.insert_obbs('repo', apps, apks)
|
|
|
|
|
for apk in apks:
|
2016-11-29 13:40:21 +01:00
|
|
|
|
if apk['packageName'] == 'obb.mainpatch.current':
|
2016-06-20 13:41:30 +02:00
|
|
|
|
self.assertEqual(apk.get('obbMainFile'), 'main.1619.obb.mainpatch.current.obb')
|
|
|
|
|
self.assertEqual(apk.get('obbPatchFile'), 'patch.1619.obb.mainpatch.current.obb')
|
2016-11-29 13:40:21 +01:00
|
|
|
|
elif apk['packageName'] == 'obb.main.oldversion':
|
2016-06-20 13:41:30 +02:00
|
|
|
|
self.assertEqual(apk.get('obbMainFile'), 'main.1434483388.obb.main.oldversion.obb')
|
|
|
|
|
self.assertIsNone(apk.get('obbPatchFile'))
|
2016-11-29 13:40:21 +01:00
|
|
|
|
elif apk['packageName'] == 'obb.main.twoversions':
|
2016-06-20 13:41:30 +02:00
|
|
|
|
self.assertIsNone(apk.get('obbPatchFile'))
|
2016-11-29 13:40:21 +01:00
|
|
|
|
if apk['versionCode'] == 1101613:
|
2016-06-20 13:41:30 +02:00
|
|
|
|
self.assertEqual(apk.get('obbMainFile'), 'main.1101613.obb.main.twoversions.obb')
|
2016-11-29 13:40:21 +01:00
|
|
|
|
elif apk['versionCode'] == 1101615:
|
2016-06-20 13:41:30 +02:00
|
|
|
|
self.assertEqual(apk.get('obbMainFile'), 'main.1101615.obb.main.twoversions.obb')
|
2016-11-29 13:40:21 +01:00
|
|
|
|
elif apk['versionCode'] == 1101617:
|
2016-06-20 13:41:30 +02:00
|
|
|
|
self.assertEqual(apk.get('obbMainFile'), 'main.1101615.obb.main.twoversions.obb')
|
|
|
|
|
else:
|
|
|
|
|
self.assertTrue(False)
|
2016-11-29 13:40:21 +01:00
|
|
|
|
elif apk['packageName'] == 'info.guardianproject.urzip':
|
2016-06-20 13:41:30 +02:00
|
|
|
|
self.assertIsNone(apk.get('obbMainFile'))
|
|
|
|
|
self.assertIsNone(apk.get('obbPatchFile'))
|
|
|
|
|
|
2018-09-03 18:07:40 +02:00
|
|
|
|
def test_apkcache_json(self):
|
|
|
|
|
"""test the migration from pickle to json"""
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2020-09-10 15:51:47 +02:00
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'repo'), 'repo')
|
2018-09-03 18:07:40 +02:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
config['ndk_paths'] = dict()
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2018-09-03 18:07:40 +02:00
|
|
|
|
fdroidserver.update.options.clean = True
|
|
|
|
|
fdroidserver.update.options.delete_unknown = True
|
|
|
|
|
|
2020-11-17 14:17:08 +01:00
|
|
|
|
fdroidserver.metadata.read_metadata()
|
2018-09-03 18:07:40 +02:00
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
apkcache = fdroidserver.update.get_cache()
|
|
|
|
|
self.assertEqual(2, len(apkcache))
|
|
|
|
|
self.assertEqual(fdroidserver.update.METADATA_VERSION, apkcache["METADATA_VERSION"])
|
|
|
|
|
self.assertEqual(fdroidserver.update.options.allow_disabled_algorithms,
|
|
|
|
|
apkcache['allow_disabled_algorithms'])
|
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks(apkcache, 'repo', knownapks, False)
|
|
|
|
|
fdroidserver.update.write_cache(apkcache)
|
|
|
|
|
|
|
|
|
|
fdroidserver.update.options.clean = False
|
|
|
|
|
read_from_json = fdroidserver.update.get_cache()
|
2019-01-30 16:48:35 +01:00
|
|
|
|
self.assertEqual(19, len(read_from_json))
|
2018-09-03 18:07:40 +02:00
|
|
|
|
for f in glob.glob('repo/*.apk'):
|
|
|
|
|
self.assertTrue(os.path.basename(f) in read_from_json)
|
|
|
|
|
|
|
|
|
|
fdroidserver.update.options.clean = True
|
|
|
|
|
reset = fdroidserver.update.get_cache()
|
|
|
|
|
self.assertEqual(2, len(reset))
|
|
|
|
|
|
2020-11-10 16:24:19 +01:00
|
|
|
|
def test_scan_repo_files(self):
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2020-11-10 16:24:19 +01:00
|
|
|
|
os.mkdir('repo')
|
2020-11-20 11:19:01 +01:00
|
|
|
|
filename = 'Norway_bouvet_europe_2.obf.zip'
|
2020-11-10 16:24:19 +01:00
|
|
|
|
shutil.copy(os.path.join(self.basedir, filename), 'repo')
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
files, fcachechanged = fdroidserver.update.scan_repo_files(dict(), 'repo', knownapks, False)
|
|
|
|
|
self.assertTrue(fcachechanged)
|
|
|
|
|
|
|
|
|
|
info = files[0]
|
|
|
|
|
self.assertEqual(filename, info['apkName'])
|
|
|
|
|
self.assertEqual(datetime, type(info['added']))
|
|
|
|
|
self.assertEqual(os.path.getsize(os.path.join('repo', filename)), info['size'])
|
2021-06-07 11:49:21 +02:00
|
|
|
|
self.assertEqual(
|
|
|
|
|
'531190bdbc07e77d5577249949106f32dac7f62d38d66d66c3ae058be53a729d',
|
|
|
|
|
info['hash'],
|
|
|
|
|
)
|
2020-11-10 16:24:19 +01:00
|
|
|
|
|
|
|
|
|
def test_read_added_date_from_all_apks(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'repo'), 'repo')
|
2020-11-10 16:24:19 +01:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
2020-11-10 16:24:19 +01:00
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks)
|
|
|
|
|
fdroidserver.update.read_added_date_from_all_apks(apps, apks)
|
|
|
|
|
|
|
|
|
|
def test_apply_info_from_latest_apk(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'repo'), 'repo')
|
2020-11-10 16:24:19 +01:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
2022-11-25 12:18:50 +01:00
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2020-11-10 16:24:19 +01:00
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks)
|
|
|
|
|
fdroidserver.update.apply_info_from_latest_apk(apps, apks)
|
|
|
|
|
|
2017-06-14 16:12:25 +02:00
|
|
|
|
def test_scan_apk(self):
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
2018-04-16 11:35:30 +02:00
|
|
|
|
fdroidserver.common.config = config
|
2017-06-14 16:12:25 +02:00
|
|
|
|
fdroidserver.update.config = config
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.basedir)
|
2019-01-30 16:48:35 +01:00
|
|
|
|
|
2021-03-22 14:51:59 +01:00
|
|
|
|
if 'apksigner' in config:
|
2020-10-14 17:04:26 +02:00
|
|
|
|
apk_info = fdroidserver.update.scan_apk('v2.only.sig_2.apk')
|
2018-09-17 23:25:03 +02:00
|
|
|
|
self.assertIsNone(apk_info.get('maxSdkVersion'))
|
2020-10-14 17:04:26 +02:00
|
|
|
|
self.assertEqual(apk_info.get('versionName'), 'v2-only')
|
|
|
|
|
self.assertEqual(apk_info.get('versionCode'), 2)
|
|
|
|
|
else:
|
|
|
|
|
print('WARNING: skipping v2-only test since apksigner cannot be found')
|
|
|
|
|
apk_info = fdroidserver.update.scan_apk('repo/v1.v2.sig_1020.apk')
|
|
|
|
|
self.assertIsNone(apk_info.get('maxSdkVersion'))
|
|
|
|
|
self.assertEqual(apk_info.get('versionName'), 'v1+2')
|
|
|
|
|
self.assertEqual(apk_info.get('versionCode'), 1020)
|
|
|
|
|
|
|
|
|
|
apk_info = fdroidserver.update.scan_apk('repo/souch.smsbypass_9.apk')
|
|
|
|
|
self.assertIsNone(apk_info.get('maxSdkVersion'))
|
|
|
|
|
self.assertEqual(apk_info.get('versionName'), '0.9')
|
|
|
|
|
|
|
|
|
|
apk_info = fdroidserver.update.scan_apk('repo/duplicate.permisssions_9999999.apk')
|
|
|
|
|
self.assertEqual(apk_info.get('versionName'), '')
|
|
|
|
|
self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable/ic_launcher.png',
|
|
|
|
|
'-1': 'res/drawable/ic_launcher.png'})
|
|
|
|
|
|
|
|
|
|
apk_info = fdroidserver.update.scan_apk('org.dyndns.fules.ck_20.apk')
|
|
|
|
|
self.assertEqual(apk_info['icons_src'], {'240': 'res/drawable-hdpi-v4/icon_launcher.png',
|
|
|
|
|
'120': 'res/drawable-ldpi-v4/icon_launcher.png',
|
|
|
|
|
'160': 'res/drawable-mdpi-v4/icon_launcher.png',
|
|
|
|
|
'-1': 'res/drawable-mdpi-v4/icon_launcher.png'})
|
|
|
|
|
self.assertEqual(apk_info['icons'], {})
|
|
|
|
|
self.assertEqual(apk_info['features'], [])
|
2023-04-21 10:00:40 +02:00
|
|
|
|
self.assertEqual(apk_info['antiFeatures'], dict())
|
2020-10-14 17:04:26 +02:00
|
|
|
|
self.assertEqual(apk_info['versionName'], 'v1.6pre2')
|
|
|
|
|
self.assertEqual(apk_info['hash'],
|
|
|
|
|
'897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8')
|
|
|
|
|
self.assertEqual(apk_info['packageName'], 'org.dyndns.fules.ck')
|
|
|
|
|
self.assertEqual(apk_info['versionCode'], 20)
|
|
|
|
|
self.assertEqual(apk_info['size'], 132453)
|
|
|
|
|
self.assertEqual(apk_info['nativecode'],
|
|
|
|
|
['arm64-v8a', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'x86', 'x86_64'])
|
|
|
|
|
self.assertEqual(apk_info['minSdkVersion'], 7)
|
|
|
|
|
self.assertEqual(apk_info['sig'], '9bf7a6a67f95688daec75eab4b1436ac')
|
|
|
|
|
self.assertEqual(apk_info['hashType'], 'sha256')
|
|
|
|
|
self.assertEqual(apk_info['targetSdkVersion'], 8)
|
|
|
|
|
|
|
|
|
|
apk_info = fdroidserver.update.scan_apk('org.bitbucket.tickytacky.mirrormirror_4.apk')
|
|
|
|
|
self.assertEqual(apk_info.get('versionName'), '1.0.3')
|
|
|
|
|
self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable-mdpi/mirror.png',
|
|
|
|
|
'-1': 'res/drawable-mdpi/mirror.png'})
|
|
|
|
|
|
|
|
|
|
apk_info = fdroidserver.update.scan_apk('repo/info.zwanenburg.caffeinetile_4.apk')
|
|
|
|
|
self.assertEqual(apk_info.get('versionName'), '1.3')
|
2018-02-14 13:44:58 +01:00
|
|
|
|
self.assertEqual(apk_info['icons_src'], {})
|
2020-10-14 17:04:26 +02:00
|
|
|
|
|
|
|
|
|
apk_info = fdroidserver.update.scan_apk('repo/com.politedroid_6.apk')
|
|
|
|
|
self.assertEqual(apk_info.get('versionName'), '1.5')
|
|
|
|
|
self.assertEqual(apk_info['icons_src'], {'120': 'res/drawable-ldpi-v4/icon.png',
|
|
|
|
|
'160': 'res/drawable-mdpi-v4/icon.png',
|
|
|
|
|
'240': 'res/drawable-hdpi-v4/icon.png',
|
|
|
|
|
'320': 'res/drawable-xhdpi-v4/icon.png',
|
|
|
|
|
'-1': 'res/drawable-mdpi-v4/icon.png'})
|
|
|
|
|
|
|
|
|
|
apk_info = fdroidserver.update.scan_apk('SpeedoMeterApp.main_1.apk')
|
|
|
|
|
self.assertEqual(apk_info.get('versionName'), '1.0')
|
|
|
|
|
self.assertEqual(apk_info['icons_src'], {})
|
2018-03-11 22:09:09 +01:00
|
|
|
|
|
2019-02-01 09:34:57 +01:00
|
|
|
|
def test_scan_apk_no_min_target(self):
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
apk_info = fdroidserver.update.scan_apk('repo/no.min.target.sdk_987.apk')
|
|
|
|
|
self.maxDiff = None
|
2022-09-14 08:42:23 +02:00
|
|
|
|
expected = {
|
2019-02-01 09:34:57 +01:00
|
|
|
|
'icons': {},
|
|
|
|
|
'icons_src': {'-1': 'res/drawable/ic_launcher.png',
|
|
|
|
|
'160': 'res/drawable/ic_launcher.png'},
|
|
|
|
|
'name': 'No minSdkVersion or targetSdkVersion',
|
|
|
|
|
'signer': '32a23624c201b949f085996ba5ed53d40f703aca4989476949cae891022e0ed6',
|
|
|
|
|
'hashType': 'sha256',
|
|
|
|
|
'packageName': 'no.min.target.sdk',
|
|
|
|
|
'features': [],
|
2023-04-21 10:00:40 +02:00
|
|
|
|
'antiFeatures': dict(),
|
2019-02-01 09:34:57 +01:00
|
|
|
|
'size': 14102,
|
|
|
|
|
'sig': 'b4964fd759edaa54e65bb476d0276880',
|
|
|
|
|
'versionName': '1.2-fake',
|
|
|
|
|
'uses-permission-sdk-23': [],
|
|
|
|
|
'hash': 'e2e1dc1d550df2b5bc383860139207258645b5540abeccd305ed8b2cb6459d2c',
|
|
|
|
|
'versionCode': 987,
|
|
|
|
|
'minSdkVersion': 3,
|
|
|
|
|
'uses-permission': [
|
|
|
|
|
fdroidserver.update.UsesPermission(name='android.permission.WRITE_EXTERNAL_STORAGE',
|
|
|
|
|
maxSdkVersion=None),
|
|
|
|
|
fdroidserver.update.UsesPermission(name='android.permission.READ_PHONE_STATE',
|
|
|
|
|
maxSdkVersion=None),
|
|
|
|
|
fdroidserver.update.UsesPermission(name='android.permission.READ_EXTERNAL_STORAGE',
|
2022-09-14 08:42:23 +02:00
|
|
|
|
maxSdkVersion=None),
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
if config.get('ipfs_cid'):
|
|
|
|
|
expected['ipfsCIDv1'] = 'bafybeidwxseoagnew3gtlasttqovl7ciuwxaud5a5p4a5pzpbrfcfj2gaa'
|
|
|
|
|
|
|
|
|
|
self.assertDictEqual(apk_info, expected)
|
2019-02-01 09:34:57 +01:00
|
|
|
|
|
2017-06-14 16:12:25 +02:00
|
|
|
|
def test_scan_apk_no_sig(self):
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
2022-09-14 08:42:23 +02:00
|
|
|
|
fdroidserver.common.config = config
|
2017-06-14 16:12:25 +02:00
|
|
|
|
fdroidserver.update.config = config
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.basedir)
|
2017-06-14 16:12:25 +02:00
|
|
|
|
if os.path.basename(os.getcwd()) != 'tests':
|
|
|
|
|
raise Exception('This test must be run in the "tests/" subdir')
|
|
|
|
|
|
|
|
|
|
with self.assertRaises(fdroidserver.exception.BuildException):
|
|
|
|
|
fdroidserver.update.scan_apk('urzip-release-unsigned.apk')
|
|
|
|
|
|
2020-12-08 09:19:25 +01:00
|
|
|
|
def test_scan_apk_bad_zip(self):
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2020-12-08 09:19:25 +01:00
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
apkfile = 'repo/badzip_1.apk'
|
|
|
|
|
with open(apkfile, 'w') as fp:
|
|
|
|
|
fp.write('this is not a zip file')
|
|
|
|
|
with self.assertRaises(fdroidserver.exception.BuildException):
|
|
|
|
|
fdroidserver.update.scan_apk(apkfile)
|
|
|
|
|
|
2021-10-01 16:01:39 +02:00
|
|
|
|
@unittest.skipUnless(
|
|
|
|
|
os.path.exists('tests/SystemWebView-repack.apk'), "file too big for sdist"
|
|
|
|
|
)
|
|
|
|
|
def test_scan_apk_bad_icon_id(self):
|
|
|
|
|
"""Some APKs can produce an exception when extracting the icon
|
|
|
|
|
|
|
|
|
|
This kind of parsing exception should be reported then ignored
|
|
|
|
|
so that working APKs can be included in the index. There are
|
|
|
|
|
so many weird things that make it into APKs, that does not
|
|
|
|
|
automatically disqualify them from inclusion. For example:
|
|
|
|
|
|
|
|
|
|
ValueError: invalid literal for int() with base 16: '<0x801FF, type 0x07>'
|
|
|
|
|
|
|
|
|
|
The test APK was made from:
|
|
|
|
|
https://gitlab.com/fdroid/fdroidserver/-/merge_requests/1018#note_690565333
|
|
|
|
|
It was then stripped down by doing:
|
|
|
|
|
|
|
|
|
|
* mkdir SystemWebView
|
|
|
|
|
* cd SystemWebView/
|
|
|
|
|
* unzip ../SystemWebView.apk
|
|
|
|
|
* rm -rf META-INF/ lib assets/icudtl.dat assets/stored-locales/
|
|
|
|
|
* jar cf ../SystemWebView-repack.apk *
|
|
|
|
|
"""
|
|
|
|
|
# reset the state, perhaps this should be in setUp()
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
|
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
apkfile = 'repo/SystemWebView-repack.apk'
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, os.path.basename(apkfile)), apkfile)
|
|
|
|
|
fdroidserver.update.scan_apk(apkfile)
|
|
|
|
|
|
2023-03-20 14:24:59 +01:00
|
|
|
|
def test_scan_apk_bad_namespace_in_manifest(self):
|
|
|
|
|
"""Some APKs can produce an exception when parsing the AndroidManifest.xml
|
|
|
|
|
|
|
|
|
|
This kind of parsing exception should be reported then ignored
|
|
|
|
|
so that working APKs can be included in the index. There are
|
|
|
|
|
so many weird things that make it into APKs, that does not
|
|
|
|
|
automatically disqualify them from inclusion.
|
|
|
|
|
|
|
|
|
|
This APK has <uses-permission> elements with messed up namespaces:
|
|
|
|
|
<uses-permission xmlns:n1="android" n1:name="android.permission.VIBRATE"/>
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
# reset the state, perhaps this should be in setUp()
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
|
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
apkfile = 'repo/org.sajeg.fallingblocks_3.apk'
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, os.path.basename(apkfile)), apkfile)
|
|
|
|
|
fdroidserver.update.scan_apk(apkfile)
|
|
|
|
|
|
2017-06-14 16:12:25 +02:00
|
|
|
|
def test_process_apk(self):
|
2017-04-13 14:18:48 +02:00
|
|
|
|
def _build_yaml_representer(dumper, data):
|
|
|
|
|
'''Creates a YAML representation of a Build instance'''
|
|
|
|
|
return dumper.represent_dict(data)
|
|
|
|
|
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
shutil.copytree(self.basedir, 'tests')
|
2017-04-13 14:18:48 +02:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
2022-09-14 08:42:23 +02:00
|
|
|
|
fdroidserver.common.config = config
|
2017-04-13 14:18:48 +02:00
|
|
|
|
fdroidserver.update.config = config
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir("tests")
|
2017-04-13 14:18:48 +02:00
|
|
|
|
|
|
|
|
|
config['ndk_paths'] = dict()
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
2021-07-23 19:16:56 +02:00
|
|
|
|
|
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2017-04-13 14:18:48 +02:00
|
|
|
|
fdroidserver.update.options.clean = True
|
|
|
|
|
fdroidserver.update.options.delete_unknown = True
|
|
|
|
|
|
|
|
|
|
for icon_dir in fdroidserver.update.get_all_icon_dirs('repo'):
|
|
|
|
|
if not os.path.exists(icon_dir):
|
|
|
|
|
os.makedirs(icon_dir)
|
|
|
|
|
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
apkList = ['../urzip.apk', '../org.dyndns.fules.ck_20.apk']
|
|
|
|
|
|
|
|
|
|
for apkName in apkList:
|
2017-06-14 16:12:25 +02:00
|
|
|
|
_, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo', knownapks,
|
|
|
|
|
False)
|
2017-04-13 14:18:48 +02:00
|
|
|
|
# Don't care about the date added to the repo and relative apkName
|
2020-11-10 16:24:19 +01:00
|
|
|
|
self.assertEqual(datetime, type(apk['added']))
|
2017-04-13 14:18:48 +02:00
|
|
|
|
del apk['added']
|
|
|
|
|
del apk['apkName']
|
|
|
|
|
|
2017-06-14 16:12:25 +02:00
|
|
|
|
# ensure that icons have been extracted properly
|
|
|
|
|
if apkName == '../urzip.apk':
|
|
|
|
|
self.assertEqual(apk['icon'], 'info.guardianproject.urzip.100.png')
|
|
|
|
|
if apkName == '../org.dyndns.fules.ck_20.apk':
|
|
|
|
|
self.assertEqual(apk['icon'], 'org.dyndns.fules.ck.20.png')
|
|
|
|
|
for density in fdroidserver.update.screen_densities:
|
2021-06-07 11:49:21 +02:00
|
|
|
|
icon_path = os.path.join(
|
|
|
|
|
fdroidserver.update.get_icon_dir('repo', density), apk['icon']
|
|
|
|
|
)
|
2017-06-14 16:12:25 +02:00
|
|
|
|
self.assertTrue(os.path.isfile(icon_path))
|
|
|
|
|
self.assertTrue(os.path.getsize(icon_path) > 1)
|
|
|
|
|
|
2017-04-13 14:18:48 +02:00
|
|
|
|
savepath = os.path.join('metadata', 'apk', apk['packageName'] + '.yaml')
|
|
|
|
|
# Uncomment to save APK metadata
|
|
|
|
|
# with open(savepath, 'w') as f:
|
|
|
|
|
# yaml.add_representer(fdroidserver.metadata.Build, _build_yaml_representer)
|
|
|
|
|
# yaml.dump(apk, f, default_flow_style=False)
|
|
|
|
|
|
2021-01-23 13:36:41 +01:00
|
|
|
|
# CFullLoader doesn't always work
|
|
|
|
|
# https://github.com/yaml/pyyaml/issues/266#issuecomment-559116876
|
|
|
|
|
TestLoader = FullLoader
|
|
|
|
|
try:
|
|
|
|
|
testyaml = '- !!python/object/new:fdroidserver.update.UsesPermission\n - test\n - null'
|
|
|
|
|
from_yaml = yaml.load(testyaml, Loader=TestLoader)
|
|
|
|
|
except yaml.constructor.ConstructorError:
|
|
|
|
|
from yaml import UnsafeLoader as TestLoader
|
|
|
|
|
|
2017-04-13 14:18:48 +02:00
|
|
|
|
with open(savepath, 'r') as f:
|
2021-01-23 13:36:41 +01:00
|
|
|
|
from_yaml = yaml.load(f, Loader=TestLoader)
|
2017-04-13 14:18:48 +02:00
|
|
|
|
self.maxDiff = None
|
2022-09-14 08:42:23 +02:00
|
|
|
|
if not config.get('ipfs_cid'):
|
|
|
|
|
del from_yaml['ipfsCIDv1'] # handle when ipfs_cid is not installed
|
2019-02-01 09:34:57 +01:00
|
|
|
|
self.assertEqual(apk, from_yaml)
|
2017-04-13 14:18:48 +02:00
|
|
|
|
|
2017-06-14 16:12:25 +02:00
|
|
|
|
def test_process_apk_signed_by_disabled_algorithms(self):
|
2017-06-27 21:40:39 +02:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
|
|
|
|
config['ndk_paths'] = dict()
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2017-06-27 21:40:39 +02:00
|
|
|
|
fdroidserver.update.options.clean = True
|
|
|
|
|
fdroidserver.update.options.verbose = True
|
|
|
|
|
fdroidserver.update.options.delete_unknown = True
|
|
|
|
|
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
2017-12-14 11:06:22 +01:00
|
|
|
|
|
2022-11-22 17:17:45 +01:00
|
|
|
|
with tempfile.TemporaryDirectory() as tmptestsdir, TmpCwd(tmptestsdir):
|
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
os.mkdir('archive')
|
|
|
|
|
# setup the repo, create icons dirs, etc.
|
|
|
|
|
fdroidserver.update.process_apks({}, 'repo', knownapks)
|
|
|
|
|
fdroidserver.update.process_apks({}, 'archive', knownapks)
|
|
|
|
|
|
|
|
|
|
disabledsigs = ['org.bitbucket.tickytacky.mirrormirror_2.apk']
|
|
|
|
|
for apkName in disabledsigs:
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, apkName),
|
|
|
|
|
os.path.join(tmptestsdir, 'repo'))
|
|
|
|
|
|
|
|
|
|
skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo',
|
|
|
|
|
knownapks,
|
|
|
|
|
allow_disabled_algorithms=True,
|
|
|
|
|
archive_bad_sig=False)
|
|
|
|
|
self.assertFalse(skip)
|
|
|
|
|
self.assertIsNotNone(apk)
|
|
|
|
|
self.assertTrue(cachechanged)
|
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('archive', apkName)))
|
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('repo', apkName)))
|
|
|
|
|
|
|
|
|
|
if os.path.exists('/usr/bin/apksigner') or 'apksigner' in config:
|
|
|
|
|
print('SKIPPING: apksigner installed and it allows MD5 signatures')
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
javac = config['jarsigner'].replace('jarsigner', 'javac')
|
|
|
|
|
v = subprocess.check_output([javac, '-version'], stderr=subprocess.STDOUT)[6:-1].decode('utf-8')
|
2023-12-08 09:48:11 +01:00
|
|
|
|
if LooseVersion(v) < LooseVersion('1.8.0_132'):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
print('SKIPPING: running tests with old Java (' + v + ')')
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# this test only works on systems with fully updated Java/jarsigner
|
|
|
|
|
# that has MD5 listed in jdk.jar.disabledAlgorithms in java.security
|
|
|
|
|
# https://blogs.oracle.com/java-platform-group/oracle-jre-will-no-longer-trust-md5-signed-code-by-default
|
|
|
|
|
skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo',
|
|
|
|
|
knownapks,
|
|
|
|
|
allow_disabled_algorithms=False,
|
|
|
|
|
archive_bad_sig=True)
|
|
|
|
|
self.assertTrue(skip)
|
|
|
|
|
self.assertIsNone(apk)
|
|
|
|
|
self.assertFalse(cachechanged)
|
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('archive', apkName)))
|
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('repo', apkName)))
|
|
|
|
|
|
|
|
|
|
skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'archive',
|
|
|
|
|
knownapks,
|
|
|
|
|
allow_disabled_algorithms=False,
|
|
|
|
|
archive_bad_sig=False)
|
|
|
|
|
self.assertFalse(skip)
|
|
|
|
|
self.assertIsNotNone(apk)
|
|
|
|
|
self.assertTrue(cachechanged)
|
|
|
|
|
self.assertTrue(os.path.exists(os.path.join('archive', apkName)))
|
|
|
|
|
self.assertFalse(os.path.exists(os.path.join('repo', apkName)))
|
|
|
|
|
|
|
|
|
|
# ensure that icons have been moved to the archive as well
|
|
|
|
|
for density in fdroidserver.update.screen_densities:
|
|
|
|
|
icon_path = os.path.join(fdroidserver.update.get_icon_dir('archive', density),
|
|
|
|
|
apk['icon'])
|
|
|
|
|
self.assertTrue(os.path.isfile(icon_path))
|
|
|
|
|
self.assertTrue(os.path.getsize(icon_path) > 1)
|
|
|
|
|
|
|
|
|
|
badsigs = ['urzip-badcert.apk', 'urzip-badsig.apk', 'urzip-release-unsigned.apk', ]
|
|
|
|
|
for apkName in badsigs:
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, apkName),
|
|
|
|
|
os.path.join(self.testdir, 'repo'))
|
|
|
|
|
|
|
|
|
|
skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo',
|
|
|
|
|
knownapks,
|
|
|
|
|
allow_disabled_algorithms=False,
|
|
|
|
|
archive_bad_sig=False)
|
|
|
|
|
self.assertTrue(skip)
|
|
|
|
|
self.assertIsNone(apk)
|
|
|
|
|
self.assertFalse(cachechanged)
|
2017-06-27 21:40:39 +02:00
|
|
|
|
|
2017-06-14 16:12:25 +02:00
|
|
|
|
def test_process_invalid_apk(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.basedir)
|
2017-04-03 18:07:49 +02:00
|
|
|
|
if os.path.basename(os.getcwd()) != 'tests':
|
|
|
|
|
raise Exception('This test must be run in the "tests/" subdir')
|
|
|
|
|
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
2020-09-10 02:37:30 +02:00
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2017-04-03 18:07:49 +02:00
|
|
|
|
fdroidserver.update.options.delete_unknown = False
|
|
|
|
|
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
apk = 'fake.ota.update_1234.zip' # this is not an APK, scanning should fail
|
2017-06-14 16:12:25 +02:00
|
|
|
|
(skip, apk, cachechanged) = fdroidserver.update.process_apk({}, apk, 'repo', knownapks,
|
|
|
|
|
False)
|
2017-04-03 18:07:49 +02:00
|
|
|
|
|
|
|
|
|
self.assertTrue(skip)
|
|
|
|
|
self.assertIsNone(apk)
|
|
|
|
|
self.assertFalse(cachechanged)
|
|
|
|
|
|
2021-07-20 22:31:27 +02:00
|
|
|
|
def test_get_apks_without_allowed_signatures(self):
|
|
|
|
|
"""Test when no AllowedAPKSigningKeys is specified"""
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'repo'), 'repo')
|
2021-07-20 22:31:27 +02:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.common.options = Options
|
2022-11-25 12:18:50 +01:00
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2021-07-20 22:31:27 +02:00
|
|
|
|
|
|
|
|
|
app = fdroidserver.metadata.App()
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
2022-11-22 17:17:45 +01:00
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks)
|
2021-07-20 22:31:27 +02:00
|
|
|
|
apkfile = 'v1.v2.sig_1020.apk'
|
|
|
|
|
(skip, apk, cachechanged) = fdroidserver.update.process_apk(
|
|
|
|
|
{}, apkfile, 'repo', knownapks, False
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
r = fdroidserver.update.get_apks_without_allowed_signatures(app, apk)
|
|
|
|
|
self.assertIsNone(r)
|
|
|
|
|
|
|
|
|
|
def test_get_apks_without_allowed_signatures_allowed(self):
|
|
|
|
|
"""Test when the APK matches the specified AllowedAPKSigningKeys"""
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'repo'), 'repo')
|
2021-07-20 22:31:27 +02:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
|
|
|
|
|
|
|
|
|
app = fdroidserver.metadata.App(
|
|
|
|
|
{
|
|
|
|
|
'AllowedAPKSigningKeys': '32a23624c201b949f085996ba5ed53d40f703aca4989476949cae891022e0ed6'
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
2022-11-22 17:17:45 +01:00
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks)
|
2021-07-20 22:31:27 +02:00
|
|
|
|
apkfile = 'v1.v2.sig_1020.apk'
|
|
|
|
|
(skip, apk, cachechanged) = fdroidserver.update.process_apk(
|
|
|
|
|
{}, apkfile, 'repo', knownapks, False
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
r = fdroidserver.update.get_apks_without_allowed_signatures(app, apk)
|
|
|
|
|
self.assertIsNone(r)
|
|
|
|
|
|
|
|
|
|
def test_get_apks_without_allowed_signatures_blocked(self):
|
|
|
|
|
"""Test when the APK does not match any specified AllowedAPKSigningKeys"""
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'repo'), 'repo')
|
2021-07-20 22:31:27 +02:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
|
|
|
|
|
|
|
|
|
app = fdroidserver.metadata.App(
|
|
|
|
|
{
|
|
|
|
|
'AllowedAPKSigningKeys': 'fa4edeadfa4edeadfa4edeadfa4edeadfa4edeadfa4edeadfa4edeadfa4edead'
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
2022-11-22 17:17:45 +01:00
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks)
|
2021-07-20 22:31:27 +02:00
|
|
|
|
apkfile = 'v1.v2.sig_1020.apk'
|
|
|
|
|
(skip, apk, cachechanged) = fdroidserver.update.process_apk(
|
|
|
|
|
{}, apkfile, 'repo', knownapks, False
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
r = fdroidserver.update.get_apks_without_allowed_signatures(app, apk)
|
|
|
|
|
self.assertEqual(apkfile, r)
|
|
|
|
|
|
|
|
|
|
def test_update_with_AllowedAPKSigningKeys(self):
|
|
|
|
|
"""Test that APKs without allowed signatures get deleted."""
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2021-07-20 22:31:27 +02:00
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
testapk = os.path.join('repo', 'com.politedroid_6.apk')
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, testapk), testapk)
|
|
|
|
|
os.mkdir('metadata')
|
|
|
|
|
metadatafile = os.path.join('metadata', 'com.politedroid.yml')
|
2021-12-29 13:29:00 +01:00
|
|
|
|
|
|
|
|
|
# Copy and manipulate metadata file
|
2021-07-20 22:31:27 +02:00
|
|
|
|
shutil.copy(os.path.join(self.basedir, metadatafile), metadatafile)
|
|
|
|
|
with open(metadatafile, 'a') as fp:
|
|
|
|
|
fp.write(
|
|
|
|
|
'\n\nAllowedAPKSigningKeys: 32a23624c201b949f085996ba5ed53d40f703aca4989476949cae891022e0ed6\n'
|
|
|
|
|
)
|
|
|
|
|
|
2021-12-29 13:29:00 +01:00
|
|
|
|
# Set up options
|
2021-07-20 22:31:27 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
2024-05-08 16:26:46 +02:00
|
|
|
|
config = fdroidserver.common.read_config()
|
2022-05-25 09:44:39 +02:00
|
|
|
|
if 'apksigner' not in config: # TODO remove me for buildserver-bullseye
|
|
|
|
|
self.skipTest('SKIPPING test_update_with_AllowedAPKSigningKeys, apksigner not installed!')
|
2021-07-20 22:31:27 +02:00
|
|
|
|
config['repo_keyalias'] = 'sova'
|
|
|
|
|
config['keystorepass'] = 'r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI='
|
|
|
|
|
config['keypass'] = 'r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI='
|
|
|
|
|
config['keystore'] = os.path.join(self.basedir, 'keystore.jks')
|
|
|
|
|
|
|
|
|
|
self.assertTrue(os.path.exists(testapk))
|
2021-12-29 13:29:00 +01:00
|
|
|
|
|
|
|
|
|
# Test for non-deletion
|
2021-07-20 22:31:27 +02:00
|
|
|
|
with mock.patch('sys.argv', ['fdroid update', '--delete-unknown']):
|
|
|
|
|
fdroidserver.update.main()
|
|
|
|
|
self.assertTrue(os.path.exists(testapk))
|
|
|
|
|
|
2021-12-29 13:29:00 +01:00
|
|
|
|
# Copy and manipulate metadata file again
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, metadatafile), metadatafile)
|
2021-07-20 22:31:27 +02:00
|
|
|
|
with open(metadatafile, 'a') as fp:
|
|
|
|
|
fp.write(
|
|
|
|
|
'\n\nAllowedAPKSigningKeys: fa4edeadfa4edeadfa4edeadfa4edeadfa4edeadfa4edeadfa4edeadfa4edead\n'
|
|
|
|
|
)
|
2021-12-29 13:29:00 +01:00
|
|
|
|
|
|
|
|
|
# Test for deletion
|
2021-07-20 22:31:27 +02:00
|
|
|
|
with mock.patch('sys.argv', ['fdroid update', '--delete-unknown']):
|
|
|
|
|
fdroidserver.update.main()
|
|
|
|
|
self.assertFalse(os.path.exists(testapk))
|
|
|
|
|
|
2017-06-27 23:55:38 +02:00
|
|
|
|
def test_translate_per_build_anti_features(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2020-09-10 02:37:30 +02:00
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'repo'), 'repo')
|
|
|
|
|
shutil.copytree(os.path.join(self.basedir, 'metadata'), 'metadata')
|
2017-06-27 23:55:38 +02:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
config['ndk_paths'] = dict()
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2017-06-27 23:55:38 +02:00
|
|
|
|
fdroidserver.update.options.clean = True
|
|
|
|
|
fdroidserver.update.options.delete_unknown = True
|
|
|
|
|
|
2020-11-17 14:17:08 +01:00
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
2017-06-27 23:55:38 +02:00
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
|
|
|
|
|
fdroidserver.update.translate_per_build_anti_features(apps, apks)
|
2019-01-30 16:48:35 +01:00
|
|
|
|
self.assertEqual(len(apks), 17)
|
2017-06-27 23:55:38 +02:00
|
|
|
|
foundtest = False
|
|
|
|
|
for apk in apks:
|
|
|
|
|
if apk['packageName'] == 'com.politedroid' and apk['versionCode'] == 3:
|
|
|
|
|
antiFeatures = apk.get('antiFeatures')
|
|
|
|
|
self.assertTrue('KnownVuln' in antiFeatures)
|
|
|
|
|
self.assertEqual(3, len(antiFeatures))
|
|
|
|
|
foundtest = True
|
|
|
|
|
self.assertTrue(foundtest)
|
|
|
|
|
|
2017-07-27 00:34:13 +02:00
|
|
|
|
def test_create_metadata_from_template(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2017-07-27 00:34:13 +02:00
|
|
|
|
os.mkdir('repo')
|
2017-07-27 03:21:28 +02:00
|
|
|
|
os.mkdir('metadata')
|
2017-07-27 00:34:13 +02:00
|
|
|
|
shutil.copy(os.path.join(localmodule, 'tests', 'urzip.apk'), 'repo')
|
|
|
|
|
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
config['ndk_paths'] = dict()
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
2021-07-23 19:16:56 +02:00
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
2017-07-27 00:34:13 +02:00
|
|
|
|
fdroidserver.update.options.clean = True
|
|
|
|
|
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
|
|
|
|
|
self.assertEqual(1, len(apks))
|
|
|
|
|
apk = apks[0]
|
|
|
|
|
|
2017-07-27 03:21:28 +02:00
|
|
|
|
testfile = 'metadata/info.guardianproject.urzip.yml'
|
|
|
|
|
# create empty 0 byte .yml file, run read_metadata, it should work
|
|
|
|
|
open(testfile, 'a').close()
|
2020-11-17 14:17:08 +01:00
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
2017-07-27 03:21:28 +02:00
|
|
|
|
self.assertEqual(1, len(apps))
|
|
|
|
|
os.remove(testfile)
|
|
|
|
|
|
2017-07-27 00:34:13 +02:00
|
|
|
|
# test using internal template
|
2020-11-17 14:17:08 +01:00
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
2017-07-27 03:21:28 +02:00
|
|
|
|
self.assertEqual(0, len(apps))
|
2017-07-27 00:34:13 +02:00
|
|
|
|
fdroidserver.update.create_metadata_from_template(apk)
|
2017-07-27 03:21:28 +02:00
|
|
|
|
self.assertTrue(os.path.exists(testfile))
|
2020-11-17 14:17:08 +01:00
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
2017-07-27 00:34:13 +02:00
|
|
|
|
self.assertEqual(1, len(apps))
|
|
|
|
|
for app in apps.values():
|
|
|
|
|
self.assertEqual('urzip', app['Name'])
|
|
|
|
|
self.assertEqual(1, len(app['Categories']))
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# test using external template.yml
|
2017-07-27 03:21:28 +02:00
|
|
|
|
os.remove(testfile)
|
|
|
|
|
self.assertFalse(os.path.exists(testfile))
|
2022-11-22 17:17:45 +01:00
|
|
|
|
shutil.copy(os.path.join(localmodule, 'examples', 'template.yml'), self.testdir)
|
2017-07-27 00:34:13 +02:00
|
|
|
|
fdroidserver.update.create_metadata_from_template(apk)
|
2017-07-27 03:21:28 +02:00
|
|
|
|
self.assertTrue(os.path.exists(testfile))
|
2020-11-17 14:17:08 +01:00
|
|
|
|
apps = fdroidserver.metadata.read_metadata()
|
2017-07-27 00:34:13 +02:00
|
|
|
|
self.assertEqual(1, len(apps))
|
|
|
|
|
for app in apps.values():
|
|
|
|
|
self.assertEqual('urzip', app['Name'])
|
|
|
|
|
self.assertEqual(1, len(app['Categories']))
|
|
|
|
|
self.assertEqual('Internet', app['Categories'][0])
|
|
|
|
|
break
|
2017-07-27 03:21:28 +02:00
|
|
|
|
with open(testfile) as fp:
|
2020-09-10 02:37:05 +02:00
|
|
|
|
data = yaml.load(fp, Loader=SafeLoader)
|
2017-07-27 00:34:13 +02:00
|
|
|
|
self.assertEqual('urzip', data['Name'])
|
|
|
|
|
self.assertEqual('urzip', data['Summary'])
|
|
|
|
|
|
2017-12-11 18:36:21 +01:00
|
|
|
|
def test_has_known_vulnerability(self):
|
|
|
|
|
good = [
|
|
|
|
|
'org.bitbucket.tickytacky.mirrormirror_1.apk',
|
|
|
|
|
'org.bitbucket.tickytacky.mirrormirror_2.apk',
|
|
|
|
|
'org.bitbucket.tickytacky.mirrormirror_3.apk',
|
|
|
|
|
'org.bitbucket.tickytacky.mirrormirror_4.apk',
|
|
|
|
|
'org.dyndns.fules.ck_20.apk',
|
|
|
|
|
'urzip.apk',
|
|
|
|
|
'urzip-badcert.apk',
|
|
|
|
|
'urzip-badsig.apk',
|
|
|
|
|
'urzip-release.apk',
|
|
|
|
|
'urzip-release-unsigned.apk',
|
|
|
|
|
'repo/com.politedroid_3.apk',
|
|
|
|
|
'repo/com.politedroid_4.apk',
|
|
|
|
|
'repo/com.politedroid_5.apk',
|
|
|
|
|
'repo/com.politedroid_6.apk',
|
|
|
|
|
'repo/obb.main.oldversion_1444412523.apk',
|
|
|
|
|
'repo/obb.mainpatch.current_1619_another-release-key.apk',
|
|
|
|
|
'repo/obb.mainpatch.current_1619.apk',
|
|
|
|
|
'repo/obb.main.twoversions_1101613.apk',
|
|
|
|
|
'repo/obb.main.twoversions_1101615.apk',
|
|
|
|
|
'repo/obb.main.twoversions_1101617.apk',
|
2018-05-25 17:27:58 +02:00
|
|
|
|
'repo/urzip-; Рахма́, [rɐxˈmanʲɪnəf] سيرجي_رخمانينوف 谢·.apk',
|
2017-12-11 18:36:21 +01:00
|
|
|
|
]
|
|
|
|
|
for f in good:
|
|
|
|
|
self.assertFalse(fdroidserver.update.has_known_vulnerability(f))
|
|
|
|
|
with self.assertRaises(fdroidserver.exception.FDroidException):
|
|
|
|
|
fdroidserver.update.has_known_vulnerability('janus.apk')
|
|
|
|
|
|
2018-03-27 18:39:59 +02:00
|
|
|
|
def test_get_apk_icon_when_src_is_none(self):
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
|
|
|
|
# pylint: disable=protected-access
|
|
|
|
|
icons_src = fdroidserver.update._get_apk_icons_src('urzip-release.apk', None)
|
2023-02-02 15:58:15 +01:00
|
|
|
|
assert not icons_src
|
2018-03-27 18:39:59 +02:00
|
|
|
|
|
update: log errors on bad graphics, and then ignore the file
Python PIL is not so tolerant, so bad EXIF causes crashes:
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2088, in main
insert_localized_app_metadata(apps)
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 978, in insert_localized_app_metadata
_strip_and_copy_image(os.path.join(root, f), destdir)
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 754, in _strip_and_copy_image
in_image = Image.open(fp)
File "/usr/lib/python3/dist-packages/PIL/Image.py", line 2687, in open
% (filename if filename else fp))
OSError: cannot identify image file <_io.BufferedReader name='build/org.sw24softwares.starkeverben/fastlane/metadata/android/en-US/images/featureGraphic.png'>
2019-09-26 19:38:24 +02:00
|
|
|
|
def test_strip_and_copy_image(self):
|
|
|
|
|
in_file = os.path.join(self.basedir, 'metadata', 'info.guardianproject.urzip', 'en-US', 'images', 'icon.png')
|
2022-11-22 17:17:45 +01:00
|
|
|
|
out_file = os.path.join(self.testdir, 'icon.png')
|
update: log errors on bad graphics, and then ignore the file
Python PIL is not so tolerant, so bad EXIF causes crashes:
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2088, in main
insert_localized_app_metadata(apps)
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 978, in insert_localized_app_metadata
_strip_and_copy_image(os.path.join(root, f), destdir)
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 754, in _strip_and_copy_image
in_image = Image.open(fp)
File "/usr/lib/python3/dist-packages/PIL/Image.py", line 2687, in open
% (filename if filename else fp))
OSError: cannot identify image file <_io.BufferedReader name='build/org.sw24softwares.starkeverben/fastlane/metadata/android/en-US/images/featureGraphic.png'>
2019-09-26 19:38:24 +02:00
|
|
|
|
fdroidserver.update._strip_and_copy_image(in_file, out_file)
|
|
|
|
|
self.assertTrue(os.path.exists(out_file))
|
|
|
|
|
|
|
|
|
|
in_file = os.path.join(self.basedir, 'corrupt-featureGraphic.png')
|
2022-11-22 17:17:45 +01:00
|
|
|
|
out_file = os.path.join(self.testdir, 'corrupt-featureGraphic.png')
|
update: log errors on bad graphics, and then ignore the file
Python PIL is not so tolerant, so bad EXIF causes crashes:
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2088, in main
insert_localized_app_metadata(apps)
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 978, in insert_localized_app_metadata
_strip_and_copy_image(os.path.join(root, f), destdir)
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 754, in _strip_and_copy_image
in_image = Image.open(fp)
File "/usr/lib/python3/dist-packages/PIL/Image.py", line 2687, in open
% (filename if filename else fp))
OSError: cannot identify image file <_io.BufferedReader name='build/org.sw24softwares.starkeverben/fastlane/metadata/android/en-US/images/featureGraphic.png'>
2019-09-26 19:38:24 +02:00
|
|
|
|
fdroidserver.update._strip_and_copy_image(in_file, out_file)
|
|
|
|
|
self.assertFalse(os.path.exists(out_file))
|
|
|
|
|
|
2019-07-22 01:34:55 +02:00
|
|
|
|
def test_create_metadata_from_template_empty_keys(self):
|
|
|
|
|
apk = {'packageName': 'rocks.janicerand'}
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
|
|
|
|
|
os.mkdir('metadata')
|
|
|
|
|
with open('template.yml', 'w') as f:
|
2021-06-07 11:49:21 +02:00
|
|
|
|
f.write(
|
|
|
|
|
textwrap.dedent(
|
|
|
|
|
'''\
|
2019-07-22 01:34:55 +02:00
|
|
|
|
Disabled:
|
|
|
|
|
License:
|
|
|
|
|
AuthorName:
|
|
|
|
|
AuthorEmail:
|
|
|
|
|
AuthorWebSite:
|
|
|
|
|
WebSite:
|
|
|
|
|
SourceCode:
|
|
|
|
|
IssueTracker:
|
|
|
|
|
Translation:
|
|
|
|
|
Changelog:
|
|
|
|
|
Donate:
|
|
|
|
|
Bitcoin:
|
|
|
|
|
Litecoin:
|
|
|
|
|
Name:
|
|
|
|
|
AutoName:
|
|
|
|
|
Summary:
|
|
|
|
|
RequiresRoot:
|
|
|
|
|
RepoType:
|
|
|
|
|
Repo:
|
|
|
|
|
Binaries:
|
|
|
|
|
Builds:
|
|
|
|
|
ArchivePolicy:
|
|
|
|
|
AutoUpdateMode:
|
|
|
|
|
UpdateCheckMode:
|
|
|
|
|
UpdateCheckIgnore:
|
|
|
|
|
VercodeOperation:
|
|
|
|
|
UpdateCheckName:
|
|
|
|
|
UpdateCheckData:
|
|
|
|
|
CurrentVersion:
|
|
|
|
|
CurrentVersionCode:
|
|
|
|
|
NoSourceSince:
|
2021-06-07 11:49:21 +02:00
|
|
|
|
'''
|
|
|
|
|
)
|
|
|
|
|
)
|
2019-07-22 01:34:55 +02:00
|
|
|
|
fdroidserver.update.create_metadata_from_template(apk)
|
|
|
|
|
with open(os.path.join('metadata', 'rocks.janicerand.yml')) as f:
|
2020-09-10 02:37:05 +02:00
|
|
|
|
metadata_content = yaml.load(f, Loader=SafeLoader)
|
2019-07-22 01:34:55 +02:00
|
|
|
|
self.maxDiff = None
|
2021-06-07 11:49:21 +02:00
|
|
|
|
self.assertDictEqual(
|
|
|
|
|
metadata_content,
|
|
|
|
|
{
|
2023-05-25 19:05:57 +02:00
|
|
|
|
'ArchivePolicy': None,
|
2021-06-07 11:49:21 +02:00
|
|
|
|
'AuthorEmail': '',
|
|
|
|
|
'AuthorName': '',
|
|
|
|
|
'AuthorWebSite': '',
|
|
|
|
|
'AutoName': 'rocks.janicerand',
|
|
|
|
|
'AutoUpdateMode': '',
|
|
|
|
|
'Binaries': '',
|
|
|
|
|
'Bitcoin': '',
|
2022-09-14 03:45:19 +02:00
|
|
|
|
'Builds': None,
|
2021-06-07 11:49:21 +02:00
|
|
|
|
'Changelog': '',
|
|
|
|
|
'CurrentVersion': '',
|
2022-09-14 03:45:24 +02:00
|
|
|
|
'CurrentVersionCode': None,
|
2021-06-07 11:49:21 +02:00
|
|
|
|
'Disabled': '',
|
|
|
|
|
'Donate': '',
|
|
|
|
|
'IssueTracker': '',
|
|
|
|
|
'License': '',
|
|
|
|
|
'Litecoin': '',
|
|
|
|
|
'Name': 'rocks.janicerand',
|
|
|
|
|
'NoSourceSince': '',
|
|
|
|
|
'Repo': '',
|
|
|
|
|
'RepoType': '',
|
2023-05-09 13:43:33 +02:00
|
|
|
|
'RequiresRoot': None,
|
2021-06-07 11:49:21 +02:00
|
|
|
|
'SourceCode': '',
|
|
|
|
|
'Summary': 'rocks.janicerand',
|
|
|
|
|
'Translation': '',
|
|
|
|
|
'UpdateCheckData': '',
|
|
|
|
|
'UpdateCheckIgnore': '',
|
|
|
|
|
'UpdateCheckMode': '',
|
|
|
|
|
'UpdateCheckName': '',
|
2022-10-27 13:11:37 +02:00
|
|
|
|
'VercodeOperation': None,
|
2021-06-07 11:49:21 +02:00
|
|
|
|
'WebSite': '',
|
|
|
|
|
},
|
|
|
|
|
)
|
2019-07-22 01:34:55 +02:00
|
|
|
|
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
def test_insert_funding_yml_donation_links(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
os.mkdir('build')
|
2021-06-07 11:49:21 +02:00
|
|
|
|
content = textwrap.dedent(
|
|
|
|
|
"""
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
community_bridge: ''
|
|
|
|
|
custom: [LINK1, LINK2]
|
|
|
|
|
github: USERNAME
|
|
|
|
|
issuehunt: USERNAME
|
|
|
|
|
ko_fi: USERNAME
|
|
|
|
|
liberapay: USERNAME
|
|
|
|
|
open_collective: USERNAME
|
|
|
|
|
otechie: USERNAME
|
|
|
|
|
patreon: USERNAME
|
2021-06-07 11:49:21 +02:00
|
|
|
|
"""
|
|
|
|
|
)
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
app = fdroidserver.metadata.App()
|
|
|
|
|
app.id = 'fake.app.id'
|
|
|
|
|
apps = {app.id: app}
|
|
|
|
|
os.mkdir(os.path.join('build', app.id))
|
|
|
|
|
fdroidserver.update.insert_funding_yml_donation_links(apps)
|
|
|
|
|
for field in DONATION_FIELDS:
|
|
|
|
|
self.assertFalse(app.get(field))
|
|
|
|
|
with open(os.path.join('build', app.id, 'FUNDING.yml'), 'w') as fp:
|
|
|
|
|
fp.write(content)
|
|
|
|
|
|
|
|
|
|
fdroidserver.update.insert_funding_yml_donation_links(apps)
|
|
|
|
|
for field in DONATION_FIELDS:
|
|
|
|
|
self.assertIsNotNone(app.get(field), field)
|
|
|
|
|
self.assertEqual('LINK1', app.get('Donate'))
|
2020-06-16 14:39:46 +02:00
|
|
|
|
self.assertEqual('USERNAME', app.get('Liberapay'))
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
self.assertEqual('USERNAME', app.get('OpenCollective'))
|
|
|
|
|
|
|
|
|
|
app['Donate'] = 'keepme'
|
2020-06-16 14:39:46 +02:00
|
|
|
|
app['Liberapay'] = 'keepme'
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
app['OpenCollective'] = 'keepme'
|
|
|
|
|
fdroidserver.update.insert_funding_yml_donation_links(apps)
|
|
|
|
|
for field in DONATION_FIELDS:
|
|
|
|
|
self.assertEqual('keepme', app.get(field))
|
|
|
|
|
|
2020-06-24 20:54:16 +02:00
|
|
|
|
def test_insert_funding_yml_donation_links_one_at_a_time(self):
|
|
|
|
|
"""Exercise the FUNDING.yml code one entry at a time"""
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2020-06-24 20:54:16 +02:00
|
|
|
|
os.mkdir('build')
|
|
|
|
|
|
|
|
|
|
app = fdroidserver.metadata.App()
|
|
|
|
|
app.id = 'fake.app.id'
|
|
|
|
|
apps = {app.id: app}
|
|
|
|
|
os.mkdir(os.path.join('build', app.id))
|
|
|
|
|
fdroidserver.update.insert_funding_yml_donation_links(apps)
|
|
|
|
|
for field in DONATION_FIELDS:
|
|
|
|
|
self.assertIsNone(app.get(field))
|
|
|
|
|
|
2021-06-07 11:49:21 +02:00
|
|
|
|
content = textwrap.dedent(
|
|
|
|
|
"""
|
2020-06-24 20:54:16 +02:00
|
|
|
|
community_bridge: 'blah-de-blah'
|
|
|
|
|
github: USERNAME
|
|
|
|
|
issuehunt: USERNAME
|
|
|
|
|
ko_fi: USERNAME
|
|
|
|
|
liberapay: USERNAME
|
|
|
|
|
open_collective: USERNAME
|
|
|
|
|
patreon: USERNAME
|
2021-06-07 11:49:21 +02:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-06-24 20:54:16 +02:00
|
|
|
|
for line in content.split('\n'):
|
|
|
|
|
if not line:
|
|
|
|
|
continue
|
|
|
|
|
app = fdroidserver.metadata.App()
|
|
|
|
|
app.id = 'fake.app.id'
|
|
|
|
|
apps = {app.id: app}
|
|
|
|
|
with open(os.path.join('build', app.id, 'FUNDING.yml'), 'w') as fp:
|
|
|
|
|
fp.write(line)
|
2020-09-10 02:37:05 +02:00
|
|
|
|
data = yaml.load(line, Loader=SafeLoader)
|
2020-06-24 20:54:16 +02:00
|
|
|
|
fdroidserver.update.insert_funding_yml_donation_links(apps)
|
|
|
|
|
if 'liberapay' in data:
|
|
|
|
|
self.assertEqual(data['liberapay'], app.get('Liberapay'))
|
|
|
|
|
elif 'open_collective' in data:
|
|
|
|
|
self.assertEqual(data['open_collective'], app.get('OpenCollective'))
|
|
|
|
|
else:
|
|
|
|
|
for v in data.values():
|
|
|
|
|
self.assertEqual(app.get('Donate', '').split('/')[-1], v)
|
|
|
|
|
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
def test_insert_funding_yml_donation_links_with_corrupt_file(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
os.mkdir('build')
|
|
|
|
|
app = fdroidserver.metadata.App()
|
|
|
|
|
app.id = 'fake.app.id'
|
|
|
|
|
apps = {app.id: app}
|
|
|
|
|
os.mkdir(os.path.join('build', app.id))
|
|
|
|
|
with open(os.path.join('build', app.id, 'FUNDING.yml'), 'w') as fp:
|
2021-06-07 11:49:21 +02:00
|
|
|
|
fp.write(
|
|
|
|
|
textwrap.dedent(
|
|
|
|
|
"""
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
opencollective: foo
|
|
|
|
|
custom: []
|
|
|
|
|
liberapay: :
|
2021-06-07 11:49:21 +02:00
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
)
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
fdroidserver.update.insert_funding_yml_donation_links(apps)
|
|
|
|
|
for field in DONATION_FIELDS:
|
|
|
|
|
self.assertIsNone(app.get(field))
|
|
|
|
|
|
|
|
|
|
def test_sanitize_funding_yml(self):
|
|
|
|
|
with open(os.path.join(self.basedir, 'funding-usernames.yaml')) as fp:
|
2020-09-10 02:37:05 +02:00
|
|
|
|
data = yaml.load(fp, Loader=SafeLoader)
|
update: insert donation links based on FUNDING.yml
GitHub has specified FUNDING.yml, a file to include in a git repo for
pointing people to donation links. Since F-Droid also points people
to donation links, this parses them to fill out Donate:
and OpenCollective:. Specifying those in the metadata file takes
precedence over the FUNDING.yml. This follows the same pattern as how
`fdroid update` includes Fastlane/Triple-T metadata. This lets the
git repo maintain those specific donations links themselves.
https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#about-funding-files
The test file was generated using:
```python
import os, re, yaml
found = dict()
for root, dirs, files in os.walk('.'):
for f in files:
if f == 'FUNDING.yml':
with open(os.path.join(root, f)) as fp:
data = yaml.safe_load(fp)
for k, v in data.items():
if k not in found:
found[k] = set()
if not v:
continue
if isinstance(v, list):
for i in v:
found[k].add(i)
else:
found[k].add(v)
with open('gather-funding-names.yaml', 'w') as fp:
output = dict()
for k, v in found.items():
output[k] = sorted(v)
yaml.dump(output, fp, default_flow_style=False)
```
2019-11-06 09:03:27 +01:00
|
|
|
|
for k, entries in data.items():
|
|
|
|
|
for entry in entries:
|
|
|
|
|
if k in 'custom':
|
|
|
|
|
m = fdroidserver.update.sanitize_funding_yml_entry(entry)
|
|
|
|
|
else:
|
|
|
|
|
m = fdroidserver.update.sanitize_funding_yml_name(entry)
|
|
|
|
|
if k == 'bad':
|
|
|
|
|
self.assertIsNone(m)
|
|
|
|
|
else:
|
|
|
|
|
self.assertIsNotNone(m)
|
|
|
|
|
self.assertIsNone(fdroidserver.update.sanitize_funding_yml_entry('foo\nbar'))
|
|
|
|
|
self.assertIsNone(fdroidserver.update.sanitize_funding_yml_entry(
|
|
|
|
|
''.join(chr(random.randint(65, 90)) for _ in range(2049))))
|
|
|
|
|
|
|
|
|
|
# not recommended but valid entries
|
|
|
|
|
self.assertIsNotNone(fdroidserver.update.sanitize_funding_yml_entry(12345))
|
|
|
|
|
self.assertIsNotNone(fdroidserver.update.sanitize_funding_yml_entry(5.0))
|
|
|
|
|
self.assertIsNotNone(fdroidserver.update.sanitize_funding_yml_entry(' WhyIncludeWhitespace '))
|
|
|
|
|
self.assertIsNotNone(fdroidserver.update.sanitize_funding_yml_entry(['first', 'second']))
|
|
|
|
|
|
update: handle large, corrupt, or inaccessible fastlane/triple-t files
```
Traceback (most recent call last):
File "../fdroid", line 22, in <module>
fdroidserver.__main__.main()
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/__main__.py", line 227, in main
raise e
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/__main__.py", line 208, in main
mod.main()
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2340, in main
repoapps = prepare_apps(apps, apks, repodirs[0])
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2176, in prepare_apps
copy_triple_t_store_metadata(apps_with_packages)
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 1076, in copy_triple_t_store_metadata
_set_author_entry(app, 'authorWebSite', os.path.join(root, f))
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 784, in _set_author_entry
with open(f, errors='replace') as fp:
FileNotFoundError: [Errno 2] No such file or directory: 'build/player.efis.cfd/pfd/src/main/play/contact-website.txt'
```
2021-02-11 17:29:48 +01:00
|
|
|
|
def test_set_localized_text_entry(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
update: handle large, corrupt, or inaccessible fastlane/triple-t files
```
Traceback (most recent call last):
File "../fdroid", line 22, in <module>
fdroidserver.__main__.main()
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/__main__.py", line 227, in main
raise e
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/__main__.py", line 208, in main
mod.main()
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2340, in main
repoapps = prepare_apps(apps, apks, repodirs[0])
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2176, in prepare_apps
copy_triple_t_store_metadata(apps_with_packages)
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 1076, in copy_triple_t_store_metadata
_set_author_entry(app, 'authorWebSite', os.path.join(root, f))
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 784, in _set_author_entry
with open(f, errors='replace') as fp:
FileNotFoundError: [Errno 2] No such file or directory: 'build/player.efis.cfd/pfd/src/main/play/contact-website.txt'
```
2021-02-11 17:29:48 +01:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
|
|
|
|
|
|
|
|
|
files = {
|
|
|
|
|
'full-description.txt': 'description',
|
|
|
|
|
'short-description.txt': 'summary',
|
|
|
|
|
'title.txt': 'name',
|
|
|
|
|
'video-url.txt': 'video',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for f, key in files.items():
|
|
|
|
|
limit = config['char_limits'][key]
|
|
|
|
|
with open(f, 'w') as fp:
|
|
|
|
|
fp.write(''.join(random.choice(string.ascii_letters) for i in range(limit + 100)))
|
|
|
|
|
locale = 'ru_US'
|
|
|
|
|
app = dict()
|
|
|
|
|
fdroidserver.update._set_localized_text_entry(app, locale, key, f)
|
|
|
|
|
self.assertEqual(limit, len(app['localized'][locale][key]))
|
|
|
|
|
|
|
|
|
|
f = 'badlink-' + f
|
|
|
|
|
os.symlink('/path/to/nowhere', f)
|
|
|
|
|
app = dict()
|
|
|
|
|
fdroidserver.update._set_localized_text_entry(app, locale, key, f)
|
|
|
|
|
self.assertIsNone(app['localized'].get(locale, {}).get(key))
|
|
|
|
|
|
|
|
|
|
def test_set_author_entry(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
update: handle large, corrupt, or inaccessible fastlane/triple-t files
```
Traceback (most recent call last):
File "../fdroid", line 22, in <module>
fdroidserver.__main__.main()
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/__main__.py", line 227, in main
raise e
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/__main__.py", line 208, in main
mod.main()
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2340, in main
repoapps = prepare_apps(apps, apks, repodirs[0])
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 2176, in prepare_apps
copy_triple_t_store_metadata(apps_with_packages)
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 1076, in copy_triple_t_store_metadata
_set_author_entry(app, 'authorWebSite', os.path.join(root, f))
File "/var/lib/jenkins/userContent/reproducible/reproducible_fdroid_build_apps/fdroidserver/update.py", line 784, in _set_author_entry
with open(f, errors='replace') as fp:
FileNotFoundError: [Errno 2] No such file or directory: 'build/player.efis.cfd/pfd/src/main/play/contact-website.txt'
```
2021-02-11 17:29:48 +01:00
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
|
|
|
|
|
|
|
|
|
f = 'contact-website.txt'
|
|
|
|
|
key = 'author'
|
|
|
|
|
url = 'https://f-droid.org/'
|
|
|
|
|
limit = config['char_limits']['author']
|
|
|
|
|
with open(f, 'w') as fp:
|
|
|
|
|
fp.write(url)
|
|
|
|
|
fp.write('\n')
|
|
|
|
|
app = dict()
|
|
|
|
|
fdroidserver.update._set_author_entry(app, key, f)
|
|
|
|
|
self.assertEqual(url, app[key])
|
|
|
|
|
|
|
|
|
|
f = 'limits.txt'
|
|
|
|
|
key = 'author'
|
|
|
|
|
limit = config['char_limits']['author']
|
|
|
|
|
for key in ('authorEmail', 'authorPhone', 'authorWebSite'):
|
|
|
|
|
with open(f, 'w') as fp:
|
|
|
|
|
fp.write(''.join(random.choice(string.ascii_letters) for i in range(limit + 100)))
|
|
|
|
|
app = dict()
|
|
|
|
|
fdroidserver.update._set_author_entry(app, key, f)
|
|
|
|
|
self.assertEqual(limit, len(app[key]))
|
|
|
|
|
|
|
|
|
|
f = 'badlink.txt'
|
|
|
|
|
os.symlink('/path/to/nowhere', f)
|
|
|
|
|
app = dict()
|
|
|
|
|
fdroidserver.update._set_author_entry(app, key, f)
|
|
|
|
|
self.assertIsNone(app.get(key))
|
|
|
|
|
|
2021-03-19 15:44:43 +01:00
|
|
|
|
def test_status_update_json(self):
|
|
|
|
|
fdroidserver.common.config = {}
|
|
|
|
|
fdroidserver.update.config = {}
|
|
|
|
|
fdroidserver.update.options = Options
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
|
|
|
os.chdir(tmpdir)
|
|
|
|
|
with mock.patch('sys.argv', ['fdroid update', '']):
|
|
|
|
|
fdroidserver.update.status_update_json([], [])
|
|
|
|
|
with open('repo/status/update.json') as fp:
|
|
|
|
|
data = json.load(fp)
|
|
|
|
|
self.assertTrue('apksigner' in data)
|
|
|
|
|
|
|
|
|
|
fdroidserver.update.config = {
|
|
|
|
|
'apksigner': 'apksigner',
|
|
|
|
|
}
|
|
|
|
|
fdroidserver.update.status_update_json([], [])
|
|
|
|
|
with open('repo/status/update.json') as fp:
|
|
|
|
|
data = json.load(fp)
|
|
|
|
|
self.assertEqual(shutil.which(fdroidserver.update.config['apksigner']), data['apksigner'])
|
|
|
|
|
|
|
|
|
|
fdroidserver.update.config = {}
|
|
|
|
|
fdroidserver.common.fill_config_defaults(fdroidserver.update.config)
|
|
|
|
|
fdroidserver.update.status_update_json([], [])
|
|
|
|
|
with open('repo/status/update.json') as fp:
|
|
|
|
|
data = json.load(fp)
|
|
|
|
|
self.assertEqual(fdroidserver.update.config.get('apksigner'), data['apksigner'])
|
|
|
|
|
self.assertEqual(fdroidserver.update.config['jarsigner'], data['jarsigner'])
|
|
|
|
|
self.assertEqual(fdroidserver.update.config['keytool'], data['keytool'])
|
|
|
|
|
|
2022-05-08 12:41:23 +02:00
|
|
|
|
def test_scan_metadata_androguard(self):
|
|
|
|
|
|
|
|
|
|
def _create_apkmetadata_object(apkName):
|
|
|
|
|
"""Create an empty apk metadata object."""
|
|
|
|
|
apk = {}
|
|
|
|
|
apk['apkName'] = apkName
|
|
|
|
|
apk['uses-permission'] = []
|
|
|
|
|
apk['uses-permission-sdk-23'] = []
|
|
|
|
|
apk['features'] = []
|
|
|
|
|
apk['icons_src'] = {}
|
|
|
|
|
return apk
|
|
|
|
|
|
|
|
|
|
apkList = [
|
|
|
|
|
(
|
|
|
|
|
'org.dyndns.fules.ck_20.apk',
|
|
|
|
|
{
|
|
|
|
|
'apkName': 'org.dyndns.fules.ck_20.apk',
|
|
|
|
|
'uses-permission': [
|
|
|
|
|
fdroidserver.update.UsesPermission(
|
|
|
|
|
name='android.permission.BIND_INPUT_METHOD',
|
|
|
|
|
maxSdkVersion=None,
|
|
|
|
|
),
|
|
|
|
|
fdroidserver.update.UsesPermission(
|
|
|
|
|
name='android.permission.READ_EXTERNAL_STORAGE',
|
|
|
|
|
maxSdkVersion=None,
|
|
|
|
|
),
|
|
|
|
|
fdroidserver.update.UsesPermission(
|
|
|
|
|
name='android.permission.VIBRATE', maxSdkVersion=None
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
'uses-permission-sdk-23': [],
|
|
|
|
|
'features': [],
|
|
|
|
|
'icons_src': {
|
|
|
|
|
'240': 'res/drawable-hdpi-v4/icon_launcher.png',
|
|
|
|
|
'120': 'res/drawable-ldpi-v4/icon_launcher.png',
|
|
|
|
|
'160': 'res/drawable-mdpi-v4/icon_launcher.png',
|
|
|
|
|
'-1': 'res/drawable-mdpi-v4/icon_launcher.png',
|
|
|
|
|
},
|
|
|
|
|
'packageName': 'org.dyndns.fules.ck',
|
|
|
|
|
'versionCode': 20,
|
|
|
|
|
'versionName': 'v1.6pre2',
|
|
|
|
|
'minSdkVersion': 7,
|
|
|
|
|
'name': 'Compass Keyboard',
|
|
|
|
|
'targetSdkVersion': 8,
|
|
|
|
|
'nativecode': [
|
|
|
|
|
'arm64-v8a',
|
|
|
|
|
'armeabi',
|
|
|
|
|
'armeabi-v7a',
|
|
|
|
|
'mips',
|
|
|
|
|
'mips64',
|
|
|
|
|
'x86',
|
|
|
|
|
'x86_64',
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
for apkfile, apkaapt in apkList:
|
|
|
|
|
apkandroguard = _create_apkmetadata_object(apkfile)
|
|
|
|
|
fdroidserver.update.scan_apk_androguard(apkandroguard, apkfile)
|
|
|
|
|
|
|
|
|
|
self.maxDiff = None
|
|
|
|
|
self.assertEqual(apkaapt, apkandroguard)
|
|
|
|
|
|
2022-05-19 13:49:15 +02:00
|
|
|
|
def test_exclude_disabled_apks(self):
|
2022-11-22 17:17:45 +01:00
|
|
|
|
os.chdir(self.testdir)
|
2022-05-19 13:49:15 +02:00
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
testapk = os.path.join('repo', 'com.politedroid_6.apk')
|
|
|
|
|
testapk_new = os.path.join('repo', 'Politedroid-1.5.apk')
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, testapk), testapk_new)
|
|
|
|
|
|
|
|
|
|
config = dict()
|
|
|
|
|
fdroidserver.common.fill_config_defaults(config)
|
|
|
|
|
config['ndk_paths'] = dict()
|
|
|
|
|
fdroidserver.common.config = config
|
|
|
|
|
fdroidserver.update.config = config
|
|
|
|
|
|
|
|
|
|
fdroidserver.common.options = Options
|
|
|
|
|
fdroidserver.update.options = fdroidserver.common.options
|
|
|
|
|
fdroidserver.update.options.clean = True
|
|
|
|
|
|
|
|
|
|
app = fdroidserver.metadata.App()
|
|
|
|
|
app.id = 'com.politedroid'
|
|
|
|
|
apps = {app.id: app}
|
|
|
|
|
build = fdroidserver.metadata.Build()
|
|
|
|
|
build.versionCode = 6
|
|
|
|
|
build.disable = "disabled"
|
|
|
|
|
app['Builds'] = [build]
|
|
|
|
|
|
|
|
|
|
knownapks = fdroidserver.common.KnownApks()
|
|
|
|
|
apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False, apps)
|
|
|
|
|
self.assertEqual([], apks)
|
|
|
|
|
|
2023-06-01 20:26:42 +02:00
|
|
|
|
def test_archive_old_apks_ArchivePolicy_0(self):
|
|
|
|
|
app = fdroidserver.metadata.App()
|
|
|
|
|
app.id = 'test'
|
|
|
|
|
app.ArchivePolicy = 0
|
|
|
|
|
apps = {app.id: app}
|
|
|
|
|
with self.assertLogs(level='DEBUG') as cm:
|
|
|
|
|
fdroidserver.update.archive_old_apks(apps, [], [], '', '', 3)
|
|
|
|
|
self.assertEqual(cm.output, [
|
|
|
|
|
"DEBUG:root:Checking archiving for test - apks:0, keepversions:0, archapks:0"
|
|
|
|
|
])
|
|
|
|
|
|
2022-11-15 11:50:09 +01:00
|
|
|
|
def test_archive_old_apks(self):
|
|
|
|
|
app = fdroidserver.metadata.App()
|
|
|
|
|
app.id = 'test'
|
|
|
|
|
app.VercodeOperation = ['%c+1', '%c+2', '%c+3', '%c+4']
|
|
|
|
|
apps = {app.id: app}
|
|
|
|
|
with self.assertLogs(level='DEBUG') as cm:
|
|
|
|
|
fdroidserver.update.archive_old_apks(apps, [], [], '', '', 3)
|
|
|
|
|
self.assertEqual(cm.output, [
|
|
|
|
|
"DEBUG:root:Checking archiving for test - apks:0, keepversions:12, archapks:0"
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
app = fdroidserver.metadata.App()
|
|
|
|
|
app.id = 'org.smssecure.smssecure'
|
|
|
|
|
app.CurrentVersionCode = 135
|
|
|
|
|
apps = {app.id: app}
|
|
|
|
|
with self.assertLogs(level='DEBUG') as cm:
|
|
|
|
|
fdroidserver.update.archive_old_apks(apps, [], [], '', '', 3)
|
|
|
|
|
self.assertEqual(cm.output, [
|
|
|
|
|
"DEBUG:root:Checking archiving for org.smssecure.smssecure - apks:0, keepversions:6, archapks:0"
|
|
|
|
|
])
|
|
|
|
|
|
2023-05-30 22:42:22 +02:00
|
|
|
|
def test_categories_txt_is_removed_by_delete_unknown(self):
|
|
|
|
|
"""categories.txt used to be a part of this system, now its nothing."""
|
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
Path('config.yml').write_text('repo_pubkey: ffffffffffffffffffffffffffffffffffffffff')
|
|
|
|
|
|
|
|
|
|
categories_txt = Path('repo/categories.txt')
|
|
|
|
|
categories_txt.parent.mkdir()
|
|
|
|
|
categories_txt.write_text('placeholder')
|
|
|
|
|
|
|
|
|
|
self.assertTrue(categories_txt.exists())
|
|
|
|
|
with mock.patch('sys.argv', ['fdroid update', '--delete-unknown', '--nosign']):
|
|
|
|
|
fdroidserver.update.main()
|
|
|
|
|
self.assertFalse(categories_txt.exists())
|
|
|
|
|
|
2023-06-07 13:36:02 +02:00
|
|
|
|
def test_no_blank_auto_defined_categories(self):
|
|
|
|
|
"""When no app has Categories, there should be no definitions in the repo."""
|
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
os.mkdir('metadata')
|
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
Path('config.yml').write_text(
|
|
|
|
|
'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
testapk = os.path.join('repo', 'com.politedroid_6.apk')
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, testapk), testapk)
|
|
|
|
|
Path('metadata/com.politedroid.yml').write_text('Name: Polite')
|
|
|
|
|
|
|
|
|
|
with mock.patch('sys.argv', ['fdroid update', '--delete-unknown', '--nosign']):
|
|
|
|
|
fdroidserver.update.main()
|
|
|
|
|
with open('repo/index-v2.json') as fp:
|
|
|
|
|
index = json.load(fp)
|
|
|
|
|
self.assertFalse(CATEGORIES_CONFIG_NAME in index['repo'])
|
|
|
|
|
|
|
|
|
|
def test_auto_defined_categories(self):
|
|
|
|
|
"""Repos that don't define categories in config/ should use auto-generated."""
|
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
os.mkdir('metadata')
|
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
Path('config.yml').write_text(
|
|
|
|
|
'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
testapk = os.path.join('repo', 'com.politedroid_6.apk')
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, testapk), testapk)
|
|
|
|
|
Path('metadata/com.politedroid.yml').write_text('Categories: [Time]')
|
|
|
|
|
|
|
|
|
|
with mock.patch('sys.argv', ['fdroid update', '--delete-unknown', '--nosign']):
|
|
|
|
|
fdroidserver.update.main()
|
|
|
|
|
with open('repo/index-v2.json') as fp:
|
|
|
|
|
index = json.load(fp)
|
|
|
|
|
self.assertEqual(
|
2023-09-10 10:05:15 +02:00
|
|
|
|
{'Time': {'name': {'en-US': 'Time'}}},
|
2023-06-07 13:36:02 +02:00
|
|
|
|
index['repo'][CATEGORIES_CONFIG_NAME],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_auto_defined_categories_two_apps(self):
|
|
|
|
|
"""Repos that don't define categories in config/ should use auto-generated."""
|
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
os.mkdir('metadata')
|
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
Path('config.yml').write_text(
|
|
|
|
|
'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
testapk = os.path.join('repo', 'com.politedroid_6.apk')
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, testapk), testapk)
|
|
|
|
|
Path('metadata/com.politedroid.yml').write_text('Categories: [bar]')
|
|
|
|
|
testapk = os.path.join('repo', 'souch.smsbypass_9.apk')
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, testapk), testapk)
|
|
|
|
|
Path('metadata/souch.smsbypass.yml').write_text('Categories: [foo, bar]')
|
|
|
|
|
|
|
|
|
|
with mock.patch('sys.argv', ['fdroid update', '--delete-unknown', '--nosign']):
|
|
|
|
|
fdroidserver.update.main()
|
|
|
|
|
with open('repo/index-v2.json') as fp:
|
|
|
|
|
index = json.load(fp)
|
|
|
|
|
self.assertEqual(
|
2023-09-10 10:05:15 +02:00
|
|
|
|
{'bar': {'name': {'en-US': 'bar'}}, 'foo': {'name': {'en-US': 'foo'}}},
|
2023-06-07 13:36:02 +02:00
|
|
|
|
index['repo'][CATEGORIES_CONFIG_NAME],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_auto_defined_categories_mix_into_config_categories(self):
|
|
|
|
|
"""Repos that don't define all categories in config/ also use auto-generated."""
|
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
os.mkdir('config')
|
|
|
|
|
Path('config/categories.yml').write_text('System: {name: System Apps}')
|
|
|
|
|
os.mkdir('metadata')
|
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
Path('config.yml').write_text(
|
|
|
|
|
'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
testapk = os.path.join('repo', 'com.politedroid_6.apk')
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, testapk), testapk)
|
|
|
|
|
Path('metadata/com.politedroid.yml').write_text('Categories: [Time]')
|
|
|
|
|
testapk = os.path.join('repo', 'souch.smsbypass_9.apk')
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, testapk), testapk)
|
|
|
|
|
Path('metadata/souch.smsbypass.yml').write_text('Categories: [System, Time]')
|
|
|
|
|
|
|
|
|
|
with mock.patch('sys.argv', ['fdroid update', '--delete-unknown', '--nosign']):
|
|
|
|
|
fdroidserver.update.main()
|
|
|
|
|
with open('repo/index-v2.json') as fp:
|
|
|
|
|
index = json.load(fp)
|
|
|
|
|
self.assertEqual(
|
2024-01-11 15:45:54 +01:00
|
|
|
|
{
|
|
|
|
|
'System': {'name': {'en-US': 'System Apps'}},
|
|
|
|
|
'Time': {'name': {'en-US': 'Time'}},
|
|
|
|
|
},
|
2023-06-07 13:36:02 +02:00
|
|
|
|
index['repo'][CATEGORIES_CONFIG_NAME],
|
|
|
|
|
)
|
|
|
|
|
|
2023-06-07 15:57:58 +02:00
|
|
|
|
def test_empty_categories_not_in_index(self):
|
|
|
|
|
"""A category with no apps should be ignored, even if defined in config."""
|
|
|
|
|
os.chdir(self.testdir)
|
|
|
|
|
os.mkdir('config')
|
|
|
|
|
Path('config/categories.yml').write_text('System: {name: S}\nTime: {name: T}\n')
|
|
|
|
|
os.mkdir('metadata')
|
|
|
|
|
os.mkdir('repo')
|
|
|
|
|
Path('config.yml').write_text(
|
|
|
|
|
'repo_pubkey: ffffffffffffffffffffffffffffffffffffffff'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
testapk = os.path.join('repo', 'com.politedroid_6.apk')
|
|
|
|
|
shutil.copy(os.path.join(self.basedir, testapk), testapk)
|
|
|
|
|
Path('metadata/com.politedroid.yml').write_text('Categories: [Time]')
|
|
|
|
|
|
|
|
|
|
with mock.patch('sys.argv', ['fdroid update', '--delete-unknown', '--nosign']):
|
|
|
|
|
fdroidserver.update.main()
|
|
|
|
|
with open('repo/index-v2.json') as fp:
|
|
|
|
|
index = json.load(fp)
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
{'Time': {'name': {'en-US': 'T'}}},
|
|
|
|
|
index['repo'][CATEGORIES_CONFIG_NAME],
|
|
|
|
|
)
|
|
|
|
|
|
2024-04-04 14:14:58 +02:00
|
|
|
|
|
|
|
|
|
class TestParseIpa(unittest.TestCase):
|
2023-12-18 13:35:06 +01:00
|
|
|
|
def test_parse_ipa(self):
|
2024-04-04 14:14:58 +02:00
|
|
|
|
self.maxDiff = None
|
2024-01-11 15:56:42 +01:00
|
|
|
|
try:
|
|
|
|
|
import biplist # Fedora does not have a biplist package
|
|
|
|
|
|
|
|
|
|
biplist # silence the linters
|
|
|
|
|
except ImportError as e:
|
|
|
|
|
self.skipTest(str(e))
|
2024-01-11 15:45:54 +01:00
|
|
|
|
ipa_path = os.path.join(
|
|
|
|
|
os.path.dirname(os.path.abspath(__file__)),
|
|
|
|
|
'com.fake.IpaApp_1000000000001.ipa',
|
|
|
|
|
)
|
2023-12-30 13:51:37 +01:00
|
|
|
|
result = fdroidserver.update.parse_ipa(ipa_path, 'fake_size', 'fake_sha')
|
2023-12-18 13:35:06 +01:00
|
|
|
|
self.maxDiff = None
|
2024-01-11 15:45:54 +01:00
|
|
|
|
self.assertDictEqual(
|
|
|
|
|
result,
|
|
|
|
|
{
|
|
|
|
|
'apkName': 'com.fake.IpaApp_1000000000001.ipa',
|
|
|
|
|
'hash': 'fake_sha',
|
|
|
|
|
'hashType': 'sha256',
|
|
|
|
|
'packageName': 'org.onionshare.OnionShare',
|
|
|
|
|
'size': 'fake_size',
|
|
|
|
|
'versionCode': 1000000000001,
|
|
|
|
|
'versionName': '1.0.1',
|
2024-04-04 14:14:58 +02:00
|
|
|
|
'ipa_DTPlatformVersion': '16.4',
|
|
|
|
|
'ipa_MinimumOSVersion': '15.0',
|
|
|
|
|
'ipa_entitlements': set(),
|
|
|
|
|
'ipa_permissions': {
|
|
|
|
|
'NSCameraUsageDescription':
|
|
|
|
|
'Please allow access to your '
|
|
|
|
|
'camera, if you want to '
|
|
|
|
|
'create photos or videos for '
|
|
|
|
|
'direct sharing.',
|
|
|
|
|
'NSMicrophoneUsageDescription':
|
|
|
|
|
'Please allow access to '
|
|
|
|
|
'your microphone, if you '
|
|
|
|
|
'want to create videos '
|
|
|
|
|
'for direct sharing.',
|
|
|
|
|
'NSPhotoLibraryUsageDescription':
|
|
|
|
|
'Please allow access to '
|
|
|
|
|
'your photo library, if '
|
|
|
|
|
'you want to share '
|
|
|
|
|
'photos.',
|
|
|
|
|
},
|
|
|
|
|
'name': 'OnionShare',
|
2024-01-11 15:45:54 +01:00
|
|
|
|
},
|
|
|
|
|
)
|
2023-12-18 13:35:06 +01:00
|
|
|
|
|
2014-08-30 17:07:29 +02:00
|
|
|
|
|
2023-12-18 12:58:37 +01:00
|
|
|
|
class TestUpdateVersionStringToInt(unittest.TestCase):
|
|
|
|
|
def test_version_string_to_int(self):
|
2024-01-11 15:45:54 +01:00
|
|
|
|
self.assertEqual(
|
|
|
|
|
fdroidserver.update.version_string_to_int("1.2.3"), 1000002000003
|
|
|
|
|
)
|
2023-12-18 12:58:37 +01:00
|
|
|
|
self.assertEqual(fdroidserver.update.version_string_to_int("0.0.0003"), 3)
|
|
|
|
|
self.assertEqual(fdroidserver.update.version_string_to_int("0.0.0"), 0)
|
2024-01-11 15:45:54 +01:00
|
|
|
|
self.assertEqual(
|
|
|
|
|
fdroidserver.update.version_string_to_int("4321.321.21"), 4321000321000021
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
fdroidserver.update.version_string_to_int("18446744.073709.551615"),
|
|
|
|
|
18446744073709551615,
|
|
|
|
|
)
|
2023-12-18 12:58:37 +01:00
|
|
|
|
|
|
|
|
|
def test_version_string_to_int_value_errors(self):
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
|
fdroidserver.update.version_string_to_int("1.2.3a")
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
|
fdroidserver.update.version_string_to_int("asdfasdf")
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
|
fdroidserver.update.version_string_to_int("1.2.-3")
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
|
fdroidserver.update.version_string_to_int("-1.2.-3")
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
|
fdroidserver.update.version_string_to_int("0.0.0x3")
|
|
|
|
|
|
|
|
|
|
|
2023-12-20 04:11:05 +01:00
|
|
|
|
class TestScanRepoForIpas(unittest.TestCase):
|
|
|
|
|
def setUp(self):
|
|
|
|
|
self.maxDiff = None
|
|
|
|
|
|
|
|
|
|
def test_scan_repo_for_ipas_no_cache(self):
|
|
|
|
|
self.maxDiff = None
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
|
|
|
|
|
os.mkdir("repo")
|
|
|
|
|
with open('repo/abc.Def_123.ipa', 'w') as f:
|
|
|
|
|
f.write('abc')
|
|
|
|
|
with open('repo/xyz.XXX_123.ipa', 'w') as f:
|
|
|
|
|
f.write('xyz')
|
|
|
|
|
|
|
|
|
|
apkcache = mock.MagicMock()
|
|
|
|
|
# apkcache['a'] = 1
|
|
|
|
|
repodir = "repo"
|
|
|
|
|
knownapks = mock.MagicMock()
|
|
|
|
|
|
|
|
|
|
def mocked_parse(p, s, c):
|
2023-12-30 13:51:37 +01:00
|
|
|
|
# pylint: disable=unused-argument
|
2024-01-11 15:45:54 +01:00
|
|
|
|
return {'packageName': 'abc' if 'abc' in p else 'xyz'}
|
2023-12-20 04:11:05 +01:00
|
|
|
|
|
|
|
|
|
with mock.patch('fdroidserver.update.parse_ipa', mocked_parse):
|
2024-01-11 15:45:54 +01:00
|
|
|
|
ipas, checkchanged = fdroidserver.update.scan_repo_for_ipas(
|
|
|
|
|
apkcache, repodir, knownapks
|
|
|
|
|
)
|
2023-12-20 04:11:05 +01:00
|
|
|
|
|
|
|
|
|
self.assertEqual(checkchanged, True)
|
|
|
|
|
self.assertEqual(len(ipas), 2)
|
2023-12-30 13:51:37 +01:00
|
|
|
|
package_names_in_ipas = [x['packageName'] for x in ipas]
|
|
|
|
|
self.assertTrue('abc' in package_names_in_ipas)
|
|
|
|
|
self.assertTrue('xyz' in package_names_in_ipas)
|
2023-12-20 04:11:05 +01:00
|
|
|
|
|
2024-01-11 15:45:54 +01:00
|
|
|
|
apkcache_setter_package_name = [
|
|
|
|
|
x.args[1]['packageName'] for x in apkcache.__setitem__.mock_calls
|
|
|
|
|
]
|
2023-12-30 13:51:37 +01:00
|
|
|
|
self.assertTrue('abc' in apkcache_setter_package_name)
|
|
|
|
|
self.assertTrue('xyz' in apkcache_setter_package_name)
|
2023-12-20 04:11:05 +01:00
|
|
|
|
self.assertEqual(apkcache.__setitem__.call_count, 2)
|
|
|
|
|
|
|
|
|
|
knownapks.recordapk.call_count = 2
|
2024-01-11 15:45:54 +01:00
|
|
|
|
self.assertTrue(
|
2024-10-30 14:01:54 +01:00
|
|
|
|
unittest.mock.call('abc.Def_123.ipa') in knownapks.recordapk.mock_calls
|
2024-01-11 15:45:54 +01:00
|
|
|
|
)
|
|
|
|
|
self.assertTrue(
|
2024-10-30 14:01:54 +01:00
|
|
|
|
unittest.mock.call('xyz.XXX_123.ipa') in knownapks.recordapk.mock_calls
|
2024-01-11 15:45:54 +01:00
|
|
|
|
)
|
2023-12-20 04:11:05 +01:00
|
|
|
|
|
|
|
|
|
|
2024-03-18 14:01:36 +01:00
|
|
|
|
class TestParseIosScreenShotName(unittest.TestCase):
|
|
|
|
|
def setUp(self):
|
|
|
|
|
self.maxDiff = None
|
|
|
|
|
|
|
|
|
|
def test_parse_ios_screenshot_name_atforamt_iphone8(self):
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
fdroidserver.update.parse_ios_screenshot_name(Path("iPhone 8+ @ iOS 16-1.png")),
|
|
|
|
|
("phoneScreenshots", "iPhone 8+", "iOS 16",),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_parse_ios_screenshot_name_atforamt_ipad13(self):
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
fdroidserver.update.parse_ios_screenshot_name(Path("iPad Pro 12.9\" 2gen @ iOS 16-1.png")),
|
|
|
|
|
("tenInchScreenshots", "iPad Pro 12.9\" 2gen", "iOS 16",),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_parse_ios_screenshot_name_underscoreforamt_ipad(self):
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
fdroidserver.update.parse_ios_screenshot_name(Path("1_ipadPro129_1.1.png")),
|
|
|
|
|
("tenInchScreenshots", "ipadpro129", "unknown",),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_parse_ios_screenshot_name_underscoreforamt_iphone(self):
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
fdroidserver.update.parse_ios_screenshot_name(Path("1_iphone6Plus_1.1.png")),
|
|
|
|
|
("phoneScreenshots", "iphone6plus", "unknown",),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2024-03-19 23:30:45 +01:00
|
|
|
|
class TestInsertLocalizedIosAppMetadata(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def test_insert_localized_ios_app_metadata(self):
|
|
|
|
|
self.maxDiff = None
|
|
|
|
|
|
|
|
|
|
self.apps_with_packages = {
|
|
|
|
|
"org.fake": {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def _mock_discover(fastlane_dir):
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
fastlane_dir,
|
|
|
|
|
Path('build/org.fake/fastlane'),
|
|
|
|
|
)
|
|
|
|
|
return {"fake screenshots": "fake"}
|
|
|
|
|
|
|
|
|
|
def _mock_copy(screenshots, package_name):
|
|
|
|
|
self.assertEqual(screenshots, {"fake screenshots": "fake"})
|
|
|
|
|
self.assertEqual(package_name, "org.fake")
|
|
|
|
|
|
|
|
|
|
with mock.patch('fdroidserver.update.discover_ios_screenshots', _mock_discover):
|
|
|
|
|
self.set_localized_mock = mock.Mock()
|
|
|
|
|
with mock.patch('fdroidserver.update.copy_ios_screenshots_to_repo', _mock_copy):
|
|
|
|
|
with mock.patch("fdroidserver.update._set_localized_text_entry", self.set_localized_mock):
|
|
|
|
|
return fdroidserver.update.insert_localized_ios_app_metadata(
|
|
|
|
|
self.apps_with_packages
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertListEqual(
|
|
|
|
|
self.set_localized_mock.call_args_list,
|
|
|
|
|
[
|
|
|
|
|
mock.call({}, 'en-US', 'name', Path('build/org.fake/fastlane/metadata/en-US/name.txt')),
|
|
|
|
|
mock.call({}, 'en-US', 'summary', Path('build/org.fake/fastlane/metadata/en-US/subtitle.txt')),
|
|
|
|
|
mock.call({}, 'en-US', 'description', Path('build/org.fake/fastlane/metadata/en-US/description.txt')),
|
|
|
|
|
mock.call({}, 'de-DE', 'name', Path('build/org.fake/fastlane/metadata/de-DE/name.txt')),
|
|
|
|
|
mock.call({}, 'de-DE', 'summary', Path('build/org.fake/fastlane/metadata/de-DE/subtitle.txt')),
|
|
|
|
|
mock.call({}, 'de-DE', 'description', Path('build/org.fake/fastlane/metadata/de-DE/description.txt')),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestDiscoverIosScreenshots(unittest.TestCase):
|
|
|
|
|
def test_discover_ios_screenshots(self):
|
|
|
|
|
self.maxDiff = None
|
|
|
|
|
|
|
|
|
|
with tempfile.TemporaryDirectory() as fastlane_dir:
|
|
|
|
|
fastlane_dir = Path(fastlane_dir)
|
|
|
|
|
(fastlane_dir / "screenshots/en-US").mkdir(parents=True)
|
|
|
|
|
with open(fastlane_dir / "screenshots/en-US/iPhone 8+ @ iOS 16-1.png", 'w') as f:
|
|
|
|
|
f.write("1")
|
|
|
|
|
with open(fastlane_dir / "screenshots/en-US/iPad Pro 12.9\" 2gen @ iOS 16-1.png", "w") as f:
|
|
|
|
|
f.write("2")
|
|
|
|
|
with open(fastlane_dir / "screenshots/en-US/iPad Pro 12.9\" 2gen @ iOS 16-2.png", "w") as f:
|
|
|
|
|
f.write("3")
|
|
|
|
|
(fastlane_dir / "screenshots/de-DE").mkdir(parents=True)
|
|
|
|
|
with open(fastlane_dir / "screenshots/de-DE/1_ipadPro129_1.1.png", "w") as f:
|
|
|
|
|
f.write("4")
|
|
|
|
|
|
|
|
|
|
screenshots = fdroidserver.update.discover_ios_screenshots(fastlane_dir)
|
|
|
|
|
|
|
|
|
|
self.assertDictEqual(
|
|
|
|
|
screenshots,
|
|
|
|
|
{
|
|
|
|
|
"en-US": {
|
|
|
|
|
"phoneScreenshots": [
|
|
|
|
|
fastlane_dir / "screenshots/en-US/iPhone 8+ @ iOS 16-1.png",
|
|
|
|
|
],
|
|
|
|
|
"tenInchScreenshots": [
|
|
|
|
|
fastlane_dir / "screenshots/en-US/iPad Pro 12.9\" 2gen @ iOS 16-1.png",
|
|
|
|
|
fastlane_dir / "screenshots/en-US/iPad Pro 12.9\" 2gen @ iOS 16-2.png",
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
"de-DE": {
|
|
|
|
|
"tenInchScreenshots": [
|
|
|
|
|
fastlane_dir / "screenshots/de-DE/1_ipadPro129_1.1.png",
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestCopyIosScreenshotsToRepo(unittest.TestCase):
|
|
|
|
|
def test_copy_ios_screenshots_to_repo(self):
|
|
|
|
|
self.maxDiff = None
|
|
|
|
|
|
|
|
|
|
screenshot_dir_en = Path("build/org.fake/fastlane/screenshots/en-US")
|
|
|
|
|
s1 = screenshot_dir_en / "iPhone 8+ @ iOS 16-1.png"
|
|
|
|
|
s2 = screenshot_dir_en / "iPad Pro 12.9\" 2gen @ iOS 16-1.png"
|
|
|
|
|
s3 = screenshot_dir_en / "iPad Pro 12.9\" 2gen @ iOS 16-2.png"
|
|
|
|
|
screenshot_dir_de = Path("build/org.fake/fastlane/screenshots/de-DE")
|
|
|
|
|
s4 = screenshot_dir_de / "1_ipadPro129_1.1.png"
|
|
|
|
|
|
|
|
|
|
cmock = mock.Mock()
|
|
|
|
|
with mock.patch("fdroidserver.update._strip_and_copy_image", cmock):
|
|
|
|
|
fdroidserver.update.copy_ios_screenshots_to_repo(
|
|
|
|
|
{
|
|
|
|
|
"en-US": {
|
|
|
|
|
"phoneScreenshots": [s1],
|
|
|
|
|
"tenInchScreenshots": [s2, s3],
|
|
|
|
|
},
|
|
|
|
|
"de-DE": {
|
|
|
|
|
"tenInchScreenshots": [s4],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
"org.fake",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertListEqual(
|
|
|
|
|
cmock.call_args_list,
|
|
|
|
|
[
|
|
|
|
|
mock.call(
|
|
|
|
|
'build/org.fake/fastlane/screenshots/en-US/iPhone 8+ @ iOS 16-1.png',
|
|
|
|
|
'repo/org.fake/en-US/phoneScreenshots/iPhone_8+_@_iOS_16-1.png',
|
|
|
|
|
),
|
|
|
|
|
mock.call(
|
|
|
|
|
'build/org.fake/fastlane/screenshots/en-US/iPad Pro 12.9" 2gen @ iOS 16-1.png',
|
|
|
|
|
'repo/org.fake/en-US/tenInchScreenshots/iPad_Pro_12.9"_2gen_@_iOS_16-1.png',
|
|
|
|
|
),
|
|
|
|
|
mock.call(
|
|
|
|
|
'build/org.fake/fastlane/screenshots/en-US/iPad Pro 12.9" 2gen @ iOS 16-2.png',
|
|
|
|
|
'repo/org.fake/en-US/tenInchScreenshots/iPad_Pro_12.9"_2gen_@_iOS_16-2.png',
|
|
|
|
|
),
|
|
|
|
|
mock.call(
|
|
|
|
|
'build/org.fake/fastlane/screenshots/de-DE/1_ipadPro129_1.1.png',
|
|
|
|
|
'repo/org.fake/de-DE/tenInchScreenshots/1_ipadPro129_1.1.png',
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2024-04-04 13:04:55 +02:00
|
|
|
|
class TestGetIpaIcon(unittest.TestCase):
|
|
|
|
|
def test_get_ipa_icon(self):
|
|
|
|
|
self.maxDiff = None
|
|
|
|
|
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
|
|
|
tmpdir = Path(tmpdir)
|
|
|
|
|
(tmpdir / 'OnionBrowser.xcodeproj').mkdir()
|
|
|
|
|
with open(tmpdir / 'OnionBrowser.xcodeproj/project.pbxproj', "w") as f:
|
|
|
|
|
f.write("")
|
|
|
|
|
icondir = tmpdir / "fake_icon.appiconset"
|
|
|
|
|
icondir.mkdir()
|
|
|
|
|
with open(icondir / "Contents.json", "w", encoding="utf-8") as f:
|
|
|
|
|
f.write("""
|
|
|
|
|
{"images": [
|
|
|
|
|
{"scale": "2x", "size": "128x128", "filename": "nope"},
|
|
|
|
|
{"scale": "1x", "size": "512x512", "filename": "nope"},
|
|
|
|
|
{"scale": "1x", "size": "16x16", "filename": "nope"},
|
|
|
|
|
{"scale": "1x", "size": "32x32", "filename": "yep"}
|
|
|
|
|
]}
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
pfp = mock.Mock(return_value="fake_icon")
|
2024-04-04 13:53:02 +02:00
|
|
|
|
with mock.patch("fdroidserver.update._parse_from_pbxproj", pfp):
|
2024-04-04 13:04:55 +02:00
|
|
|
|
p = fdroidserver.update._get_ipa_icon(tmpdir)
|
|
|
|
|
self.assertEqual(str(icondir / "yep"), p)
|
|
|
|
|
|
|
|
|
|
|
2024-04-04 13:53:02 +02:00
|
|
|
|
class TestParseFromPbxproj(unittest.TestCase):
|
|
|
|
|
def test_parse_from_pbxproj(self):
|
|
|
|
|
self.maxDiff = None
|
|
|
|
|
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
|
|
|
with open(Path(tmpdir) / "asdf.pbxproj", 'w', encoding="utf-8") as f:
|
|
|
|
|
f.write("""
|
|
|
|
|
230jfaod=flc'
|
|
|
|
|
ASSETCATALOG_COMPILER_APPICON_NAME = MyIcon;
|
|
|
|
|
cm opa1c p[m
|
|
|
|
|
""")
|
|
|
|
|
v = fdroidserver.update._parse_from_pbxproj(
|
|
|
|
|
Path(tmpdir) / "asdf.pbxproj",
|
|
|
|
|
"ASSETCATALOG_COMPILER_APPICON_NAME"
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(v, "MyIcon")
|
|
|
|
|
|
|
|
|
|
|
2014-08-30 17:07:29 +02:00
|
|
|
|
if __name__ == "__main__":
|
2018-08-08 02:36:38 +02:00
|
|
|
|
os.chdir(os.path.dirname(__file__))
|
|
|
|
|
|
2024-05-02 14:08:28 +02:00
|
|
|
|
import argparse
|
|
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
|
parser.add_argument(
|
2021-06-07 11:49:21 +02:00
|
|
|
|
"-v",
|
|
|
|
|
"--verbose",
|
|
|
|
|
action="store_true",
|
|
|
|
|
default=False,
|
|
|
|
|
help="Spew out even more information than normal",
|
|
|
|
|
)
|
2024-05-08 16:26:46 +02:00
|
|
|
|
parse_args_for_test(parser, sys.argv)
|
2014-08-30 17:07:29 +02:00
|
|
|
|
|
|
|
|
|
newSuite = unittest.TestSuite()
|
|
|
|
|
newSuite.addTest(unittest.makeSuite(UpdateTest))
|
2023-12-18 12:58:37 +01:00
|
|
|
|
newSuite.addTest(unittest.makeSuite(TestUpdateVersionStringToInt))
|
2023-12-20 04:11:05 +01:00
|
|
|
|
newSuite.addTest(unittest.makeSuite(TestScanRepoForIpas))
|
2024-03-19 23:30:45 +01:00
|
|
|
|
newSuite.addTest(unittest.makeSuite(TestParseIosScreenShotName))
|
|
|
|
|
newSuite.addTest(unittest.makeSuite(TestInsertLocalizedIosAppMetadata))
|
|
|
|
|
newSuite.addTest(unittest.makeSuite(TestDiscoverIosScreenshots))
|
2024-04-04 13:04:55 +02:00
|
|
|
|
newSuite.addTest(unittest.makeSuite(TestGetIpaIcon))
|
2017-10-20 11:51:59 +02:00
|
|
|
|
unittest.main(failfast=False)
|