chiark / gitweb /
Merge branch 'googlemaven' into 'master'
[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.metadata
25 import fdroidserver.update
26 from fdroidserver.common import FDroidPopen
27
28
29 class UpdateTest(unittest.TestCase):
30     '''fdroid update'''
31
32     def testInsertStoreMetadata(self):
33         config = dict()
34         fdroidserver.common.fill_config_defaults(config)
35         config['accepted_formats'] = ('txt', 'yml')
36         fdroidserver.update.config = config
37         fdroidserver.update.options = fdroidserver.common.options
38         os.chdir(os.path.join(localmodule, 'tests'))
39
40         shutil.rmtree(os.path.join('repo', 'info.guardianproject.urzip'), ignore_errors=True)
41
42         apps = dict()
43         for packageName in ('info.guardianproject.urzip', 'org.videolan.vlc', 'obb.mainpatch.current'):
44             apps[packageName] = dict()
45             apps[packageName]['id'] = packageName
46             apps[packageName]['CurrentVersionCode'] = 0xcafebeef
47         apps['info.guardianproject.urzip']['CurrentVersionCode'] = 100
48         fdroidserver.update.insert_localized_app_metadata(apps)
49
50         appdir = os.path.join('repo', 'info.guardianproject.urzip', 'en-US')
51         self.assertTrue(os.path.isfile(os.path.join(appdir, 'icon.png')))
52         self.assertTrue(os.path.isfile(os.path.join(appdir, 'featureGraphic.png')))
53
54         self.assertEqual(3, len(apps))
55         for packageName, app in apps.items():
56             self.assertTrue('localized' in app)
57             self.assertTrue('en-US' in app['localized'])
58             self.assertEqual(1, len(app['localized']))
59             if packageName == 'info.guardianproject.urzip':
60                 self.assertEqual(7, len(app['localized']['en-US']))
61                 self.assertEqual('full description\n', app['localized']['en-US']['description'])
62                 self.assertEqual('title\n', app['localized']['en-US']['name'])
63                 self.assertEqual('short description\n', app['localized']['en-US']['summary'])
64                 self.assertEqual('video\n', app['localized']['en-US']['video'])
65                 self.assertEqual('icon.png', app['localized']['en-US']['icon'])
66                 self.assertEqual('featureGraphic.png', app['localized']['en-US']['featureGraphic'])
67                 self.assertEqual('100\n', app['localized']['en-US']['whatsNew'])
68             elif packageName == 'org.videolan.vlc':
69                 self.assertEqual('icon.png', app['localized']['en-US']['icon'])
70                 self.assertEqual(9, len(app['localized']['en-US']['phoneScreenshots']))
71                 self.assertEqual(15, len(app['localized']['en-US']['sevenInchScreenshots']))
72             elif packageName == 'obb.mainpatch.current':
73                 self.assertEqual('icon.png', app['localized']['en-US']['icon'])
74                 self.assertEqual('featureGraphic.png', app['localized']['en-US']['featureGraphic'])
75                 self.assertEqual(1, len(app['localized']['en-US']['phoneScreenshots']))
76                 self.assertEqual(1, len(app['localized']['en-US']['sevenInchScreenshots']))
77
78     def test_insert_triple_t_metadata(self):
79         importer = os.path.join(localmodule, 'tests', 'tmp', 'importer')
80         packageName = 'org.fdroid.ci.test.app'
81         if not os.path.isdir(importer):
82             logging.warning('skipping test_insert_triple_t_metadata, import.TestCase must run first!')
83             return
84         tmpdir = os.path.join(localmodule, '.testfiles')
85         if not os.path.exists(tmpdir):
86             os.makedirs(tmpdir)
87         tmptestsdir = tempfile.mkdtemp(prefix='test_insert_triple_t_metadata-', dir=tmpdir)
88         packageDir = os.path.join(tmptestsdir, 'build', packageName)
89         shutil.copytree(importer, packageDir)
90
91         # always use the same commit so these tests work when ci-test-app.git is updated
92         repo = git.Repo(packageDir)
93         for remote in repo.remotes:
94             remote.fetch()
95         repo.git.reset('--hard', 'b9e5d1a0d8d6fc31d4674b2f0514fef10762ed4f')
96         repo.git.clean('-fdx')
97
98         os.mkdir(os.path.join(tmptestsdir, 'metadata'))
99         metadata = dict()
100         metadata['Description'] = 'This is just a test app'
101         with open(os.path.join(tmptestsdir, 'metadata', packageName + '.yml'), 'w') as fp:
102             yaml.dump(metadata, fp)
103
104         config = dict()
105         fdroidserver.common.fill_config_defaults(config)
106         config['accepted_formats'] = ('yml')
107         fdroidserver.common.config = config
108         fdroidserver.update.config = config
109         fdroidserver.update.options = fdroidserver.common.options
110         os.chdir(tmptestsdir)
111
112         apps = fdroidserver.metadata.read_metadata(xref=True)
113         fdroidserver.update.copy_triple_t_store_metadata(apps)
114
115         # TODO ideally, this would compare the whole dict like in metadata.TestCase's test_read_metadata()
116         correctlocales = [
117             'ar', 'ast_ES', 'az', 'ca', 'ca_ES', 'cs-CZ', 'cs_CZ', 'da',
118             'da-DK', 'de', 'de-DE', 'el', 'en-US', 'es', 'es-ES', 'es_ES', 'et',
119             'fi', 'fr', 'fr-FR', 'he_IL', 'hi-IN', 'hi_IN', 'hu', 'id', 'it',
120             'it-IT', 'it_IT', 'iw-IL', 'ja', 'ja-JP', 'kn_IN', 'ko', 'ko-KR',
121             'ko_KR', 'lt', 'nb', 'nb_NO', 'nl', 'nl-NL', 'no', 'pl', 'pl-PL',
122             'pl_PL', 'pt', 'pt-BR', 'pt-PT', 'pt_BR', 'ro', 'ro_RO', 'ru-RU',
123             'ru_RU', 'sv-SE', 'sv_SE', 'te', 'tr', 'tr-TR', 'uk', 'uk_UA', 'vi',
124             'vi_VN', 'zh-CN', 'zh_CN', 'zh_TW',
125         ]
126         locales = sorted(list(apps['org.fdroid.ci.test.app']['localized'].keys()))
127         self.assertEqual(correctlocales, locales)
128
129     def javagetsig(self, apkfile):
130         getsig_dir = os.path.join(os.path.dirname(__file__), 'getsig')
131         if not os.path.exists(getsig_dir + "/getsig.class"):
132             logging.critical("getsig.class not found. To fix: cd '%s' && ./make.sh" % getsig_dir)
133             sys.exit(1)
134         # FDroidPopen needs some config to work
135         config = dict()
136         fdroidserver.common.fill_config_defaults(config)
137         fdroidserver.common.config = config
138         p = FDroidPopen(['java', '-cp', os.path.join(os.path.dirname(__file__), 'getsig'),
139                          'getsig', os.path.join(os.getcwd(), apkfile)])
140         sig = None
141         for line in p.output.splitlines():
142             if line.startswith('Result:'):
143                 sig = line[7:].strip()
144                 break
145         if p.returncode == 0:
146             return sig
147         else:
148             return None
149
150     def testGoodGetsig(self):
151         # config needed to use jarsigner and keytool
152         config = dict()
153         fdroidserver.common.fill_config_defaults(config)
154         fdroidserver.update.config = config
155         apkfile = os.path.join(os.path.dirname(__file__), 'urzip.apk')
156         sig = self.javagetsig(apkfile)
157         self.assertIsNotNone(sig, "sig is None")
158         pysig = fdroidserver.update.getsig(apkfile)
159         self.assertIsNotNone(pysig, "pysig is None")
160         self.assertEqual(sig, fdroidserver.update.getsig(apkfile),
161                          "python sig not equal to java sig!")
162         self.assertEqual(len(sig), len(pysig),
163                          "the length of the two sigs are different!")
164         try:
165             self.assertEqual(unhexlify(sig), unhexlify(pysig),
166                              "the length of the two sigs are different!")
167         except TypeError as e:
168             print(e)
169             self.assertTrue(False, 'TypeError!')
170
171     def testBadGetsig(self):
172         """getsig() should still be able to fetch the fingerprint of bad signatures"""
173         # config needed to use jarsigner and keytool
174         config = dict()
175         fdroidserver.common.fill_config_defaults(config)
176         fdroidserver.update.config = config
177
178         apkfile = os.path.join(os.path.dirname(__file__), 'urzip-badsig.apk')
179         sig = fdroidserver.update.getsig(apkfile)
180         self.assertEqual(sig, 'e0ecb5fc2d63088e4a07ae410a127722',
181                          "python sig should be: " + str(sig))
182
183         apkfile = os.path.join(os.path.dirname(__file__), 'urzip-badcert.apk')
184         sig = fdroidserver.update.getsig(apkfile)
185         self.assertEqual(sig, 'e0ecb5fc2d63088e4a07ae410a127722',
186                          "python sig should be: " + str(sig))
187
188     def testScanApksAndObbs(self):
189         os.chdir(os.path.join(localmodule, 'tests'))
190         if os.path.basename(os.getcwd()) != 'tests':
191             raise Exception('This test must be run in the "tests/" subdir')
192
193         config = dict()
194         fdroidserver.common.fill_config_defaults(config)
195         config['ndk_paths'] = dict()
196         config['accepted_formats'] = ['json', 'txt', 'yml']
197         fdroidserver.common.config = config
198         fdroidserver.update.config = config
199
200         fdroidserver.update.options = type('', (), {})()
201         fdroidserver.update.options.clean = True
202         fdroidserver.update.options.delete_unknown = True
203         fdroidserver.update.options.rename_apks = False
204
205         apps = fdroidserver.metadata.read_metadata(xref=True)
206         knownapks = fdroidserver.common.KnownApks()
207         apks, cachechanged = fdroidserver.update.scan_apks({}, 'repo', knownapks, False)
208         self.assertEqual(len(apks), 7)
209         apk = apks[0]
210         self.assertEqual(apk['minSdkVersion'], '4')
211         self.assertEqual(apk['targetSdkVersion'], '18')
212         self.assertFalse('maxSdkVersion' in apk)
213
214         fdroidserver.update.insert_obbs('repo', apps, apks)
215         for apk in apks:
216             if apk['packageName'] == 'obb.mainpatch.current':
217                 self.assertEqual(apk.get('obbMainFile'), 'main.1619.obb.mainpatch.current.obb')
218                 self.assertEqual(apk.get('obbPatchFile'), 'patch.1619.obb.mainpatch.current.obb')
219             elif apk['packageName'] == 'obb.main.oldversion':
220                 self.assertEqual(apk.get('obbMainFile'), 'main.1434483388.obb.main.oldversion.obb')
221                 self.assertIsNone(apk.get('obbPatchFile'))
222             elif apk['packageName'] == 'obb.main.twoversions':
223                 self.assertIsNone(apk.get('obbPatchFile'))
224                 if apk['versionCode'] == 1101613:
225                     self.assertEqual(apk.get('obbMainFile'), 'main.1101613.obb.main.twoversions.obb')
226                 elif apk['versionCode'] == 1101615:
227                     self.assertEqual(apk.get('obbMainFile'), 'main.1101615.obb.main.twoversions.obb')
228                 elif apk['versionCode'] == 1101617:
229                     self.assertEqual(apk.get('obbMainFile'), 'main.1101615.obb.main.twoversions.obb')
230                 else:
231                     self.assertTrue(False)
232             elif apk['packageName'] == 'info.guardianproject.urzip':
233                 self.assertIsNone(apk.get('obbMainFile'))
234                 self.assertIsNone(apk.get('obbPatchFile'))
235
236     def testScanApkMetadata(self):
237
238         def _build_yaml_representer(dumper, data):
239             '''Creates a YAML representation of a Build instance'''
240             return dumper.represent_dict(data)
241
242         config = dict()
243         fdroidserver.common.fill_config_defaults(config)
244         fdroidserver.update.config = config
245         os.chdir(os.path.dirname(__file__))
246         if os.path.basename(os.getcwd()) != 'tests':
247             raise Exception('This test must be run in the "tests/" subdir')
248
249         config['ndk_paths'] = dict()
250         config['accepted_formats'] = ['json', 'txt', 'yml']
251         fdroidserver.common.config = config
252         fdroidserver.update.config = config
253
254         fdroidserver.update.options = type('', (), {})()
255         fdroidserver.update.options.clean = True
256         fdroidserver.update.options.rename_apks = False
257         fdroidserver.update.options.delete_unknown = True
258
259         for icon_dir in fdroidserver.update.get_all_icon_dirs('repo'):
260             if not os.path.exists(icon_dir):
261                 os.makedirs(icon_dir)
262
263         knownapks = fdroidserver.common.KnownApks()
264         apkList = ['../urzip.apk', '../org.dyndns.fules.ck_20.apk']
265
266         for apkName in apkList:
267             _, apk, cachechanged = fdroidserver.update.scan_apk({}, apkName, 'repo', knownapks, False)
268             # Don't care about the date added to the repo and relative apkName
269             del apk['added']
270             del apk['apkName']
271             # avoid AAPT application name bug
272             del apk['name']
273
274             savepath = os.path.join('metadata', 'apk', apk['packageName'] + '.yaml')
275             # Uncomment to save APK metadata
276             # with open(savepath, 'w') as f:
277             #     yaml.add_representer(fdroidserver.metadata.Build, _build_yaml_representer)
278             #     yaml.dump(apk, f, default_flow_style=False)
279
280             with open(savepath, 'r') as f:
281                 frompickle = yaml.load(f)
282             self.maxDiff = None
283             self.assertEqual(apk, frompickle)
284
285     def test_scan_invalid_apk(self):
286         os.chdir(os.path.join(localmodule, 'tests'))
287         if os.path.basename(os.getcwd()) != 'tests':
288             raise Exception('This test must be run in the "tests/" subdir')
289
290         config = dict()
291         fdroidserver.common.fill_config_defaults(config)
292         fdroidserver.common.config = config
293         fdroidserver.update.config = config
294         fdroidserver.update.options.delete_unknown = False
295
296         knownapks = fdroidserver.common.KnownApks()
297         apk = 'fake.ota.update_1234.zip'  # this is not an APK, scanning should fail
298         (skip, apk, cachechanged) = fdroidserver.update.scan_apk({}, apk, 'repo', knownapks, False)
299
300         self.assertTrue(skip)
301         self.assertIsNone(apk)
302         self.assertFalse(cachechanged)
303
304
305 if __name__ == "__main__":
306     parser = optparse.OptionParser()
307     parser.add_option("-v", "--verbose", action="store_true", default=False,
308                       help="Spew out even more information than normal")
309     (fdroidserver.common.options, args) = parser.parse_args(['--verbose'])
310
311     newSuite = unittest.TestSuite()
312     newSuite.addTest(unittest.makeSuite(UpdateTest))
313     unittest.main()