chiark / gitweb /
gradle file: use flavour specific versionCode/versionName, fall back to parsing line...
[fdroidserver.git] / tests / common.TestCase
1 #!/usr/bin/env python3
2
3 # http://www.drdobbs.com/testing/unit-testing-with-python/240165163
4
5 import inspect
6 import logging
7 import optparse
8 import os
9 import re
10 import shutil
11 import sys
12 import tempfile
13 import unittest
14 import textwrap
15 import yaml
16 from zipfile import ZipFile
17
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.index
26 import fdroidserver.signindex
27 import fdroidserver.common
28 import fdroidserver.metadata
29 from testcommon import TmpCwd
30 from fdroidserver.exception import FDroidException
31
32
33 class CommonTest(unittest.TestCase):
34     '''fdroidserver/common.py'''
35
36     def setUp(self):
37         logging.basicConfig(level=logging.DEBUG)
38         self.basedir = os.path.join(localmodule, 'tests')
39         self.tmpdir = os.path.abspath(os.path.join(self.basedir, '..', '.testfiles'))
40         if not os.path.exists(self.tmpdir):
41             os.makedirs(self.tmpdir)
42         os.chdir(self.basedir)
43
44     def test_assert_config_keystore(self):
45         with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
46             with self.assertRaises(FDroidException):
47                 fdroidserver.common.assert_config_keystore({})
48
49         with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
50             c = {'repo_keyalias': 'localhost',
51                  'keystore': 'keystore.jks',
52                  'keystorepass': '12345',
53                  'keypass': '12345'}
54             with open('keystore.jks', 'w'):
55                 pass
56             fdroidserver.common.assert_config_keystore(c)
57
58     def _set_build_tools(self):
59         build_tools = os.path.join(fdroidserver.common.config['sdk_path'], 'build-tools')
60         if os.path.exists(build_tools):
61             fdroidserver.common.config['build_tools'] = ''
62             for f in sorted(os.listdir(build_tools), reverse=True):
63                 versioned = os.path.join(build_tools, f)
64                 if os.path.isdir(versioned) \
65                         and os.path.isfile(os.path.join(versioned, 'aapt')):
66                     fdroidserver.common.config['build_tools'] = versioned
67                     break
68             return True
69         else:
70             print('no build-tools found: ' + build_tools)
71             return False
72
73     def _find_all(self):
74         tools = ['aapt', 'adb', 'zipalign']
75         if os.path.exists(os.path.join(os.getenv('ANDROID_HOME'), 'tools', 'android')):
76             tools.append('android')
77         for cmd in tools:
78             path = fdroidserver.common.find_sdk_tools_cmd(cmd)
79             if path is not None:
80                 self.assertTrue(os.path.exists(path))
81                 self.assertTrue(os.path.isfile(path))
82
83     def test_find_sdk_tools_cmd(self):
84         fdroidserver.common.config = dict()
85         # TODO add this once everything works without sdk_path set in config
86         # self._find_all()
87         sdk_path = os.getenv('ANDROID_HOME')
88         if os.path.exists(sdk_path):
89             fdroidserver.common.config['sdk_path'] = sdk_path
90             build_tools = os.path.join(sdk_path, 'build-tools')
91             if self._set_build_tools() or os.path.exists('/usr/bin/aapt'):
92                 self._find_all()
93             else:
94                 print('no build-tools found: ' + build_tools)
95
96     def test_find_java_root_path(self):
97         testdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir)
98         os.chdir(testdir)
99
100         all_pathlists = [
101             ([  # Debian
102                 '/usr/lib/jvm/java-1.5.0-gcj-5-amd64',
103                 '/usr/lib/jvm/java-8-openjdk-amd64',
104                 '/usr/lib/jvm/java-1.8.0-openjdk-amd64',
105             ], '/usr/lib/jvm/java-8-openjdk-amd64'),
106             ([  # OSX
107                 '/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk',
108                 '/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk',
109                 '/System/Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk',
110             ], '/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk'),
111         ]
112
113         for pathlist, choice in all_pathlists:
114             # strip leading / to make relative paths to test without root
115             pathlist = [p[1:] for p in pathlist]
116
117             # create test file used in common._add_java_paths_to_config()
118             for p in pathlist:
119                 if p.startswith('/System') or p.startswith('/Library'):
120                     basedir = os.path.join(p, 'Contents', 'Home', 'bin')
121                 else:
122                     basedir = os.path.join(p, 'bin')
123                 os.makedirs(basedir)
124                 open(os.path.join(basedir, 'javac'), 'w').close()
125
126             config = dict()
127             config['java_paths'] = dict()
128             fdroidserver.common._add_java_paths_to_config(pathlist, config)
129             self.assertEqual(config['java_paths']['8'], choice[1:])
130
131     def testIsApkDebuggable(self):
132         config = dict()
133         fdroidserver.common.fill_config_defaults(config)
134         fdroidserver.common.config = config
135         self._set_build_tools()
136         config['aapt'] = fdroidserver.common.find_sdk_tools_cmd('aapt')
137         # these are set debuggable
138         testfiles = []
139         testfiles.append(os.path.join(self.basedir, 'urzip.apk'))
140         testfiles.append(os.path.join(self.basedir, 'urzip-badsig.apk'))
141         testfiles.append(os.path.join(self.basedir, 'urzip-badcert.apk'))
142         for apkfile in testfiles:
143             debuggable = fdroidserver.common.isApkAndDebuggable(apkfile)
144             self.assertTrue(debuggable,
145                             "debuggable APK state was not properly parsed!")
146         # these are set NOT debuggable
147         testfiles = []
148         testfiles.append(os.path.join(self.basedir, 'urzip-release.apk'))
149         testfiles.append(os.path.join(self.basedir, 'urzip-release-unsigned.apk'))
150         for apkfile in testfiles:
151             debuggable = fdroidserver.common.isApkAndDebuggable(apkfile)
152             self.assertFalse(debuggable,
153                              "debuggable APK state was not properly parsed!")
154
155     def testPackageNameValidity(self):
156         for name in ["org.fdroid.fdroid",
157                      "org.f_droid.fdr0ID"]:
158             self.assertTrue(fdroidserver.common.is_valid_package_name(name),
159                             "{0} should be a valid package name".format(name))
160         for name in ["0rg.fdroid.fdroid",
161                      ".f_droid.fdr0ID",
162                      "org.fdroid/fdroid",
163                      "/org.fdroid.fdroid"]:
164             self.assertFalse(fdroidserver.common.is_valid_package_name(name),
165                              "{0} should not be a valid package name".format(name))
166
167     def test_prepare_sources(self):
168         testint = 99999999
169         teststr = 'FAKE_STR_FOR_TESTING'
170
171         testdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir)
172         shutil.copytree(os.path.join(self.basedir, 'source-files'),
173                         os.path.join(testdir, 'source-files'))
174
175         fdroidclient_testdir = os.path.join(testdir, 'source-files', 'fdroid', 'fdroidclient')
176
177         config = dict()
178         config['sdk_path'] = os.getenv('ANDROID_HOME')
179         config['ndk_paths'] = {'r10d': os.getenv('ANDROID_NDK_HOME')}
180         config['build_tools'] = 'FAKE_BUILD_TOOLS_VERSION'
181         fdroidserver.common.config = config
182         app = fdroidserver.metadata.App()
183         app.id = 'org.fdroid.froid'
184         build = fdroidserver.metadata.Build()
185         build.commit = 'master'
186         build.forceversion = True
187         build.forcevercode = True
188         build.gradle = ['yes']
189         build.target = 'android-' + str(testint)
190         build.versionName = teststr
191         build.versionCode = testint
192
193         class FakeVcs():
194             # no need to change to the correct commit here
195             def gotorevision(self, rev, refresh=True):
196                 pass
197
198             # no srclib info needed, but it could be added...
199             def getsrclib(self):
200                 return None
201
202         fdroidserver.common.prepare_source(FakeVcs(), app, build,
203                                            fdroidclient_testdir, fdroidclient_testdir, fdroidclient_testdir)
204
205         with open(os.path.join(fdroidclient_testdir, 'build.gradle'), 'r') as f:
206             filedata = f.read()
207         self.assertIsNotNone(re.search("\s+compileSdkVersion %s\s+" % testint, filedata))
208
209         with open(os.path.join(fdroidclient_testdir, 'AndroidManifest.xml')) as f:
210             filedata = f.read()
211         self.assertIsNone(re.search('android:debuggable', filedata))
212         self.assertIsNotNone(re.search('android:versionName="%s"' % build.versionName, filedata))
213         self.assertIsNotNone(re.search('android:versionCode="%s"' % build.versionCode, filedata))
214
215     def test_prepare_sources_refresh(self):
216         packageName = 'org.fdroid.ci.test.app'
217         testdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir)
218         print('testdir', testdir)
219         os.chdir(testdir)
220         os.mkdir('build')
221         os.mkdir('metadata')
222
223         # use a local copy if available to avoid hitting the network
224         tmprepo = os.path.join(self.basedir, 'tmp', 'importer')
225         if os.path.exists(tmprepo):
226             git_url = tmprepo
227         else:
228             git_url = 'https://gitlab.com/fdroid/ci-test-app.git'
229
230         metadata = dict()
231         metadata['Description'] = 'This is just a test app'
232         metadata['RepoType'] = 'git'
233         metadata['Repo'] = git_url
234         with open(os.path.join('metadata', packageName + '.yml'), 'w') as fp:
235             yaml.dump(metadata, fp)
236
237         gitrepo = os.path.join(testdir, 'build', packageName)
238         vcs0 = fdroidserver.common.getvcs('git', git_url, gitrepo)
239         vcs0.gotorevision('0.3', refresh=True)
240         vcs1 = fdroidserver.common.getvcs('git', git_url, gitrepo)
241         vcs1.gotorevision('0.3', refresh=False)
242
243     def test_fdroid_popen_stderr_redirect(self):
244         config = dict()
245         fdroidserver.common.fill_config_defaults(config)
246         fdroidserver.common.config = config
247
248         commands = ['sh', '-c', 'echo stdout message && echo stderr message 1>&2']
249
250         p = fdroidserver.common.FDroidPopen(commands)
251         self.assertEqual(p.output, 'stdout message\nstderr message\n')
252
253         p = fdroidserver.common.FDroidPopen(commands, stderr_to_stdout=False)
254         self.assertEqual(p.output, 'stdout message\n')
255
256     def test_signjar(self):
257         fdroidserver.common.config = None
258         config = fdroidserver.common.read_config(fdroidserver.common.options)
259         config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
260         fdroidserver.common.config = config
261         fdroidserver.signindex.config = config
262
263         sourcedir = os.path.join(self.basedir, 'signindex')
264         testsdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir)
265         for f in ('testy.jar', 'guardianproject.jar',):
266             sourcefile = os.path.join(sourcedir, f)
267             testfile = os.path.join(testsdir, f)
268             shutil.copy(sourcefile, testsdir)
269             fdroidserver.signindex.sign_jar(testfile)
270             # these should be resigned, and therefore different
271             self.assertNotEqual(open(sourcefile, 'rb').read(), open(testfile, 'rb').read())
272
273     def test_verify_apk_signature(self):
274         fdroidserver.common.config = None
275         config = fdroidserver.common.read_config(fdroidserver.common.options)
276         config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
277         fdroidserver.common.config = config
278
279         self.assertTrue(fdroidserver.common.verify_apk_signature('bad-unicode-πÇÇ现代通用字-български-عربي1.apk'))
280         self.assertFalse(fdroidserver.common.verify_apk_signature('org.bitbucket.tickytacky.mirrormirror_1.apk'))
281         self.assertFalse(fdroidserver.common.verify_apk_signature('org.bitbucket.tickytacky.mirrormirror_2.apk'))
282         self.assertFalse(fdroidserver.common.verify_apk_signature('org.bitbucket.tickytacky.mirrormirror_3.apk'))
283         self.assertFalse(fdroidserver.common.verify_apk_signature('org.bitbucket.tickytacky.mirrormirror_4.apk'))
284         self.assertTrue(fdroidserver.common.verify_apk_signature('org.dyndns.fules.ck_20.apk'))
285         self.assertTrue(fdroidserver.common.verify_apk_signature('urzip.apk'))
286         self.assertFalse(fdroidserver.common.verify_apk_signature('urzip-badcert.apk'))
287         self.assertFalse(fdroidserver.common.verify_apk_signature('urzip-badsig.apk'))
288         self.assertTrue(fdroidserver.common.verify_apk_signature('urzip-release.apk'))
289         self.assertFalse(fdroidserver.common.verify_apk_signature('urzip-release-unsigned.apk'))
290
291     def test_verify_old_apk_signature(self):
292         fdroidserver.common.config = None
293         config = fdroidserver.common.read_config(fdroidserver.common.options)
294         config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
295         fdroidserver.common.config = config
296
297         self.assertTrue(fdroidserver.common.verify_old_apk_signature('bad-unicode-πÇÇ现代通用字-български-عربي1.apk'))
298         self.assertTrue(fdroidserver.common.verify_old_apk_signature('org.bitbucket.tickytacky.mirrormirror_1.apk'))
299         self.assertTrue(fdroidserver.common.verify_old_apk_signature('org.bitbucket.tickytacky.mirrormirror_2.apk'))
300         self.assertTrue(fdroidserver.common.verify_old_apk_signature('org.bitbucket.tickytacky.mirrormirror_3.apk'))
301         self.assertTrue(fdroidserver.common.verify_old_apk_signature('org.bitbucket.tickytacky.mirrormirror_4.apk'))
302         self.assertTrue(fdroidserver.common.verify_old_apk_signature('org.dyndns.fules.ck_20.apk'))
303         self.assertTrue(fdroidserver.common.verify_old_apk_signature('urzip.apk'))
304         self.assertFalse(fdroidserver.common.verify_old_apk_signature('urzip-badcert.apk'))
305         self.assertFalse(fdroidserver.common.verify_old_apk_signature('urzip-badsig.apk'))
306         self.assertTrue(fdroidserver.common.verify_old_apk_signature('urzip-release.apk'))
307         self.assertFalse(fdroidserver.common.verify_old_apk_signature('urzip-release-unsigned.apk'))
308
309     def test_verify_jar_signature_succeeds(self):
310         fdroidserver.common.config = None
311         config = fdroidserver.common.read_config(fdroidserver.common.options)
312         config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
313         fdroidserver.common.config = config
314         source_dir = os.path.join(self.basedir, 'signindex')
315         for f in ('testy.jar', 'guardianproject.jar'):
316             testfile = os.path.join(source_dir, f)
317             fdroidserver.common.verify_jar_signature(testfile)
318
319     def test_verify_jar_signature_fails(self):
320         fdroidserver.common.config = None
321         config = fdroidserver.common.read_config(fdroidserver.common.options)
322         config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
323         fdroidserver.common.config = config
324         source_dir = os.path.join(self.basedir, 'signindex')
325         testfile = os.path.join(source_dir, 'unsigned.jar')
326         with self.assertRaises(fdroidserver.index.VerificationException):
327             fdroidserver.common.verify_jar_signature(testfile)
328
329     def test_verify_apks(self):
330         fdroidserver.common.config = None
331         config = fdroidserver.common.read_config(fdroidserver.common.options)
332         config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
333         fdroidserver.common.config = config
334
335         sourceapk = os.path.join(self.basedir, 'urzip.apk')
336
337         testdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir)
338         print('testdir', testdir)
339
340         copyapk = os.path.join(testdir, 'urzip-copy.apk')
341         shutil.copy(sourceapk, copyapk)
342         self.assertTrue(fdroidserver.common.verify_apk_signature(copyapk))
343         self.assertIsNone(fdroidserver.common.verify_apks(sourceapk, copyapk, self.tmpdir))
344
345         unsignedapk = os.path.join(testdir, 'urzip-unsigned.apk')
346         with ZipFile(sourceapk, 'r') as apk:
347             with ZipFile(unsignedapk, 'w') as testapk:
348                 for info in apk.infolist():
349                     if not info.filename.startswith('META-INF/'):
350                         testapk.writestr(info, apk.read(info.filename))
351         self.assertIsNone(fdroidserver.common.verify_apks(sourceapk, unsignedapk, self.tmpdir))
352
353         twosigapk = os.path.join(testdir, 'urzip-twosig.apk')
354         otherapk = ZipFile(os.path.join(self.basedir, 'urzip-release.apk'), 'r')
355         with ZipFile(sourceapk, 'r') as apk:
356             with ZipFile(twosigapk, 'w') as testapk:
357                 for info in apk.infolist():
358                     testapk.writestr(info, apk.read(info.filename))
359                     if info.filename.startswith('META-INF/'):
360                         testapk.writestr(info, otherapk.read(info.filename))
361         otherapk.close()
362         self.assertFalse(fdroidserver.common.verify_apk_signature(twosigapk))
363         self.assertIsNone(fdroidserver.common.verify_apks(sourceapk, twosigapk, self.tmpdir))
364
365     def test_write_to_config(self):
366         with tempfile.TemporaryDirectory() as tmpPath:
367             cfgPath = os.path.join(tmpPath, 'config.py')
368             with open(cfgPath, 'w', encoding='utf-8') as f:
369                 f.write(textwrap.dedent("""\
370                     # abc
371                     # test = 'example value'
372                     default_me= '%%%'
373
374                     # comment
375                     do_not_touch = "good value"
376                     default_me="!!!"
377
378                     key="123"    # inline"""))
379
380             cfg = {'key': '111', 'default_me_orig': 'orig'}
381             fdroidserver.common.write_to_config(cfg, 'key', config_file=cfgPath)
382             fdroidserver.common.write_to_config(cfg, 'default_me', config_file=cfgPath)
383             fdroidserver.common.write_to_config(cfg, 'test', value='test value', config_file=cfgPath)
384             fdroidserver.common.write_to_config(cfg, 'new_key', value='new', config_file=cfgPath)
385
386             with open(cfgPath, 'r', encoding='utf-8') as f:
387                 self.assertEqual(f.read(), textwrap.dedent("""\
388                     # abc
389                     test = 'test value'
390                     default_me = 'orig'
391
392                     # comment
393                     do_not_touch = "good value"
394
395                     key = "111"    # inline
396
397                     new_key = "new"
398                     """))
399
400     def test_write_to_config_when_empty(self):
401         with tempfile.TemporaryDirectory() as tmpPath:
402             cfgPath = os.path.join(tmpPath, 'config.py')
403             with open(cfgPath, 'w') as f:
404                 pass
405             fdroidserver.common.write_to_config({}, 'key', 'val', cfgPath)
406             with open(cfgPath, 'r', encoding='utf-8') as f:
407                 self.assertEqual(f.read(), textwrap.dedent("""\
408
409                 key = "val"
410                 """))
411
412     def test_apk_name_regex(self):
413         good = [
414             'urzipπÇÇπÇÇ现代汉语通用字българскиعربي1234ö_-123456.apk',
415             'urzipπÇÇπÇÇ现代汉语通用字българскиعربي1234ö_123456_abcdef0.apk',
416             'urzip_-123456.apk',
417             'a0_0.apk',
418             'Z0_0.apk',
419             'a0_0_abcdef0.apk',
420             'a_a_a_a_0_abcdef0.apk',
421             'a_____0.apk',
422             'a_____123456_abcdef0.apk',
423             'org.fdroid.fdroid_123456.apk',
424             # valid, but "_99999" is part of packageName rather than versionCode
425             'org.fdroid.fdroid_99999_123456.apk',
426             # should be valid, but I can't figure out the regex since \w includes digits
427             # 'πÇÇπÇÇ现代汉语通用字българскиعربي1234ö_0_123bafd.apk',
428         ]
429         for name in good:
430             m = fdroidserver.common.APK_NAME_REGEX.match(name)
431             self.assertIsNotNone(m)
432             self.assertIn(m.group(2), ('-123456', '0', '123456'))
433             self.assertIn(m.group(3), ('abcdef0', None))
434
435         bad = [
436             'urzipπÇÇπÇÇ现代汉语通用字българскиعربي1234ö_123456_abcdefg.apk',
437             'urzip-_-198274.apk',
438             'urzip-_0_123bafd.apk',
439             'no spaces allowed_123.apk',
440             '0_0.apk',
441             '0_0_abcdef0.apk',
442         ]
443         for name in bad:
444             self.assertIsNone(fdroidserver.common.APK_NAME_REGEX.match(name))
445
446     def test_standard_file_name_regex(self):
447         good = [
448             'urzipπÇÇπÇÇ现代汉语通用字българскиعربي1234ö_-123456.mp3',
449             'urzipπÇÇπÇÇ现代汉语通用字българскиعربي1234ö_123456.mov',
450             'Document_-123456.pdf',
451             'WTF_0.MOV',
452             'Z0_0.ebk',
453             'a_a_a_a_0.txt',
454             'org.fdroid.fdroid.privileged.ota_123456.zip',
455             'πÇÇπÇÇ现代汉语通用字българскиعربي1234ö_0.jpeg',
456             'a_____0.PNG',
457             # valid, but "_99999" is part of packageName rather than versionCode
458             'a_____99999_123456.zip',
459             'org.fdroid.fdroid_99999_123456.zip',
460         ]
461         for name in good:
462             m = fdroidserver.common.STANDARD_FILE_NAME_REGEX.match(name)
463             self.assertIsNotNone(m)
464             self.assertIn(m.group(2), ('-123456', '0', '123456'))
465
466         bad = [
467             'urzipπÇÇπÇÇ现代汉语通用字българскиعربي1234ö_abcdefg.JPEG',
468             'urzip-_-198274.zip',
469             'urzip-_123bafd.pdf',
470             'no spaces allowed_123.foobar',
471             'a_____0.',
472         ]
473         for name in bad:
474             self.assertIsNone(fdroidserver.common.STANDARD_FILE_NAME_REGEX.match(name))
475
476     def test_apk_signer_fingerprint(self):
477
478         # fingerprints fetched with: keytool -printcert -file ____.RSA
479         testapks = (('repo/obb.main.oldversion_1444412523.apk',
480                      '818e469465f96b704e27be2fee4c63ab9f83ddf30e7a34c7371a4728d83b0bc1'),
481                     ('repo/obb.main.twoversions_1101613.apk',
482                      '32a23624c201b949f085996ba5ed53d40f703aca4989476949cae891022e0ed6'),
483                     ('repo/obb.main.twoversions_1101617.apk',
484                      '32a23624c201b949f085996ba5ed53d40f703aca4989476949cae891022e0ed6'))
485
486         for apkfile, keytoolcertfingerprint in testapks:
487             self.assertEqual(keytoolcertfingerprint,
488                              fdroidserver.common.apk_signer_fingerprint(apkfile))
489
490     def test_apk_signer_fingerprint_short(self):
491
492         # fingerprints fetched with: keytool -printcert -file ____.RSA
493         testapks = (('repo/obb.main.oldversion_1444412523.apk', '818e469'),
494                     ('repo/obb.main.twoversions_1101613.apk', '32a2362'),
495                     ('repo/obb.main.twoversions_1101617.apk', '32a2362'))
496
497         for apkfile, keytoolcertfingerprint in testapks:
498             self.assertEqual(keytoolcertfingerprint,
499                              fdroidserver.common.apk_signer_fingerprint_short(apkfile))
500
501     def test_sign_apk(self):
502         fdroidserver.common.config = None
503         config = fdroidserver.common.read_config(fdroidserver.common.options)
504         config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
505         config['keyalias'] = 'sova'
506         config['keystorepass'] = 'r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI='
507         config['keypass'] = 'r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI='
508         config['keystore'] = os.path.join(self.basedir, 'keystore.jks')
509         fdroidserver.common.config = config
510         fdroidserver.signindex.config = config
511
512         testdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir)
513         unsigned = os.path.join(testdir, 'urzip-release-unsigned.apk')
514         signed = os.path.join(testdir, 'urzip-release.apk')
515
516         self.assertFalse(fdroidserver.common.verify_apk_signature(unsigned))
517
518         shutil.copy(os.path.join(self.basedir, 'urzip-release-unsigned.apk'), testdir)
519         fdroidserver.common.sign_apk(unsigned, signed, config['keyalias'])
520         self.assertTrue(os.path.isfile(signed))
521         self.assertFalse(os.path.isfile(unsigned))
522         self.assertTrue(fdroidserver.common.verify_apk_signature(signed))
523
524     def test_get_api_id_aapt(self):
525
526         config = dict()
527         fdroidserver.common.fill_config_defaults(config)
528         fdroidserver.common.config = config
529         self._set_build_tools()
530         config['aapt'] = fdroidserver.common.find_sdk_tools_cmd('aapt')
531
532         appid, vercode, vername = fdroidserver.common.get_apk_id_aapt('repo/obb.main.twoversions_1101613.apk')
533         self.assertEqual('obb.main.twoversions', appid)
534         self.assertEqual('1101613', vercode)
535         self.assertEqual('0.1', vername)
536
537         with self.assertRaises(FDroidException):
538             fdroidserver.common.get_apk_id_aapt('nope')
539
540     def test_get_minSdkVersion_aapt(self):
541
542         config = dict()
543         fdroidserver.common.fill_config_defaults(config)
544         fdroidserver.common.config = config
545         self._set_build_tools()
546         config['aapt'] = fdroidserver.common.find_sdk_tools_cmd('aapt')
547
548         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('bad-unicode-πÇÇ现代通用字-български-عربي1.apk')
549         self.assertEqual(4, minSdkVersion)
550         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('org.bitbucket.tickytacky.mirrormirror_1.apk')
551         self.assertEqual(14, minSdkVersion)
552         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('org.bitbucket.tickytacky.mirrormirror_2.apk')
553         self.assertEqual(14, minSdkVersion)
554         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('org.bitbucket.tickytacky.mirrormirror_3.apk')
555         self.assertEqual(14, minSdkVersion)
556         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('org.bitbucket.tickytacky.mirrormirror_4.apk')
557         self.assertEqual(14, minSdkVersion)
558         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('org.dyndns.fules.ck_20.apk')
559         self.assertEqual(7, minSdkVersion)
560         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('urzip.apk')
561         self.assertEqual(4, minSdkVersion)
562         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('urzip-badcert.apk')
563         self.assertEqual(4, minSdkVersion)
564         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('urzip-badsig.apk')
565         self.assertEqual(4, minSdkVersion)
566         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('urzip-release.apk')
567         self.assertEqual(4, minSdkVersion)
568         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('urzip-release-unsigned.apk')
569         self.assertEqual(4, minSdkVersion)
570         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/com.politedroid_3.apk')
571         self.assertEqual(3, minSdkVersion)
572         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/com.politedroid_4.apk')
573         self.assertEqual(3, minSdkVersion)
574         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/com.politedroid_5.apk')
575         self.assertEqual(3, minSdkVersion)
576         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/com.politedroid_6.apk')
577         self.assertEqual(14, minSdkVersion)
578         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.main.oldversion_1444412523.apk')
579         self.assertEqual(4, minSdkVersion)
580         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.mainpatch.current_1619_another-release-key.apk')
581         self.assertEqual(4, minSdkVersion)
582         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.mainpatch.current_1619.apk')
583         self.assertEqual(4, minSdkVersion)
584         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.main.twoversions_1101613.apk')
585         self.assertEqual(4, minSdkVersion)
586         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.main.twoversions_1101615.apk')
587         self.assertEqual(4, minSdkVersion)
588         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.main.twoversions_1101617.apk')
589         self.assertEqual(4, minSdkVersion)
590         minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/urzip-; Рахма́нинов, [rɐxˈmanʲɪnəf] سيرجي_رخمانينوف 谢尔盖·.apk')
591
592         with self.assertRaises(FDroidException):
593             fdroidserver.common.get_minSdkVersion_aapt('nope')
594
595     def test_apk_release_name(self):
596         appid, vercode, sigfp = fdroidserver.common.apk_parse_release_filename('com.serwylo.lexica_905.apk')
597         self.assertEqual(appid, 'com.serwylo.lexica')
598         self.assertEqual(vercode, '905')
599         self.assertEqual(sigfp, None)
600
601         appid, vercode, sigfp = fdroidserver.common.apk_parse_release_filename('com.serwylo.lexica_905_c82e0f6.apk')
602         self.assertEqual(appid, 'com.serwylo.lexica')
603         self.assertEqual(vercode, '905')
604         self.assertEqual(sigfp, 'c82e0f6')
605
606         appid, vercode, sigfp = fdroidserver.common.apk_parse_release_filename('beverly_hills-90210.apk')
607         self.assertEqual(appid, None)
608         self.assertEqual(vercode, None)
609         self.assertEqual(sigfp, None)
610
611     def test_metadata_find_developer_signature(self):
612         sig = fdroidserver.common.metadata_find_developer_signature('org.smssecure.smssecure')
613         self.assertEqual('b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868', sig)
614
615     def test_parse_androidmanifests(self):
616         source_files_dir = os.path.join(os.path.dirname(__file__), 'source-files')
617         app = fdroidserver.metadata.App()
618         app.id = 'org.fdroid.fdroid'
619         paths = [
620             os.path.join(source_files_dir, 'fdroid', 'fdroidclient', 'AndroidManifest.xml'),
621             os.path.join(source_files_dir, 'fdroid', 'fdroidclient', 'build.gradle'),
622         ]
623         for path in paths:
624             self.assertTrue(os.path.isfile(path))
625         self.assertEqual(('0.94-test', '940', 'org.fdroid.fdroid'),
626                          fdroidserver.common.parse_androidmanifests(paths, app))
627
628     def test_parse_androidmanifests_with_flavor(self):
629         source_files_dir = os.path.join(os.path.dirname(__file__), 'source-files')
630
631         app = fdroidserver.metadata.App()
632         build = fdroidserver.metadata.Build()
633         build.gradle = ['devVersion']
634         app.builds = [build]
635         app.id = 'org.fdroid.fdroid.dev'
636         paths = [
637             os.path.join(source_files_dir, 'fdroid', 'fdroidclient', 'AndroidManifest.xml'),
638             os.path.join(source_files_dir, 'fdroid', 'fdroidclient', 'build.gradle'),
639         ]
640         for path in paths:
641             self.assertTrue(os.path.isfile(path))
642         self.assertEqual(('0.95-dev', '949', 'org.fdroid.fdroid.dev'),
643                          fdroidserver.common.parse_androidmanifests(paths, app))
644
645         app = fdroidserver.metadata.App()
646         build = fdroidserver.metadata.Build()
647         build.gradle = ['free']
648         app.builds = [build]
649         app.id = 'eu.siacs.conversations'
650         paths = [
651             os.path.join(source_files_dir, 'eu.siacs.conversations', 'build.gradle'),
652         ]
653         for path in paths:
654             self.assertTrue(os.path.isfile(path))
655         self.assertEqual(('1.23.1', '245', 'eu.siacs.conversations'),
656                          fdroidserver.common.parse_androidmanifests(paths, app))
657
658
659 if __name__ == "__main__":
660     parser = optparse.OptionParser()
661     parser.add_option("-v", "--verbose", action="store_true", default=False,
662                       help="Spew out even more information than normal")
663     (fdroidserver.common.options, args) = parser.parse_args(['--verbose'])
664
665     newSuite = unittest.TestSuite()
666     newSuite.addTest(unittest.makeSuite(CommonTest))
667     unittest.main(failfast=False)