From 18db382c47641f094bb231b34e72ef8be00aa1a8 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sat, 30 Aug 2014 11:07:29 -0400 Subject: [PATCH] include test cases for python getsig replacement This includes the old getsig.java since that is the canonical implementation of that algorithm. fixes #5 https://gitlab.com/fdroid/fdroidserver/issues/5 --- MANIFEST.in | 6 ++ {fdroidserver => tests}/getsig/getsig.java | 0 {fdroidserver => tests}/getsig/make.sh | 0 {fdroidserver => tests}/getsig/run.sh | 0 tests/run-tests | 9 +++ tests/update.TestCase | 82 +++++++++++++++++++++ tests/urzip-badcert.apk | Bin 0 -> 9925 bytes tests/urzip-badsig.apk | Bin 0 -> 9923 bytes 8 files changed, 97 insertions(+) rename {fdroidserver => tests}/getsig/getsig.java (100%) rename {fdroidserver => tests}/getsig/make.sh (100%) rename {fdroidserver => tests}/getsig/run.sh (100%) create mode 100755 tests/update.TestCase create mode 100644 tests/urzip-badcert.apk create mode 100644 tests/urzip-badsig.apk diff --git a/MANIFEST.in b/MANIFEST.in index 468d24ee..2cf1078c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -24,8 +24,14 @@ include examples/config.py include examples/fdroid-icon.png include examples/makebs.config.py include examples/opensc-fdroid.cfg +include tests/getsig/run.sh +include tests/getsig/make.sh +include tests/getsig/getsig.java +include tests/getsig/getsig.class include tests/run-tests +include tests/update.TestCase include tests/urzip.apk +include tests/urzip-badsig.apk include wp-fdroid/AndroidManifest.xml include wp-fdroid/android-permissions.php include wp-fdroid/readme.txt diff --git a/fdroidserver/getsig/getsig.java b/tests/getsig/getsig.java similarity index 100% rename from fdroidserver/getsig/getsig.java rename to tests/getsig/getsig.java diff --git a/fdroidserver/getsig/make.sh b/tests/getsig/make.sh similarity index 100% rename from fdroidserver/getsig/make.sh rename to tests/getsig/make.sh diff --git a/fdroidserver/getsig/run.sh b/tests/getsig/run.sh similarity index 100% rename from fdroidserver/getsig/run.sh rename to tests/getsig/run.sh diff --git a/tests/run-tests b/tests/run-tests index 1f0e7709..c12c78a7 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -92,6 +92,15 @@ cd $WORKSPACE ./hooks/pre-commit +#------------------------------------------------------------------------------# +echo_header "test python getsig replacement" + +cd $WORKSPACE/tests/getsig +./make.sh +cd $WORKSPACE/tests +./update.TestCase + + #------------------------------------------------------------------------------# echo_header "create a source tarball and use that to build a repo" diff --git a/tests/update.TestCase b/tests/update.TestCase new file mode 100755 index 00000000..88b429fa --- /dev/null +++ b/tests/update.TestCase @@ -0,0 +1,82 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# http://www.drdobbs.com/testing/unit-testing-with-python/240165163 + +import inspect +import optparse +import os +import sys +import unittest + +localmodule = os.path.realpath(os.path.join( + os.path.dirname(inspect.getfile(inspect.currentframe())), + '..')) +print('localmodule: ' + localmodule) +if localmodule not in sys.path: + sys.path.insert(0,localmodule) + +import fdroidserver.common +import fdroidserver.update +from fdroidserver.common import FDroidPopen, SilentPopen + +class UpdateTest(unittest.TestCase): + '''fdroid update''' + + def javagetsig(self, apkfile): + getsig_dir = os.path.join(os.path.dirname(__file__), 'getsig') + if not os.path.exists(getsig_dir + "/getsig.class"): + logging.critical("getsig.class not found. To fix: cd '%s' && ./make.sh" % getsig_dir) + sys.exit(1) + p = FDroidPopen(['java', '-cp', os.path.join(os.path.dirname(__file__), 'getsig'), + 'getsig', os.path.join(os.getcwd(), apkfile)]) + sig = None + for line in p.output.splitlines(): + if line.startswith('Result:'): + sig = line[7:].strip() + break + if p.returncode == 0: + return sig + else: + return None + + def testGoodGetsig(self): + apkfile = os.path.join(os.path.dirname(__file__), 'urzip.apk') + sig = self.javagetsig(apkfile) + self.assertIsNotNone(sig, "sig is None") + pysig = fdroidserver.update.getsig(apkfile) + self.assertIsNotNone(pysig, "pysig is None") + self.assertEquals(sig, fdroidserver.update.getsig(apkfile), + "python sig not equal to java sig!") + self.assertEquals(len(sig), len(pysig), + "the length of the two sigs are different!") + try: + self.assertEquals(sig.decode('hex'), pysig.decode('hex'), + "the length of the two sigs are different!") + except TypeError as e: + print e + self.assertTrue(False, 'TypeError!') + + def testBadGetsig(self): + apkfile = os.path.join(os.path.dirname(__file__), 'urzip-badsig.apk') + sig = self.javagetsig(apkfile) + self.assertIsNone(sig, "sig should be None: " + str(sig)) + pysig = fdroidserver.update.getsig(apkfile) + self.assertIsNone(pysig, "python sig should be None: " + str(sig)) + + apkfile = os.path.join(os.path.dirname(__file__), 'urzip-badcert.apk') + sig = self.javagetsig(apkfile) + self.assertIsNone(sig, "sig should be None: " + str(sig)) + pysig = fdroidserver.update.getsig(apkfile) + self.assertIsNone(pysig, "python sig should be None: " + str(sig)) + + +if __name__ == "__main__": + parser = optparse.OptionParser() + parser.add_option("-v", "--verbose", action="store_true", default=False, + help="Spew out even more information than normal") + (fdroidserver.common.options, args) = parser.parse_args(['--verbose']) + + newSuite = unittest.TestSuite() + newSuite.addTest(unittest.makeSuite(UpdateTest)) + unittest.main() diff --git a/tests/urzip-badcert.apk b/tests/urzip-badcert.apk new file mode 100644 index 0000000000000000000000000000000000000000..cd7dd08fa3026d78bfe96b754531162d285a34eb GIT binary patch literal 9925 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