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