diff --git a/fdroidserver/checkupdates.py b/fdroidserver/checkupdates.py index fe9b63d8..460a7373 100644 --- a/fdroidserver/checkupdates.py +++ b/fdroidserver/checkupdates.py @@ -326,7 +326,7 @@ def try_init_submodules(app, last_build, vcs): try: vcs.initsubmodules() except NoSubmodulesException: - logging.info("No submodules present for {}".format(app.Name)) + logging.info("No submodules present for {}".format(_getappname(app))) # Return all directories under startdir that contain any of the manifest @@ -359,11 +359,7 @@ def possible_subdirs(app): def _getappname(app): - if app.Name: - return app.Name - if app.AutoName: - return app.AutoName - return app.id + return common.get_app_display_name(app) def _getcvname(app): diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 1de841c1..22a4f3ff 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -3511,9 +3511,9 @@ def get_certificate(signature_block_file): def load_stats_fdroid_signing_key_fingerprints(): - """Load list of signing-key fingerprints stored by fdroid publish from file. + """Load signing-key fingerprints stored in file generated by fdroid publish. - :returns: list of dictionanryies containing the singing-key fingerprints. + :returns: dict containing the signing-key fingerprints. """ jar_file = os.path.join('stats', 'publishsigkeys.jar') if not os.path.isfile(jar_file): @@ -3628,6 +3628,26 @@ def version_code_string_to_int(vercode): return int(vercode) +def get_app_display_name(app): + """get a human readable name for the app for logging and sorting + + When trying to find a localized name, this first tries en-US since + that his the historical language used for sorting. + + """ + if app.get('Name'): + return app['Name'] + if app.get('localized'): + localized = app['localized'].get('en-US') + if not localized: + for v in app['localized'].values(): + localized = v + break + if localized.get('name'): + return localized['name'] + return app.get('AutoName') or app['id'] + + def local_rsync(options, fromdir, todir): '''Rsync method for local to local copying of things diff --git a/fdroidserver/index.py b/fdroidserver/index.py index 9b2b68c9..95b46ed5 100644 --- a/fdroidserver/index.py +++ b/fdroidserver/index.py @@ -61,7 +61,7 @@ def make(apps, apks, repodir, archive): common.assert_config_keystore(common.config) # Historically the index has been sorted by App Name, so we enforce this ordering here - sortedids = sorted(apps, key=lambda appid: apps[appid]['Name'].upper()) + sortedids = sorted(apps, key=lambda appid: common.get_app_display_name(apps[appid]).upper()) sortedapps = collections.OrderedDict() for appid in sortedids: sortedapps[appid] = apps[appid] @@ -159,7 +159,7 @@ def make_v1(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_ if not v: continue if k in ('Builds', 'comments', 'metadatapath', - 'ArchivePolicy', 'AutoUpdateMode', 'MaintainerNotes', + 'ArchivePolicy', 'AutoName', 'AutoUpdateMode', 'MaintainerNotes', 'Provides', 'Repo', 'RepoType', 'RequiresRoot', 'UpdateCheckData', 'UpdateCheckIgnore', 'UpdateCheckMode', 'UpdateCheckName', 'NoSourceSince', 'VercodeOperation'): @@ -172,10 +172,6 @@ def make_v1(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_ k = 'suggestedVersionCode' elif k == 'CurrentVersion': # TODO make SuggestedVersionName the canonical name k = 'suggestedVersionName' - elif k == 'AutoName': - if 'Name' not in apps[packageName]: - d['name'] = v - continue else: k = k[:1].lower() + k[1:] d[k] = v @@ -326,6 +322,8 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, fdroid_signing_key_fing value = localized[lang].get(lkey) if not value: value = default + if not value and name == 'name' and app.get('AutoName'): + value = app['AutoName'] el.appendChild(doc.createTextNode(value)) parent.appendChild(el) @@ -363,10 +361,13 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, fdroid_signing_key_fing # Get a list of the apks for this app... apklist = [] + name_from_apk = None apksbyversion = collections.defaultdict(lambda: []) for apk in apks: if apk.get('versionCode') and apk.get('packageName') == appid: apksbyversion[apk['versionCode']].append(apk) + if name_from_apk is None: + name_from_apk = apk.get('name') for versionCode, apksforver in apksbyversion.items(): fdroidsig = fdroid_signing_key_fingerprints.get(appid, {}).get('signer') fdroid_signed_apk = None @@ -398,7 +399,7 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, fdroid_signing_key_fing if app.lastUpdated: addElement('lastupdated', app.lastUpdated.strftime('%Y-%m-%d'), doc, apel) - addElementCheckLocalized('name', app, 'Name', doc, apel) + addElementCheckLocalized('name', app, 'Name', doc, apel, name_from_apk) addElementCheckLocalized('summary', app, 'Summary', doc, apel) if app.icon: @@ -466,10 +467,10 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, fdroid_signing_key_fing for apk in apklist: file_extension = common.get_file_extension(apk['apkName']) # find the APK for the "Current Version" - if current_version_code < apk['versionCode']: - current_version_code = apk['versionCode'] if current_version_code < int(app.CurrentVersionCode): current_version_file = apk['apkName'] + if current_version_code < apk['versionCode']: + current_version_code = apk['versionCode'] apkel = doc.createElement("package") apel.appendChild(apkel) @@ -543,7 +544,12 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, fdroid_signing_key_fing and common.config['make_current_version_link'] \ and repodir == 'repo': # only create these namefield = common.config['current_version_name_source'] - sanitized_name = re.sub(b'''[ '"&%?+=/]''', b'', app.get(namefield).encode('utf-8')) + name = app.get(namefield) + if not name and namefield == 'Name': + name = app.get('localized', {}).get('en-US', {}).get('name') + if not name: + name = app.id + sanitized_name = re.sub(b'''[ '"&%?+=/]''', b'', name.encode('utf-8')) apklinkname = sanitized_name + os.path.splitext(current_version_file)[1].encode('utf-8') current_version_path = os.path.join(repodir, current_version_file).encode('utf-8', 'surrogateescape') if os.path.islink(apklinkname): diff --git a/fdroidserver/lint.py b/fdroidserver/lint.py index 61916e71..99a62f31 100644 --- a/fdroidserver/lint.py +++ b/fdroidserver/lint.py @@ -323,9 +323,6 @@ def check_categories(app): def check_duplicates(app): - if app.Name and app.Name == app.AutoName: - yield _("Name '%s' is just the auto name - remove it") % app.Name - links_seen = set() for f in ['Source Code', 'Web Site', 'Issue Tracker', 'Changelog']: v = app.get(f) @@ -337,7 +334,7 @@ def check_duplicates(app): else: links_seen.add(v) - name = app.Name or app.AutoName + name = common.get_app_display_name(app) if app.Summary and name: if app.Summary.lower() == name.lower(): yield _("Summary '%s' is just the app's name") % app.Summary diff --git a/fdroidserver/update.py b/fdroidserver/update.py index ef1310c7..80e092c3 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -207,9 +207,12 @@ def update_wiki(apps, apks): requiresroot = 'Yes' else: requiresroot = 'No' + + apppagename = common.get_app_display_name(app) + wikidata += '{{App|id=%s|name=%s|added=%s|lastupdated=%s|source=%s|tracker=%s|web=%s|changelog=%s|donate=%s|flattr=%s|liberapay=%s|bitcoin=%s|litecoin=%s|license=%s|root=%s|author=%s|email=%s}}\n' % ( appid, - app.Name, + apppagename, app.added.strftime('%Y-%m-%d') if app.added else '', app.lastUpdated.strftime('%Y-%m-%d') if app.lastUpdated else '', app.SourceCode, @@ -338,7 +341,6 @@ def update_wiki(apps, apks): # Make a redirect from the name to the ID too, unless there's # already an existing page with the name and it isn't a redirect. noclobber = False - apppagename = app.Name for ch in '_{}:[]|': apppagename = apppagename.replace(ch, ' ') # Drop double spaces caused mostly by replacing ':' above @@ -1903,15 +1905,10 @@ def apply_info_from_latest_apk(apps, apks): logging.debug("Don't know when " + appid + " was last updated") if bestver == UNSET_VERSION_CODE: - - if app.get('Name') is None: - app['Name'] = app['AutoName'] or appid app['icon'] = None logging.debug("Application " + appid + " has no packages") else: - if app.get('Name') is None: - app['Name'] = bestapk['name'] - app['icon'] = bestapk['icon'] if 'icon' in bestapk else None + app.icon = bestapk['icon'] if 'icon' in bestapk else None if app.get('CurrentVersionCode') is None: app['CurrentVersionCode'] = str(bestver) @@ -2104,23 +2101,45 @@ def read_added_date_from_all_apks(apps, apks): app['lastUpdated'] = apk['added'] -def read_names_from_apks(apps, apks): - """This is a stripped down copy of apply_info_from_latest_apk that only parses app names""" +def insert_missing_app_names_from_apks(apps, apks): + """Use app name from APK if it is not set in the metadata + + Name -> localized -> from APK + + The name from the APK is set as the default name for the app if + there is no other default set, e.g. app['Name'] or + app['localized']['en-US']['name']. The en-US locale is defined in + the F-Droid ecosystem as the locale of last resort, as in the one + that should always be present. en-US is used since it is the + locale of the source strings. + + This should only be used for index v0 and v1. Later versions of + the index should be sorted by Application ID, since it is + guaranteed to always be there. Before, the index was stored by + the app name (aka ) to save the + website from having to sort the entries. That is no longer + relevant since the website switched from Wordpress to Jekyll. + + """ for appid, app in apps.items(): + if app.get('Name') is not None: + continue + if app.get('localized', {}).get('en-US', {}).get('name') is not None: + continue + bestver = UNSET_VERSION_CODE for apk in apks: if apk['packageName'] == appid: - if apk['versionCode'] > bestver: + if apk.get('name') and apk['versionCode'] > bestver: bestver = apk['versionCode'] bestapk = apk - if bestver == UNSET_VERSION_CODE: - if app.Name is None: - app.Name = app.AutoName or appid - app.icon = None - else: - if app.Name is None: - app.Name = bestapk['name'] + if bestver != UNSET_VERSION_CODE: + if 'localized' not in app: + app['localized'] = {} + if 'en-US' not in app['localized']: + app['localized']['en-US'] = {} + app['localized']['en-US']['name'] = bestapk.get('name') def get_apps_with_packages(apps, apks): @@ -2159,6 +2178,7 @@ def prepare_apps(apps, apks, repodir): translate_per_build_anti_features(apps_with_packages, apks) if repodir == 'repo': insert_localized_app_metadata(apps_with_packages) + insert_missing_app_names_from_apks(apps_with_packages, apks) return apps_with_packages @@ -2305,11 +2325,9 @@ def main(): else: archapks = [] - # We need app.Name populated for all apps regardless of which repo they end up in - # for the old-style inter-app links, so let's do it before we do anything else. - # This will be done again (as part of apply_info_from_latest_apk) for repo and archive - # separately later on, but it's fairly cheap anyway. - read_names_from_apks(apps, apks + archapks) + if cachechanged: + write_cache(apkcache) + # The added date currently comes from the oldest apk which might be in the archive. # So we need this populated at app level before continuing with only processing /repo # or /archive @@ -2349,9 +2367,6 @@ def main(): # Update known apks info... knownapks.writeifchanged() - if cachechanged: - write_cache(apkcache) - # Update the wiki... if options.wiki: logging.warning(_('wiki support is deprecated and will be removed in the next release!')) diff --git a/tests/common.TestCase b/tests/common.TestCase index 253f8037..2f226dee 100755 --- a/tests/common.TestCase +++ b/tests/common.TestCase @@ -1612,6 +1612,21 @@ class CommonTest(unittest.TestCase): self.assertEqual([dirtyfile, 'repo/status/running.json'], data['fdroiddata']['untrackedFiles']) + def test_get_app_display_name(self): + testvalue = 'WIN!' + for app in [ + {'Name': testvalue}, + {'AutoName': testvalue}, + {'id': testvalue}, + {'id': 'a', 'localized': {'de-AT': {'name': testvalue}}}, + {'id': 'a', 'localized': { + 'de-AT': {'name': 'nope'}, + 'en-US': {'name': testvalue}, + }}, + {'AutoName': 'ignore me', 'Name': testvalue, 'id': 'nope'}, + {'AutoName': testvalue, 'id': 'nope'}]: + self.assertEqual(testvalue, fdroidserver.common.get_app_display_name(app)) + if __name__ == "__main__": os.chdir(os.path.dirname(__file__)) diff --git a/tests/index.TestCase b/tests/index.TestCase index 131b3a1a..16885dbf 100755 --- a/tests/index.TestCase +++ b/tests/index.TestCase @@ -247,7 +247,7 @@ class IndexTest(unittest.TestCase): self.maxDiff = None self.assertEqual(json.dumps(i, indent=2), json.dumps(o, indent=2)) - def test_make_v0(self): + def test_make_v0_repo_only(self): tmptestsdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir) os.chdir(tmptestsdir) @@ -264,7 +264,71 @@ class IndexTest(unittest.TestCase): } requestsdict = {'install': [], 'uninstall': []} fdroidserver.common.config['repo_pubkey'] = 'ffffffffffffffffffffffffffffffffff' - fdroidserver.index.make_v0({}, [], 'repo', repodict, requestsdict, []) + fdroidserver.index.make_v0({}, [], 'repo', repodict, requestsdict, {}) + self.assertTrue(os.path.isdir(repo_icons_dir)) + self.assertTrue(os.path.exists(os.path.join(repo_icons_dir, + fdroidserver.common.default_config['repo_icon']))) + self.assertTrue(os.path.exists(os.path.join('repo', 'index.xml'))) + + def test_make_v0(self): + tmptestsdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, + dir=self.tmpdir) + os.chdir(tmptestsdir) + os.mkdir('metadata') + os.mkdir('repo') + metadatafile = 'metadata/info.zwanenburg.caffeinetile.yml' + shutil.copy(os.path.join(self.basedir, metadatafile), + metadatafile) + repo_icons_dir = os.path.join('repo', 'icons') + self.assertFalse(os.path.isdir(repo_icons_dir)) + repodict = { + 'address': 'https://example.com/fdroid/repo', + 'description': 'This is just a test', + 'icon': 'blahblah', + 'name': 'test', + 'timestamp': datetime.datetime.now(), + 'version': 12, + } + app = fdroidserver.metadata.parse_metadata(metadatafile, False, False) + app['icon'] = 'info.zwanenburg.caffeinetile.4.xml' + app['CurrentVersionCode'] = '4' + apps = {app.id: app} + apk = { + 'hash': 'dbbdd7deadb038862f426b71efe4a64df8c3edf25d669e935f349510e16f65db', + 'hashType': 'sha256', + 'uses-permission': [ + [ + 'android.permission.WAKE_LOCK', + None + ] + ], + 'uses-permission-sdk-23': [], + 'features': [], + 'icons_src': { + '160': 'res/drawable/ic_coffee_on.xml', + '-1': 'res/drawable/ic_coffee_on.xml' + }, + 'icons': { + '160': 'info.zwanenburg.caffeinetile.4.xml' + }, + 'antiFeatures': [], + 'packageName': 'info.zwanenburg.caffeinetile', + 'versionCode': 4, + 'name': 'Caffeine Tile', + 'versionName': '1.3', + 'minSdkVersion': 24, + 'targetSdkVersion': 25, + 'sig': '03f9b2f848d22fd1d8d1331e8b1b486d', + 'signer': '51cfa5c8a743833ad89acf81cb755936876a5c8b8eca54d1ffdcec0cdca25d0e', + 'size': 11740, + 'apkName': 'info.zwanenburg.caffeinetile_4.apk', + 'icon': 'info.zwanenburg.caffeinetile.4.xml', + 'added': datetime.datetime.fromtimestamp(1539122400), + } + requestsdict = {'install': [], 'uninstall': []} + fdroidserver.common.config['repo_pubkey'] = 'ffffffffffffffffffffffffffffffffff' + fdroidserver.common.config['make_current_version_link'] = True + fdroidserver.index.make_v0(apps, [apk], 'repo', repodict, requestsdict, {}) self.assertTrue(os.path.isdir(repo_icons_dir)) self.assertTrue(os.path.exists(os.path.join(repo_icons_dir, fdroidserver.common.default_config['repo_icon']))) diff --git a/tests/metadata/info.guardianproject.checkey.yml b/tests/metadata/info.guardianproject.checkey.yml index 78b75320..7d933abd 100644 --- a/tests/metadata/info.guardianproject.checkey.yml +++ b/tests/metadata/info.guardianproject.checkey.yml @@ -9,6 +9,7 @@ Translation: https://www.transifex.com/otf/checkey Bitcoin: 1Fi5xUHiAPRKxHvyUGVFGt9extBe8Srdbk Liberapay: GuardianProject +Name: Checkey the app! AutoName: Checkey AutoUpdateMode: None diff --git a/tests/metadata/info.guardianproject.checkey/en-US/name.txt b/tests/metadata/info.guardianproject.checkey/en-US/name.txt new file mode 100644 index 00000000..09d725c6 --- /dev/null +++ b/tests/metadata/info.guardianproject.checkey/en-US/name.txt @@ -0,0 +1 @@ +Checkey: info on local apps diff --git a/tests/metadata/info.guardianproject.checkey/ja-JP/name.txt b/tests/metadata/info.guardianproject.checkey/ja-JP/name.txt new file mode 100644 index 00000000..c96d43b5 --- /dev/null +++ b/tests/metadata/info.guardianproject.checkey/ja-JP/name.txt @@ -0,0 +1 @@ +Checkey: ローカルアプリの情報 diff --git a/tests/repo/index-v1.json b/tests/repo/index-v1.json index e781d974..fe93947a 100644 --- a/tests/repo/index-v1.json +++ b/tests/repo/index-v1.json @@ -32,14 +32,18 @@ "flattrID": "cad90e036b975ed129a3ce80a0750466", "issueTracker": "https://gitlab.com/souch/SMSbypass/issues", "license": "GPL-3.0-only", - "name": "Battery level", "sourceCode": "https://gitlab.com/souch/SMSbypass/tree/HEAD", "summary": "Filter SMS and show them in a fake app", "webSite": "https://gitlab.com/souch/SMSbypass", "added": 1524700800000, "icon": "souch.smsbypass.9.png", "packageName": "souch.smsbypass", - "lastUpdated": 1524700800000 + "lastUpdated": 1524700800000, + "localized": { + "en-US": { + "name": "Battery level" + } + } }, { "categories": [ @@ -77,13 +81,17 @@ "donate": "https://f-droid.org/about", "issueTracker": "https://gitlab.com/fdroid/privileged-extension/issues", "license": "Apache-2.0", - "name": "fake.ota.update_1234", "sourceCode": "https://gitlab.com/fdroid/privileged-extension", "summary": "Tests whether OTA ZIP files are being include", "webSite": "https://f-droid.org", "added": 1457568000000, "packageName": "fake.ota.update", - "lastUpdated": 1457568000000 + "lastUpdated": 1457568000000, + "localized": { + "en-US": { + "name": "fake.ota.update_1234" + } + } }, { "categories": [ @@ -106,12 +114,16 @@ "suggestedVersionCode": "99999999", "liberapay": "12334", "license": "GPL-3.0-only", - "name": "OBB Main Old Version", "sourceCode": "https://github.com/eighthave/urzip", "added": 1388448000000, "icon": "obb.main.oldversion.1444412523.png", "packageName": "obb.main.oldversion", - "lastUpdated": 1388448000000 + "lastUpdated": 1388448000000, + "localized": { + "en-US": { + "name": "OBB Main Old Version" + } + } }, { "bitcoin": "1Fi5xUHiAPRKxHvyUGVFGt9extBe8Srdbk", @@ -120,12 +132,16 @@ ], "suggestedVersionCode": "99999999", "license": "GPL-3.0-only", - "name": "OBB Main Two Versions", "sourceCode": "https://github.com/eighthave/urzip", "added": 1444608000000, "icon": "obb.main.twoversions.1101617.png", "packageName": "obb.main.twoversions", - "lastUpdated": 1466380800000 + "lastUpdated": 1466380800000, + "localized": { + "en-US": { + "name": "OBB Main Two Versions" + } + } }, { "bitcoin": "1Fi5xUHiAPRKxHvyUGVFGt9extBe8Srdbk", @@ -134,7 +150,6 @@ ], "suggestedVersionCode": "99999999", "license": "GPL-3.0-only", - "name": "OBB Main/Patch Current", "sourceCode": "https://github.com/eighthave/urzip", "added": 1461369600000, "icon": "obb.mainpatch.current.1619.png", @@ -144,6 +159,7 @@ "en-US": { "featureGraphic": "featureGraphic_ffhLaojxbGAfu9ROe1MJgK5ux8d0OVc6b65nmvOBaTk=.png", "icon": "icon_WI0pkO3LsklrsTAnRr-OQSxkkoMY41lYe2-fAvXLiLg=.png", + "name": "OBB Main/Patch Current", "phoneScreenshots": [ "screenshot-main.png" ], @@ -165,13 +181,17 @@ "description": "Activates silent mode during calendar events.", "issueTracker": "https://github.com/miguelvps/PoliteDroid/issues", "license": "GPL-3.0-only", - "name": "Polite Droid", "sourceCode": "https://github.com/miguelvps/PoliteDroid", "summary": "Calendar tool", "added": 1498176000000, "icon": "com.politedroid.6.png", "packageName": "com.politedroid", - "lastUpdated": 1498176000000 + "lastUpdated": 1498176000000, + "localized": { + "en-US": { + "name": "Polite Droid" + } + } }, { "authorWebSite": "https://guardianproject.info", @@ -187,7 +207,6 @@ "issueTracker": "https://dev.guardianproject.info/projects/urzip/issues", "liberapayID": "9999999", "license": "GPL-3.0-only", - "name": "urzip-\u03c0\u00c7\u00c7\u03c0\u00c7\u00c7\u73b0\u4ee3\u6c49\u8bed\u901a\u7528\u5b57-\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438-\u0639\u0631\u0628\u064a1234", "openCollective": "f-droid-just-testing", "sourceCode": "https://github.com/guardianproject/urzip", "summary": "\u4e00\u4e2a\u5b9e\u7528\u5de5\u5177\uff0c\u83b7\u53d6\u5df2\u5b89\u88c5\u5728\u60a8\u7684\u8bbe\u5907\u4e0a\u7684\u5e94\u7528\u7684\u6709\u5173\u4fe1\u606f", diff --git a/tests/repo/index.xml b/tests/repo/index.xml index 298fd022..9af45f96 100644 --- a/tests/repo/index.xml +++ b/tests/repo/index.xml @@ -371,7 +371,7 @@ APK is called F-Droid Privileged Extension. info.guardianproject.urzip 2016-06-23 2016-06-23 - urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234 + title 一个实用工具,获取已安装在您的设备上的应用的有关信息 info.guardianproject.urzip.100.png It’s Urzip 是一个获得已安装 APK 相关信息的实用工具。它从您的设备上已安装的所有应用开始,一键触摸即可显示 APK 的指纹,并且提供到达 virustotal.com 和 androidobservatory.org 的快捷链接,让您方便地了解特定 APK 的档案。它还可以让您导出签名证书和生成 ApkSignaturePin Pin 文件供 TrustedIntents 库使用。 diff --git a/tests/update.TestCase b/tests/update.TestCase index 4593f770..ffc8fa5f 100755 --- a/tests/update.TestCase +++ b/tests/update.TestCase @@ -2,6 +2,7 @@ # http://www.drdobbs.com/testing/unit-testing-with-python/240165163 +import copy import git import glob import inspect @@ -69,6 +70,10 @@ class UpdateTest(unittest.TestCase): def setUp(self): logging.basicConfig(level=logging.INFO) + logging.getLogger('androguard.axml').setLevel(logging.INFO) + logging.getLogger('androguard.core.api_specific_resources').setLevel(logging.INFO) + from PIL import PngImagePlugin + logging.getLogger(PngImagePlugin.__name__).setLevel(logging.INFO) self.basedir = os.path.join(localmodule, 'tests') self.tmpdir = os.path.abspath(os.path.join(self.basedir, '..', '.testfiles')) if not os.path.exists(self.tmpdir): @@ -174,6 +179,140 @@ class UpdateTest(unittest.TestCase): elif packageName == 'eu.siacs.conversations': self.assertEqual('Conversations', app['localized']['en-US']['name']) + def test_name_title_scraping(self): + """metadata file --> fdroiddata localized files --> fastlane/triple-t in app source --> APK""" + config = dict() + fdroidserver.common.fill_config_defaults(config) + fdroidserver.common.config = config + fdroidserver.update.config = config + os.chdir(os.path.join(localmodule, 'tests')) + fdroidserver.update.options = type('', (), {})() + fdroidserver.update.options.clean = True + fdroidserver.update.options.delete_unknown = True + fdroidserver.update.options.rename_apks = False + fdroidserver.update.options.allow_disabled_algorithms = False + + apps = fdroidserver.metadata.read_metadata() + knownapks = fdroidserver.common.KnownApks() + apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False) + fdroidserver.update.insert_localized_app_metadata(apps) + 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']) + 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}, + ] + 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): + config = dict() + fdroidserver.common.fill_config_defaults(config) + fdroidserver.common.config = config + fdroidserver.update.config = config + fdroidserver.update.options = type('', (), {})() + fdroidserver.update.options.clean = True + fdroidserver.update.options.delete_unknown = True + fdroidserver.update.options.rename_apks = False + fdroidserver.update.options.allow_disabled_algorithms = False + + 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']) + del(testapps[appid]['Name']) + 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')) + + del(repoapps['info.guardianproject.urzip']['localized']) + fdroidserver.update.insert_missing_app_names_from_apks(repoapps, apks) + self.assertEqual('urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234', + repoapps['info.guardianproject.urzip']['localized']['en-US']['name']) + def test_insert_triple_t_metadata(self): importer = os.path.join(self.basedir, 'tmp', 'importer') packageName = 'org.fdroid.ci.test.app'