chiark / gitweb /
bash completion: use correct yml suffix
[fdroidserver.git] / tests / update.TestCase
1 #!/usr/bin/env python3
2
3 # http://www.drdobbs.com/testing/unit-testing-with-python/240165163
4
5 import git
6 import inspect
7 import logging
8 import optparse
9 import os
10 import shutil
11 import subprocess
12 import sys
13 import tempfile
14 import unittest
15 import yaml
16 from binascii import unhexlify
17 from distutils.version import LooseVersion
18
19 localmodule = os.path.realpath(
20     os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
21 print('localmodule: ' + localmodule)
22 if localmodule not in sys.path:
23     sys.path.insert(0, localmodule)
24
25 import fdroidserver.common
26 import fdroidserver.exception
27 import fdroidserver.metadata
28 import fdroidserver.update
29 from fdroidserver.common import FDroidPopen
30
31
32 class UpdateTest(unittest.TestCase):
33     '''fdroid update'''
34
35     def testInsertStoreMetadata(self):
36         config = dict()
37         fdroidserver.common.fill_config_defaults(config)
38         config['accepted_formats'] = ('txt', 'yml')
39         fdroidserver.update.config = config
40         fdroidserver.update.options = fdroidserver.common.options
41         os.chdir(os.path.join(localmodule, 'tests'))
42
43         shutil.rmtree(os.path.join('repo', 'info.guardianproject.urzip'), ignore_errors=True)
44
45         shutil.rmtree(os.path.join('build', 'com.nextcloud.client'), ignore_errors=True)
46         shutil.copytree(os.path.join('source-files', 'com.nextcloud.client'), os.path.join('build', 'com.nextcloud.client'))
47
48         shutil.rmtree(os.path.join('build', 'com.nextcloud.client.dev'), ignore_errors=True)
49         shutil.copytree(os.path.join('source-files', 'com.nextcloud.client.dev'),
50                         os.path.join('build', 'com.nextcloud.client.dev'))
51
52         apps = dict()
53         for packageName in ('info.guardianproject.urzip', 'org.videolan.vlc', 'obb.mainpatch.current',
54                             'com.nextcloud.client', 'com.nextcloud.client.dev'):
55             apps[packageName] = fdroidserver.metadata.App()
56             apps[packageName]['id'] = packageName
57             apps[packageName]['CurrentVersionCode'] = 0xcafebeef
58
59         apps['info.guardianproject.urzip']['CurrentVersionCode'] = 100
60
61         buildnextcloudclient = fdroidserver.metadata.Build()
62         buildnextcloudclient.gradle = ['generic']
63         apps['com.nextcloud.client']['builds'] = [buildnextcloudclient]
64
65         buildnextclouddevclient = fdroidserver.metadata.Build()
66         buildnextclouddevclient.gradle = ['versionDev']
67         apps['com.nextcloud.client.dev']['builds'] = [buildnextclouddevclient]
68
69         fdroidserver.update.insert_localized_app_metadata(apps)
70
71         appdir = os.path.join('repo', 'info.guardianproject.urzip', 'en-US')
72         self.assertTrue(os.path.isfile(os.path.join(appdir, 'icon.png')))
73         self.assertTrue(os.path.isfile(os.path.join(appdir, 'featureGraphic.png')))
74
75         self.assertEqual(5, len(apps))
76         for packageName, app in apps.items():
77             self.assertTrue('localized' in app)
78             self.assertTrue('en-US' in app['localized'])
79             self.assertEqual(1, len(app['localized']))
80             if packageName == 'info.guardianproject.urzip':
81                 self.assertEqual(7, len(app['localized']['en-US']))
82                 self.assertEqual('full description\n', app['localized']['en-US']['description'])
83                 self.assertEqual('title\n', app['localized']['en-US']['name'])
84                 self.assertEqual('short description\n', app['localized']['en-US']['summary'])
85                 self.assertEqual('video\n', app['localized']['en-US']['video'])
86                 self.assertEqual('icon.png', app['localized']['en-US']['icon'])
87                 self.assertEqual('featureGraphic.png', app['localized']['en-US']['featureGraphic'])
88                 self.assertEqual('100\n', app['localized']['en-US']['whatsNew'])
89             elif packageName == 'org.videolan.vlc':
90                 self.assertEqual('icon.png', app['localized']['en-US']['icon'])
91                 self.assertEqual(9, len(app['localized']['en-US']['phoneScreenshots']))
92                 self.assertEqual(15, len(app['localized']['en-US']['sevenInchScreenshots']))
93             elif packageName == 'obb.mainpatch.current':
94                 self.assertEqual('icon.png', app['localized']['en-US']['icon'])
95                 self.assertEqual('featureGraphic.png', app['localized']['en-US']['featureGraphic'])
96                 self.assertEqual(1, len(app['localized']['en-US']['phoneScreenshots']))
97                 self.assertEqual(1, len(app['localized']['en-US']['sevenInchScreenshots']))
98             elif packageName == 'com.nextcloud.client':
99                 self.assertEqual('Nextcloud', app['localized']['en-US']['name'])
100                 self.assertEqual(1073, len(app['localized']['en-US']['description']))
101                 self.assertEqual(78, len(app['localized']['en-US']['summary']))
102             elif packageName == 'com.nextcloud.client.dev':
103                 self.assertEqual('Nextcloud Dev', app['localized']['en-US']['name'])
104                 self.assertEqual(586, len(app['localized']['en-US']['description']))
105                 self.assertEqual(79, len(app['localized']['en-US']['summary']))
106
107     def test_insert_triple_t_metadata(self):
108         importer = os.path.join(localmodule, 'tests', 'tmp', 'importer')
109         packageName = 'org.fdroid.ci.test.app'
110         if not os.path.isdir(importer):
111             logging.warning('skipping test_insert_triple_t_metadata, import.TestCase must run first!')
112             return
113         tmpdir = os.path.join(localmodule, '.testfiles')
114         if not os.path.exists(tmpdir):
115             os.makedirs(tmpdir)
116         tmptestsdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=tmpdir)
117         packageDir = os.path.join(tmptestsdir, 'build', packageName)
118         shutil.copytree(importer, packageDir)
119
120         # always use the same commit so these tests work when ci-test-app.git is updated
121         repo = git.Repo(packageDir)
122         for remote in repo.remotes:
123             remote.fetch()
124         repo.git.reset('--hard', 'b9e5d1a0d8d6fc31d4674b2f0514fef10762ed4f')
125         repo.git.clean('-fdx')
126
127         os.mkdir(os.path.join(tmptestsdir, 'metadata'))
128         metadata = dict()
129         metadata['Description'] = 'This is just a test app'
130         with open(os.path.join(tmptestsdir, 'metadata', packageName + '.yml'), 'w') as fp:
131             yaml.dump(metadata, fp)
132
133         config = dict()
134         fdroidserver.common.fill_config_defaults(config)
135         config['accepted_formats'] = ('yml')
136         fdroidserver.common.config = config
137         fdroidserver.update.config = config
138         fdroidserver.update.options = fdroidserver.common.options
139         os.chdir(tmptestsdir)
140
141         apps = fdroidserver.metadata.read_metadata(xref=True)
142         fdroidserver.update.copy_triple_t_store_metadata(apps)
143
144         # TODO ideally, this would compare the whole dict like in metadata.TestCase's test_read_metadata()
145         correctlocales = [
146             'ar', 'ast_ES', 'az', 'ca', 'ca_ES', 'cs-CZ', 'cs_CZ', 'da',
147             'da-DK', 'de', 'de-DE', 'el', 'en-US', 'es', 'es-ES', 'es_ES', 'et',
148             'fi', 'fr', 'fr-FR', 'he_IL', 'hi-IN', 'hi_IN', 'hu', 'id', 'it',
149             'it-IT', 'it_IT', 'iw-IL', 'ja', 'ja-JP', 'kn_IN', 'ko', 'ko-KR',
150             'ko_KR', 'lt', 'nb', 'nb_NO', 'nl', 'nl-NL', 'no', 'pl', 'pl-PL',
151             'pl_PL', 'pt', 'pt-BR', 'pt-PT', 'pt_BR', 'ro', 'ro_RO', 'ru-RU',
152             'ru_RU', 'sv-SE', 'sv_SE', 'te', 'tr', 'tr-TR', 'uk', 'uk_UA', 'vi',
153             'vi_VN', 'zh-CN', 'zh_CN', 'zh_TW',
154         ]
155         locales = sorted(list(apps['org.fdroid.ci.test.app']['localized'].keys()))
156         self.assertEqual(correctlocales, locales)
157
158     def javagetsig(self, apkfile):
159         getsig_dir = os.path.join(os.path.dirname(__file__), 'getsig')
160         if not os.path.exists(getsig_dir + "/getsig.class"):
161             logging.critical("getsig.class not found. To fix: cd '%s' && ./make.sh" % getsig_dir)
162             sys.exit(1)
163         # FDroidPopen needs some config to work
164         config = dict()
165         fdroidserver.common.fill_config_defaults(config)
166         fdroidserver.common.config = config
167         p = FDroidPopen(['java', '-cp', os.path.join(os.path.dirname(__file__), 'getsig'),
168                          'getsig', os.path.join(os.getcwd(), apkfile)])
169         sig = None
170         for line in p.output.splitlines():
171             if line.startswith('Result:'):
172                 sig = line[7:].strip()
173                 break
174         if p.returncode == 0:
175             return sig
176         else:
177             return None
178
179     def testGoodGetsig(self):
180         # config needed to use jarsigner and keytool
181         config = dict()
182         fdroidserver.common.fill_config_defaults(config)
183         fdroidserver.update.config = config
184         apkfile = os.path.join(os.path.dirname(__file__), 'urzip.apk')
185         sig = self.javagetsig(apkfile)
186         self.assertIsNotNone(sig, "sig is None")
187         pysig = fdroidserver.update.getsig(apkfile)
188         self.assertIsNotNone(pysig, "pysig is None")
189         self.assertEqual(sig, fdroidserver.update.getsig(apkfile),
190                          "python sig not equal to java sig!")
191         self.assertEqual(len(sig), len(pysig),
192                          "the length of the two sigs are different!")
193         try:
194             self.assertEqual(unhexlify(sig), unhexlify(pysig),
195                              "the length of the two sigs are different!")
196         except TypeError as e:
197             print(e)
198             self.assertTrue(False, 'TypeError!')
199
200     def testBadGetsig(self):
201         """getsig() should still be able to fetch the fingerprint of bad signatures"""
202         # config needed to use jarsigner and keytool
203         config = dict()
204         fdroidserver.common.fill_config_defaults(config)
205         fdroidserver.update.config = config
206
207         apkfile = os.path.join(os.path.dirname(__file__), 'urzip-badsig.apk')
208         sig = fdroidserver.update.getsig(apkfile)
209         self.assertEqual(sig, 'e0ecb5fc2d63088e4a07ae410a127722',
210                          "python sig should be: " + str(sig))
211
212         apkfile = os.path.join(os.path.dirname(__file__), 'urzip-badcert.apk')
213         sig = fdroidserver.update.getsig(apkfile)
214         self.assertEqual(sig, 'e0ecb5fc2d63088e4a07ae410a127722',
215                          "python sig should be: " + str(sig))
216
217     def testScanApksAndObbs(self):
218         os.chdir(os.path.join(localmodule, 'tests'))
219         if os.path.basename(os.getcwd()) != 'tests':
220             raise Exception('This test must be run in the "tests/" subdir')
221
222         config = dict()
223         fdroidserver.common.fill_config_defaults(config)
224         config['ndk_paths'] = dict()
225         config['accepted_formats'] = ['json', 'txt', 'yml']
226         fdroidserver.common.config = config
227         fdroidserver.update.config = config
228
229         fdroidserver.update.options = type('', (), {})()
230         fdroidserver.update.options.clean = True
231         fdroidserver.update.options.delete_unknown = True
232         fdroidserver.update.options.rename_apks = False
233         fdroidserver.update.options.allow_disabled_algorithms = False
234
235         apps = fdroidserver.metadata.read_metadata(xref=True)
236         knownapks = fdroidserver.common.KnownApks()
237         apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
238         self.assertEqual(len(apks), 11)
239         apk = apks[0]
240         self.assertEqual(apk['packageName'], 'com.politedroid')
241         self.assertEqual(apk['versionCode'], 3)
242         self.assertEqual(apk['minSdkVersion'], '3')
243         self.assertEqual(apk['targetSdkVersion'], '3')
244         self.assertFalse('maxSdkVersion' in apk)
245         apk = apks[4]
246         self.assertEqual(apk['packageName'], 'obb.main.oldversion')
247         self.assertEqual(apk['versionCode'], 1444412523)
248         self.assertEqual(apk['minSdkVersion'], '4')
249         self.assertEqual(apk['targetSdkVersion'], '18')
250         self.assertFalse('maxSdkVersion' in apk)
251
252         fdroidserver.update.insert_obbs('repo', apps, apks)
253         for apk in apks:
254             if apk['packageName'] == 'obb.mainpatch.current':
255                 self.assertEqual(apk.get('obbMainFile'), 'main.1619.obb.mainpatch.current.obb')
256                 self.assertEqual(apk.get('obbPatchFile'), 'patch.1619.obb.mainpatch.current.obb')
257             elif apk['packageName'] == 'obb.main.oldversion':
258                 self.assertEqual(apk.get('obbMainFile'), 'main.1434483388.obb.main.oldversion.obb')
259                 self.assertIsNone(apk.get('obbPatchFile'))
260             elif apk['packageName'] == 'obb.main.twoversions':
261                 self.assertIsNone(apk.get('obbPatchFile'))
262                 if apk['versionCode'] == 1101613:
263                     self.assertEqual(apk.get('obbMainFile'), 'main.1101613.obb.main.twoversions.obb')
264                 elif apk['versionCode'] == 1101615:
265                     self.assertEqual(apk.get('obbMainFile'), 'main.1101615.obb.main.twoversions.obb')
266                 elif apk['versionCode'] == 1101617:
267                     self.assertEqual(apk.get('obbMainFile'), 'main.1101615.obb.main.twoversions.obb')
268                 else:
269                     self.assertTrue(False)
270             elif apk['packageName'] == 'info.guardianproject.urzip':
271                 self.assertIsNone(apk.get('obbMainFile'))
272                 self.assertIsNone(apk.get('obbPatchFile'))
273
274     def test_scan_apk(self):
275         config = dict()
276         fdroidserver.common.fill_config_defaults(config)
277         fdroidserver.update.config = config
278         os.chdir(os.path.join(localmodule, 'tests'))
279         if os.path.basename(os.getcwd()) != 'tests':
280             raise Exception('This test must be run in the "tests/" subdir')
281
282         apk_info = fdroidserver.update.scan_apk('org.dyndns.fules.ck_20.apk')
283
284         self.assertEqual(apk_info['icons_src'], {'240': 'res/drawable-hdpi-v4/icon_launcher.png',
285                                                  '120': 'res/drawable-ldpi-v4/icon_launcher.png',
286                                                  '160': 'res/drawable-mdpi-v4/icon_launcher.png',
287                                                  '-1': 'res/drawable-mdpi-v4/icon_launcher.png'})
288         self.assertEqual(apk_info['icons'], {})
289         self.assertEqual(apk_info['features'], [])
290         self.assertEqual(apk_info['antiFeatures'], set())
291         self.assertEqual(apk_info['versionName'], 'v1.6pre2')
292         self.assertEqual(apk_info['hash'],
293                          '897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8')
294         self.assertEqual(apk_info['packageName'], 'org.dyndns.fules.ck')
295         self.assertEqual(apk_info['versionCode'], 20)
296         self.assertEqual(apk_info['size'], 132453)
297         self.assertEqual(apk_info['nativecode'],
298                          ['arm64-v8a', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'x86', 'x86_64'])
299         self.assertEqual(apk_info['minSdkVersion'], '7')
300         self.assertEqual(apk_info['sig'], '9bf7a6a67f95688daec75eab4b1436ac')
301         self.assertEqual(apk_info['hashType'], 'sha256')
302         self.assertEqual(apk_info['targetSdkVersion'], '8')
303
304     def test_scan_apk_no_sig(self):
305         config = dict()
306         fdroidserver.common.fill_config_defaults(config)
307         fdroidserver.update.config = config
308         os.chdir(os.path.join(localmodule, 'tests'))
309         if os.path.basename(os.getcwd()) != 'tests':
310             raise Exception('This test must be run in the "tests/" subdir')
311
312         with self.assertRaises(fdroidserver.exception.BuildException):
313             fdroidserver.update.scan_apk('urzip-release-unsigned.apk')
314
315     def test_process_apk(self):
316
317         def _build_yaml_representer(dumper, data):
318             '''Creates a YAML representation of a Build instance'''
319             return dumper.represent_dict(data)
320
321         config = dict()
322         fdroidserver.common.fill_config_defaults(config)
323         fdroidserver.update.config = config
324         os.chdir(os.path.join(localmodule, 'tests'))
325         if os.path.basename(os.getcwd()) != 'tests':
326             raise Exception('This test must be run in the "tests/" subdir')
327
328         config['ndk_paths'] = dict()
329         config['accepted_formats'] = ['json', 'txt', 'yml']
330         fdroidserver.common.config = config
331         fdroidserver.update.config = config
332
333         fdroidserver.update.options = type('', (), {})()
334         fdroidserver.update.options.clean = True
335         fdroidserver.update.options.rename_apks = False
336         fdroidserver.update.options.delete_unknown = True
337         fdroidserver.update.options.allow_disabled_algorithms = False
338
339         for icon_dir in fdroidserver.update.get_all_icon_dirs('repo'):
340             if not os.path.exists(icon_dir):
341                 os.makedirs(icon_dir)
342
343         knownapks = fdroidserver.common.KnownApks()
344         apkList = ['../urzip.apk', '../org.dyndns.fules.ck_20.apk']
345
346         for apkName in apkList:
347             _, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo', knownapks,
348                                                                    False)
349             # Don't care about the date added to the repo and relative apkName
350             del apk['added']
351             del apk['apkName']
352             # avoid AAPT application name bug
353             del apk['name']
354
355             # ensure that icons have been extracted properly
356             if apkName == '../urzip.apk':
357                 self.assertEqual(apk['icon'], 'info.guardianproject.urzip.100.png')
358             if apkName == '../org.dyndns.fules.ck_20.apk':
359                 self.assertEqual(apk['icon'], 'org.dyndns.fules.ck.20.png')
360             for density in fdroidserver.update.screen_densities:
361                 icon_path = os.path.join(fdroidserver.update.get_icon_dir('repo', density),
362                                          apk['icon'])
363                 self.assertTrue(os.path.isfile(icon_path))
364                 self.assertTrue(os.path.getsize(icon_path) > 1)
365
366             savepath = os.path.join('metadata', 'apk', apk['packageName'] + '.yaml')
367             # Uncomment to save APK metadata
368             # with open(savepath, 'w') as f:
369             #     yaml.add_representer(fdroidserver.metadata.Build, _build_yaml_representer)
370             #     yaml.dump(apk, f, default_flow_style=False)
371
372             with open(savepath, 'r') as f:
373                 frompickle = yaml.load(f)
374             self.maxDiff = None
375             self.assertEqual(apk, frompickle)
376
377     def test_process_apk_signed_by_disabled_algorithms(self):
378         os.chdir(os.path.join(localmodule, 'tests'))
379         if os.path.basename(os.getcwd()) != 'tests':
380             raise Exception('This test must be run in the "tests/" subdir')
381
382         config = dict()
383         fdroidserver.common.fill_config_defaults(config)
384         fdroidserver.update.config = config
385
386         config['ndk_paths'] = dict()
387         config['accepted_formats'] = ['json', 'txt', 'yml']
388         fdroidserver.common.config = config
389         fdroidserver.update.config = config
390
391         fdroidserver.update.options = type('', (), {})()
392         fdroidserver.update.options.clean = True
393         fdroidserver.update.options.verbose = True
394         fdroidserver.update.options.rename_apks = False
395         fdroidserver.update.options.delete_unknown = True
396         fdroidserver.update.options.allow_disabled_algorithms = False
397
398         knownapks = fdroidserver.common.KnownApks()
399         apksourcedir = os.getcwd()
400         tmpdir = os.path.join(localmodule, '.testfiles')
401         if not os.path.exists(tmpdir):
402             os.makedirs(tmpdir)
403         tmptestsdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name,
404                                        dir=tmpdir)
405         print('tmptestsdir', tmptestsdir)
406         os.chdir(tmptestsdir)
407         os.mkdir('repo')
408         os.mkdir('archive')
409         # setup the repo, create icons dirs, etc.
410         fdroidserver.update.process_apks({}, 'repo', knownapks)
411         fdroidserver.update.process_apks({}, 'archive', knownapks)
412
413         disabledsigs = ['org.bitbucket.tickytacky.mirrormirror_2.apk', ]
414         for apkName in disabledsigs:
415             shutil.copy(os.path.join(apksourcedir, apkName),
416                         os.path.join(tmptestsdir, 'repo'))
417
418             skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo',
419                                                                       knownapks,
420                                                                       allow_disabled_algorithms=True,
421                                                                       archive_bad_sig=False)
422             self.assertFalse(skip)
423             self.assertIsNotNone(apk)
424             self.assertTrue(cachechanged)
425             self.assertFalse(os.path.exists(os.path.join('archive', apkName)))
426             self.assertTrue(os.path.exists(os.path.join('repo', apkName)))
427
428             javac = config['jarsigner'].replace('jarsigner', 'javac')
429             v = subprocess.check_output([javac, '-version'], stderr=subprocess.STDOUT)[6:-1].decode('utf-8')
430             if LooseVersion(v) < LooseVersion('1.8.0_132'):
431                 print('SKIPPING: running tests with old Java (' + v + ')')
432                 return
433
434             # this test only works on systems with fully updated Java/jarsigner
435             # that has MD5 listed in jdk.jar.disabledAlgorithms in java.security
436             # https://blogs.oracle.com/java-platform-group/oracle-jre-will-no-longer-trust-md5-signed-code-by-default
437             skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo',
438                                                                       knownapks,
439                                                                       allow_disabled_algorithms=False,
440                                                                       archive_bad_sig=True)
441             self.assertTrue(skip)
442             self.assertIsNone(apk)
443             self.assertFalse(cachechanged)
444             self.assertTrue(os.path.exists(os.path.join('archive', apkName)))
445             self.assertFalse(os.path.exists(os.path.join('repo', apkName)))
446
447             skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'archive',
448                                                                       knownapks,
449                                                                       allow_disabled_algorithms=False,
450                                                                       archive_bad_sig=False)
451             self.assertFalse(skip)
452             self.assertIsNotNone(apk)
453             self.assertTrue(cachechanged)
454             self.assertTrue(os.path.exists(os.path.join('archive', apkName)))
455             self.assertFalse(os.path.exists(os.path.join('repo', apkName)))
456
457             # ensure that icons have been moved to the archive as well
458             for density in fdroidserver.update.screen_densities:
459                 icon_path = os.path.join(fdroidserver.update.get_icon_dir('archive', density),
460                                          apk['icon'])
461                 self.assertTrue(os.path.isfile(icon_path))
462                 self.assertTrue(os.path.getsize(icon_path) > 1)
463
464         badsigs = ['urzip-badcert.apk', 'urzip-badsig.apk', 'urzip-release-unsigned.apk', ]
465         for apkName in badsigs:
466             shutil.copy(os.path.join(apksourcedir, apkName),
467                         os.path.join(tmptestsdir, 'repo'))
468
469             skip, apk, cachechanged = fdroidserver.update.process_apk({}, apkName, 'repo',
470                                                                       knownapks,
471                                                                       allow_disabled_algorithms=False,
472                                                                       archive_bad_sig=False)
473             self.assertTrue(skip)
474             self.assertIsNone(apk)
475             self.assertFalse(cachechanged)
476
477     def test_process_invalid_apk(self):
478         os.chdir(os.path.join(localmodule, 'tests'))
479         if os.path.basename(os.getcwd()) != 'tests':
480             raise Exception('This test must be run in the "tests/" subdir')
481
482         config = dict()
483         fdroidserver.common.fill_config_defaults(config)
484         fdroidserver.common.config = config
485         fdroidserver.update.config = config
486         fdroidserver.update.options.delete_unknown = False
487
488         knownapks = fdroidserver.common.KnownApks()
489         apk = 'fake.ota.update_1234.zip'  # this is not an APK, scanning should fail
490         (skip, apk, cachechanged) = fdroidserver.update.process_apk({}, apk, 'repo', knownapks,
491                                                                     False)
492
493         self.assertTrue(skip)
494         self.assertIsNone(apk)
495         self.assertFalse(cachechanged)
496
497     def test_translate_per_build_anti_features(self):
498         os.chdir(os.path.join(localmodule, 'tests'))
499         if os.path.basename(os.getcwd()) != 'tests':
500             raise Exception('This test must be run in the "tests/" subdir')
501
502         config = dict()
503         fdroidserver.common.fill_config_defaults(config)
504         config['ndk_paths'] = dict()
505         config['accepted_formats'] = ['json', 'txt', 'yml']
506         fdroidserver.common.config = config
507         fdroidserver.update.config = config
508
509         fdroidserver.update.options = type('', (), {})()
510         fdroidserver.update.options.clean = True
511         fdroidserver.update.options.delete_unknown = True
512         fdroidserver.update.options.rename_apks = False
513         fdroidserver.update.options.allow_disabled_algorithms = False
514
515         apps = fdroidserver.metadata.read_metadata(xref=True)
516         knownapks = fdroidserver.common.KnownApks()
517         apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
518         fdroidserver.update.translate_per_build_anti_features(apps, apks)
519         self.assertEqual(len(apks), 11)
520         foundtest = False
521         for apk in apks:
522             if apk['packageName'] == 'com.politedroid' and apk['versionCode'] == 3:
523                 antiFeatures = apk.get('antiFeatures')
524                 self.assertTrue('KnownVuln' in antiFeatures)
525                 self.assertEqual(3, len(antiFeatures))
526                 foundtest = True
527         self.assertTrue(foundtest)
528
529     def test_create_metadata_from_template(self):
530         tmpdir = os.path.join(localmodule, '.testfiles')
531         if not os.path.exists(tmpdir):
532             os.makedirs(tmpdir)
533         tmptestsdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name,
534                                        dir=tmpdir)
535         print('tmptestsdir', tmptestsdir)
536         os.chdir(tmptestsdir)
537         os.mkdir('repo')
538         os.mkdir('metadata')
539         shutil.copy(os.path.join(localmodule, 'tests', 'urzip.apk'), 'repo')
540
541         config = dict()
542         fdroidserver.common.fill_config_defaults(config)
543         config['ndk_paths'] = dict()
544         config['accepted_formats'] = ['json', 'txt', 'yml']
545         fdroidserver.common.config = config
546         fdroidserver.update.config = config
547
548         fdroidserver.update.options = type('', (), {})()
549         fdroidserver.update.options.clean = True
550         fdroidserver.update.options.delete_unknown = False
551         fdroidserver.update.options.rename_apks = False
552         fdroidserver.update.options.allow_disabled_algorithms = False
553
554         knownapks = fdroidserver.common.KnownApks()
555         apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False)
556         self.assertEqual(1, len(apks))
557         apk = apks[0]
558
559         testfile = 'metadata/info.guardianproject.urzip.yml'
560         # create empty 0 byte .yml file, run read_metadata, it should work
561         open(testfile, 'a').close()
562         apps = fdroidserver.metadata.read_metadata(xref=True)
563         self.assertEqual(1, len(apps))
564         os.remove(testfile)
565
566         # test using internal template
567         apps = fdroidserver.metadata.read_metadata(xref=True)
568         self.assertEqual(0, len(apps))
569         fdroidserver.update.create_metadata_from_template(apk)
570         self.assertTrue(os.path.exists(testfile))
571         apps = fdroidserver.metadata.read_metadata(xref=True)
572         self.assertEqual(1, len(apps))
573         for app in apps.values():
574             self.assertEqual('urzip', app['Name'])
575             self.assertEqual(1, len(app['Categories']))
576             break
577
578         # test using external template.yml
579         os.remove(testfile)
580         self.assertFalse(os.path.exists(testfile))
581         shutil.copy(os.path.join(localmodule, 'examples', 'template.yml'), tmptestsdir)
582         fdroidserver.update.create_metadata_from_template(apk)
583         self.assertTrue(os.path.exists(testfile))
584         apps = fdroidserver.metadata.read_metadata(xref=True)
585         self.assertEqual(1, len(apps))
586         for app in apps.values():
587             self.assertEqual('urzip', app['Name'])
588             self.assertEqual(1, len(app['Categories']))
589             self.assertEqual('Internet', app['Categories'][0])
590             break
591         with open(testfile) as fp:
592             data = yaml.load(fp)
593         self.assertEqual('urzip', data['Name'])
594         self.assertEqual('urzip', data['Summary'])
595
596
597 if __name__ == "__main__":
598     parser = optparse.OptionParser()
599     parser.add_option("-v", "--verbose", action="store_true", default=False,
600                       help="Spew out even more information than normal")
601     (fdroidserver.common.options, args) = parser.parse_args(['--verbose'])
602
603     newSuite = unittest.TestSuite()
604     newSuite.addTest(unittest.makeSuite(UpdateTest))
605     unittest.main(failfast=False)