Python unit tests (#13)
* Modified build to allow Python unit tests to run * Added extra dependencies and build recipes for the unit tests
This commit is contained in:
parent
589e0fbb95
commit
9eeeefa1d2
19 changed files with 546 additions and 41 deletions
|
@ -36,7 +36,7 @@ version = 0.1
|
||||||
|
|
||||||
# (list) Application requirements
|
# (list) Application requirements
|
||||||
# comma seperated e.g. requirements = sqlite3,kivy
|
# comma seperated e.g. requirements = sqlite3,kivy
|
||||||
requirements = openssl, sqlite3, hostpython2, pycrypto==2.6.1, android, pyjnius, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pycrypto==2.6.1, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, txrequests==0.9.5, seccure==0.3.1.3, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbry
|
requirements = openssl, sqlite3, hostpython2, pycrypto==2.6.1, android, pyjnius, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pycrypto==2.6.1, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, txrequests==0.9.5, seccure==0.3.1.3, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, git+https://github.com/lbryio/lbryschema.git@v0.0.12rc1#egg=lbryschema, git+https://github.com/lbryio/lbryum.git@v3.1.9rc2#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.0.3, funcsigs, mock, pbr, unqlite
|
||||||
|
|
||||||
# (str) Custom source folders for requirements
|
# (str) Custom source folders for requirements
|
||||||
# Sets custom source for any requirements with recipes
|
# Sets custom source for any requirements with recipes
|
||||||
|
|
|
@ -36,7 +36,7 @@ version = 0.1
|
||||||
|
|
||||||
# (list) Application requirements
|
# (list) Application requirements
|
||||||
# comma seperated e.g. requirements = sqlite3,kivy
|
# comma seperated e.g. requirements = sqlite3,kivy
|
||||||
requirements = openssl, sqlite3, hostpython2, pycrypto==2.6.1, android, pyjnius, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pycrypto==2.6.1, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, txrequests==0.9.5, seccure==0.3.1.3, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbry
|
requirements = openssl, sqlite3, hostpython2, pycrypto==2.6.1, android, pyjnius, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pycrypto==2.6.1, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, txrequests==0.9.5, seccure==0.3.1.3, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, git+https://github.com/lbryio/lbryschema.git@v0.0.12rc1#egg=lbryschema, git+https://github.com/lbryio/lbryum.git@v3.1.9rc2#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.0.3, funcsigs, mock, pbr, unqlite
|
||||||
|
|
||||||
# (str) Custom source folders for requirements
|
# (str) Custom source folders for requirements
|
||||||
# Sets custom source for any requirements with recipes
|
# Sets custom source for any requirements with recipes
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
|
||||||
# unit test
|
# unit test
|
||||||
unittest/*
|
#unittest/*
|
||||||
|
|
||||||
# python config
|
# python config
|
||||||
config/makesetup
|
config/makesetup
|
||||||
|
|
|
@ -118,6 +118,8 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<service android:name="{{ args.package }}.LbrynetService"
|
<service android:name="{{ args.package }}.LbrynetService"
|
||||||
android:process=":service_lbrynet" />
|
android:process=":service_lbrynet" />
|
||||||
|
<service android:name="{{ args.package }}.LbrynetTestRunnerService"
|
||||||
|
android:process=":service_lbrynet_testrunner" />
|
||||||
|
|
||||||
{% if args.billing_pubkey %}
|
{% if args.billing_pubkey %}
|
||||||
<service android:name="org.kivy.android.billing.BillingReceiver"
|
<service android:name="org.kivy.android.billing.BillingReceiver"
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
/>
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btn_start_stop"
|
android:id="@+id/btn_start_stop"
|
||||||
android:layout_width="120dp"
|
android:layout_width="120dp"
|
||||||
|
@ -42,4 +41,47 @@
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:text="@string/start"/>
|
android:text="@string/start"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:weightSum="3">
|
||||||
|
<TextView
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:text="@string/unit_tests"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_run_tests"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="right|center_vertical"
|
||||||
|
android:text="@string/run_tests"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:padding="6dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/test_runner_output"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="12sp"
|
||||||
|
/>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -10,4 +10,7 @@
|
||||||
<string name="stopped">Stopped</string>
|
<string name="stopped">Stopped</string>
|
||||||
<string name="start">START</string>
|
<string name="start">START</string>
|
||||||
<string name="stop">STOP</string>
|
<string name="stop">STOP</string>
|
||||||
|
|
||||||
|
<string name="unit_tests">Unit tests</string>
|
||||||
|
<string name="run_tests">RUN</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
36
recipes/cffi/__init__.py
Normal file
36
recipes/cffi/__init__.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from pythonforandroid.recipe import CompiledComponentsPythonRecipe, Recipe
|
||||||
|
from os.path import join
|
||||||
|
|
||||||
|
|
||||||
|
class CffiRecipe(CompiledComponentsPythonRecipe):
|
||||||
|
name = 'cffi'
|
||||||
|
version = '1.11.0'
|
||||||
|
#url = 'https://pypi.python.org/packages/source/c/cffi/cffi-{version}.tar.gz'
|
||||||
|
url = 'https://pypi.python.org/packages/4e/32/4070bdf32812c89eb635c80880a5caa2e0189aa7999994c265577e5154f3/cffi-{version}.tar.gz'
|
||||||
|
|
||||||
|
depends = [('python2', 'python3crystax'), 'setuptools', 'pycparser', 'libffi']
|
||||||
|
|
||||||
|
patches = ['disable-pkg-config.patch']
|
||||||
|
|
||||||
|
# call_hostpython_via_targetpython = False
|
||||||
|
install_in_hostpython = True
|
||||||
|
|
||||||
|
def get_recipe_env(self, arch=None):
|
||||||
|
env = super(CffiRecipe, self).get_recipe_env(arch)
|
||||||
|
libffi = self.get_recipe('libffi', self.ctx)
|
||||||
|
includes = libffi.get_include_dirs(arch)
|
||||||
|
env['CFLAGS'] = ' -I'.join([env.get('CFLAGS', '')] + includes)
|
||||||
|
env['LDFLAGS'] = (env.get('CFLAGS', '') + ' -L' +
|
||||||
|
self.ctx.get_libs_dir(arch.arch))
|
||||||
|
env['LDSHARED'] = env['CC'] + ' -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions'
|
||||||
|
env['PYTHONPATH'] = ':'.join([self.ctx.get_site_packages_dir(), env['BUILDLIB_PATH']])
|
||||||
|
|
||||||
|
target_python = Recipe.get_recipe('python2', self.ctx).get_build_dir(arch.arch)
|
||||||
|
env['PYTHON_ROOT'] = join(target_python, 'python-install')
|
||||||
|
env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/include/python2.7'
|
||||||
|
env['LDFLAGS'] += ' -L' + env['PYTHON_ROOT'] + '/lib' + ' -lpython2.7'
|
||||||
|
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
recipe = CffiRecipe()
|
29
recipes/cffi/disable-pkg-config.patch
Normal file
29
recipes/cffi/disable-pkg-config.patch
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
diff -Naur cffi-1.4.2/setup.py b/setup.py
|
||||||
|
--- cffi-1.4.2/setup.py 2015-12-21 12:09:47.000000000 -0600
|
||||||
|
+++ b/setup.py 2015-12-23 10:20:40.590622524 -0600
|
||||||
|
@@ -5,8 +5,7 @@
|
||||||
|
|
||||||
|
sources = ['c/_cffi_backend.c']
|
||||||
|
libraries = ['ffi']
|
||||||
|
-include_dirs = ['/usr/include/ffi',
|
||||||
|
- '/usr/include/libffi'] # may be changed by pkg-config
|
||||||
|
+include_dirs = []
|
||||||
|
define_macros = []
|
||||||
|
library_dirs = []
|
||||||
|
extra_compile_args = []
|
||||||
|
@@ -67,14 +66,7 @@
|
||||||
|
sys.stderr.write("The above error message can be safely ignored\n")
|
||||||
|
|
||||||
|
def use_pkg_config():
|
||||||
|
- if sys.platform == 'darwin' and os.path.exists('/usr/local/bin/brew'):
|
||||||
|
- use_homebrew_for_libffi()
|
||||||
|
-
|
||||||
|
- _ask_pkg_config(include_dirs, '--cflags-only-I', '-I', sysroot=True)
|
||||||
|
- _ask_pkg_config(extra_compile_args, '--cflags-only-other')
|
||||||
|
- _ask_pkg_config(library_dirs, '--libs-only-L', '-L', sysroot=True)
|
||||||
|
- _ask_pkg_config(extra_link_args, '--libs-only-other')
|
||||||
|
- _ask_pkg_config(libraries, '--libs-only-l', '-l')
|
||||||
|
+ pass
|
||||||
|
|
||||||
|
def use_homebrew_for_libffi():
|
||||||
|
# We can build by setting:
|
30
recipes/cryptography/__init__.py
Normal file
30
recipes/cryptography/__init__.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
from pythonforandroid.recipe import CompiledComponentsPythonRecipe, Recipe
|
||||||
|
from os.path import dirname, join
|
||||||
|
|
||||||
|
class CryptographyRecipe(CompiledComponentsPythonRecipe):
|
||||||
|
name = 'cryptography'
|
||||||
|
version = '2.0.3'
|
||||||
|
url = 'https://github.com/pyca/cryptography/archive/{version}.tar.gz'
|
||||||
|
depends = [('python2', 'python3crystax'), 'openssl', 'idna', 'pyasn1', 'six', 'setuptools', 'enum34', 'ipaddress', 'cffi']
|
||||||
|
call_hostpython_via_targetpython = False
|
||||||
|
|
||||||
|
def get_recipe_env(self, arch):
|
||||||
|
env = super(CryptographyRecipe, self).get_recipe_env(arch)
|
||||||
|
r = self.get_recipe('openssl', self.ctx)
|
||||||
|
openssl_dir = r.get_build_dir(arch.arch)
|
||||||
|
target_python = Recipe.get_recipe('python2', self.ctx).get_build_dir(arch.arch)
|
||||||
|
env['PYTHON_ROOT'] = self.ctx.get_python_install_dir()
|
||||||
|
env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/include/python2.7' + \
|
||||||
|
' -I' + join(openssl_dir, 'include') + \
|
||||||
|
' -I' + join(target_python, 'Modules/_ctypes/libffi_arm_wince')
|
||||||
|
# Set linker to use the correct gcc
|
||||||
|
env['LDSHARED'] = env['CC'] + ' -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions'
|
||||||
|
env['LDFLAGS'] += ' -L' + env['PYTHON_ROOT'] + '/lib' + \
|
||||||
|
' -L' + openssl_dir + \
|
||||||
|
' -lpython2.7' + \
|
||||||
|
' -lssl' + r.version + \
|
||||||
|
' -lcrypto' + r.version
|
||||||
|
|
||||||
|
return env
|
||||||
|
|
||||||
|
recipe = CryptographyRecipe()
|
14
recipes/ipaddress/__init__.py
Normal file
14
recipes/ipaddress/__init__.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from pythonforandroid.recipe import PythonRecipe
|
||||||
|
|
||||||
|
|
||||||
|
class IpaddressRecipe(PythonRecipe):
|
||||||
|
name = 'ipaddress'
|
||||||
|
version = '1.0.16'
|
||||||
|
url = 'https://pypi.python.org/packages/source/i/ipaddress/ipaddress-{version}.tar.gz'
|
||||||
|
|
||||||
|
depends = ['python2']
|
||||||
|
|
||||||
|
call_hostpython_via_targetpython = False
|
||||||
|
|
||||||
|
|
||||||
|
recipe = IpaddressRecipe()
|
11
recipes/pyasn1/__init__.py
Normal file
11
recipes/pyasn1/__init__.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
from pythonforandroid.toolchain import PythonRecipe
|
||||||
|
|
||||||
|
|
||||||
|
class PyASN1Recipe(PythonRecipe):
|
||||||
|
version = '0.1.8'
|
||||||
|
url = 'https://pypi.python.org/packages/source/p/pyasn1/pyasn1-{version}.tar.gz'
|
||||||
|
depends = ['python2']
|
||||||
|
call_hostpython_via_targetpython = False
|
||||||
|
|
||||||
|
recipe = PyASN1Recipe()
|
32
recipes/unqlite/__init__.py
Normal file
32
recipes/unqlite/__init__.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import glob
|
||||||
|
from pythonforandroid.toolchain import (
|
||||||
|
CythonRecipe,
|
||||||
|
Recipe,
|
||||||
|
current_directory,
|
||||||
|
info,
|
||||||
|
shprint,
|
||||||
|
)
|
||||||
|
from os.path import join
|
||||||
|
import sh
|
||||||
|
|
||||||
|
|
||||||
|
class UnqliteRecipe(CythonRecipe):
|
||||||
|
version = '0.6.0'
|
||||||
|
url = 'https://pypi.python.org/packages/cb/4e/e1f64a3d0f6462167805940b4c72f47bafc1129e363fc4c0f79a1cdc5dd1/unqlite-{version}.tar.gz'
|
||||||
|
depends = ['python2', 'setuptools']
|
||||||
|
call_hostpython_via_targetpython = False
|
||||||
|
|
||||||
|
patches = ['setup.patch']
|
||||||
|
|
||||||
|
def get_recipe_env(self, arch):
|
||||||
|
env = super(UnqliteRecipe, self).get_recipe_env(arch)
|
||||||
|
|
||||||
|
target_python = Recipe.get_recipe('python2', self.ctx).get_build_dir(arch.arch)
|
||||||
|
env['PYTHON_ROOT'] = join(target_python, 'python-install')
|
||||||
|
env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/include/python2.7'
|
||||||
|
env['LDFLAGS'] += ' -L' + env['PYTHON_ROOT'] + '/lib' + ' -lpython2.7'
|
||||||
|
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
recipe = UnqliteRecipe()
|
26
recipes/unqlite/setup.patch
Normal file
26
recipes/unqlite/setup.patch
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
--- a/setup.py 2016-08-22 19:45:52.000000000 +0100
|
||||||
|
+++ b/setup.py 2017-09-16 01:35:36.675202502 +0100
|
||||||
|
@@ -1,11 +1,7 @@
|
||||||
|
import glob
|
||||||
|
-from distutils.core import setup, Extension
|
||||||
|
-
|
||||||
|
-try:
|
||||||
|
- from Cython.Build import cythonize
|
||||||
|
-except ImportError:
|
||||||
|
- raise RuntimeError('Cython must be installed to build unqlite-python.')
|
||||||
|
-
|
||||||
|
+from setuptools import setup
|
||||||
|
+from setuptools.extension import Extension
|
||||||
|
+from setuptools.command.build_ext import build_ext
|
||||||
|
|
||||||
|
python_source = 'unqlite.pyx'
|
||||||
|
library_source = glob.glob('src/*.c')
|
||||||
|
@@ -45,5 +41,7 @@
|
||||||
|
'Topic :: Database :: Database Engines/Servers',
|
||||||
|
'Topic :: Software Development :: Embedded Systems',
|
||||||
|
'Topic :: Software Development :: Libraries :: Python Modules'],
|
||||||
|
- ext_modules=cythonize(unqlite_extension)
|
||||||
|
+ cmdclass = {'build_ext': build_ext},
|
||||||
|
+ ext_modules=[unqlite_extension]
|
||||||
|
)
|
||||||
|
+
|
|
@ -1,19 +1,20 @@
|
||||||
package io.lbry.lbrynet;
|
package io.lbry.lbrynet;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import java.io.InputStream;
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.File;
|
import java.io.InputStream;
|
||||||
import android.os.Bundle;
|
|
||||||
import org.kivy.android.PythonService;
|
import org.kivy.android.PythonService;
|
||||||
import org.kivy.android.PythonActivity;
|
|
||||||
import org.renpy.android.AssetExtract;
|
import org.renpy.android.AssetExtract;
|
||||||
import org.renpy.android.ResourceManager;
|
import org.renpy.android.ResourceManager;
|
||||||
|
|
||||||
|
@ -51,7 +52,8 @@ public class LbrynetService extends PythonService {
|
||||||
unpackData("private", app_root_file);
|
unpackData("private", app_root_file);
|
||||||
|
|
||||||
if (intent == null) {
|
if (intent == null) {
|
||||||
intent = buildIntent(getApplicationContext(), "");
|
intent = ServiceHelper.buildIntent(
|
||||||
|
getApplicationContext(), "", LbrynetService.class, "lbrynetservice");
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onStartCommand(intent, flags, startId);
|
return super.onStartCommand(intent, flags, startId);
|
||||||
|
@ -134,28 +136,4 @@ public class LbrynetService extends PythonService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Intent buildIntent(Context ctx, String pythonServiceArgument) {
|
|
||||||
Intent intent = new Intent(ctx, LbrynetService.class);
|
|
||||||
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
|
|
||||||
intent.putExtra("androidPrivate", ctx.getFilesDir().getAbsolutePath());
|
|
||||||
intent.putExtra("androidArgument", argument);
|
|
||||||
intent.putExtra("serviceEntrypoint", "./lbrynetservice.py");
|
|
||||||
intent.putExtra("pythonName", "lbrynetservice");
|
|
||||||
intent.putExtra("pythonHome", argument);
|
|
||||||
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");
|
|
||||||
intent.putExtra("pythonServiceArgument", pythonServiceArgument);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void start(Context ctx, String pythonServiceArgument) {
|
|
||||||
Intent intent = buildIntent(ctx, pythonServiceArgument);
|
|
||||||
ctx.startService(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void stop(Context ctx) {
|
|
||||||
Intent intent = new Intent(ctx, LbrynetService.class);
|
|
||||||
ctx.stopService(intent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
126
src/main/java/io/lbry/lbrynet/LbrynetTestRunnerService.java
Normal file
126
src/main/java/io/lbry/lbrynet/LbrynetTestRunnerService.java
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package io.lbry.lbrynet;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.kivy.android.PythonService;
|
||||||
|
import org.renpy.android.AssetExtract;
|
||||||
|
import org.renpy.android.ResourceManager;
|
||||||
|
|
||||||
|
public class LbrynetTestRunnerService extends PythonService {
|
||||||
|
|
||||||
|
public static String TAG = "LbrynetTestRunnerService";
|
||||||
|
|
||||||
|
public static LbrynetTestRunnerService serviceInstance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canDisplayNotification() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
// Extract files
|
||||||
|
File app_root_file = new File(getAppRoot());
|
||||||
|
unpackData("private", app_root_file);
|
||||||
|
|
||||||
|
if (intent == null) {
|
||||||
|
intent = ServiceHelper.buildIntent(
|
||||||
|
getApplicationContext(), "", LbrynetTestRunnerService.class, "testrunnerservice");
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceInstance = this;
|
||||||
|
return super.onStartCommand(intent, flags, startId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
serviceInstance = null;
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void broadcastTestRunnerOutput(String output) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(ServiceControlActivity.TEST_RUNNER_OUTPUT);
|
||||||
|
intent.putExtra("output", output);
|
||||||
|
sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unpackData(final String resource, File target) {
|
||||||
|
Log.v(TAG, "UNPACKING!!! " + resource + " " + target.getName());
|
||||||
|
|
||||||
|
// The version of data in memory and on disk.
|
||||||
|
ResourceManager resourceManager = new ResourceManager(getApplicationContext());
|
||||||
|
String data_version = resourceManager.getString(resource + "_version");
|
||||||
|
String disk_version = null;
|
||||||
|
|
||||||
|
Log.v(TAG, "Data version is " + data_version);
|
||||||
|
|
||||||
|
// If no version, no unpacking is necessary.
|
||||||
|
if (data_version == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the current disk version, if any.
|
||||||
|
String filesDir = target.getAbsolutePath();
|
||||||
|
String disk_version_fn = filesDir + "/" + resource + ".version";
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte buf[] = new byte[64];
|
||||||
|
InputStream is = new FileInputStream(disk_version_fn);
|
||||||
|
int len = is.read(buf);
|
||||||
|
disk_version = new String(buf, 0, len);
|
||||||
|
is.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
disk_version = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the disk data is out of date, extract it and write the
|
||||||
|
// version file.
|
||||||
|
// if (! data_version.equals(disk_version)) {
|
||||||
|
if (! data_version.equals(disk_version)) {
|
||||||
|
Log.v(TAG, "Extracting " + resource + " assets.");
|
||||||
|
|
||||||
|
recursiveDelete(target);
|
||||||
|
target.mkdirs();
|
||||||
|
|
||||||
|
AssetExtract ae = new AssetExtract(getApplicationContext());
|
||||||
|
if (!ae.extractTar(resource + ".mp3", target.getAbsolutePath())) {
|
||||||
|
//toastError("Could not extract " + resource + " data.");
|
||||||
|
Log.e(TAG, "Could not extract " + resource + " data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Write .nomedia.
|
||||||
|
new File(target, ".nomedia").createNewFile();
|
||||||
|
|
||||||
|
// Write version file.
|
||||||
|
FileOutputStream os = new FileOutputStream(disk_version_fn);
|
||||||
|
os.write(data_version.getBytes());
|
||||||
|
os.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w("python", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void recursiveDelete(File f) {
|
||||||
|
if (f.isDirectory()) {
|
||||||
|
for (File r : f.listFiles()) {
|
||||||
|
recursiveDelete(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppRoot() {
|
||||||
|
String app_root = getApplicationContext().getFilesDir().getAbsolutePath() + "/app";
|
||||||
|
return app_root;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,34 @@
|
||||||
package io.lbry.lbrynet;
|
package io.lbry.lbrynet;
|
||||||
|
|
||||||
import org.kivy.android.PythonActivity;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Handler;
|
import android.content.Intent;
|
||||||
//import android.support.v7.app.AppCompatActivity;
|
import android.content.IntentFilter;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
public class ServiceControlActivity extends Activity {
|
public class ServiceControlActivity extends Activity {
|
||||||
|
|
||||||
|
public static ServiceControlActivity activityInstance;
|
||||||
|
|
||||||
|
private IntentFilter intentFilter;
|
||||||
|
|
||||||
|
public static String TEST_RUNNER_OUTPUT = "io.lbry.lbrynet.TEST_RUNNER_OUTPUT";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag which indicates whether or not the service is running. Will be updated in the
|
* Flag which indicates whether or not the service is running. Will be updated in the
|
||||||
* onResume method.
|
* onResume method.
|
||||||
|
@ -24,6 +40,10 @@ public class ServiceControlActivity extends Activity {
|
||||||
*/
|
*/
|
||||||
private Button startStopButton;
|
private Button startStopButton;
|
||||||
|
|
||||||
|
private Button runTestsButton;
|
||||||
|
|
||||||
|
private TextView testRunnerOutput;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service status text.
|
* Service status text.
|
||||||
*/
|
*/
|
||||||
|
@ -33,32 +53,69 @@ public class ServiceControlActivity extends Activity {
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_service_control);
|
setContentView(R.layout.activity_service_control);
|
||||||
|
intentFilter = new IntentFilter();
|
||||||
|
intentFilter.addAction(TEST_RUNNER_OUTPUT);
|
||||||
|
|
||||||
startStopButton = (Button) findViewById(R.id.btn_start_stop);
|
startStopButton = (Button) findViewById(R.id.btn_start_stop);
|
||||||
|
runTestsButton = (Button) findViewById(R.id.btn_run_tests);
|
||||||
serviceStatusText = (TextView) findViewById(R.id.text_service_status);
|
serviceStatusText = (TextView) findViewById(R.id.text_service_status);
|
||||||
|
testRunnerOutput = (TextView) findViewById(R.id.test_runner_output);
|
||||||
|
|
||||||
startStopButton.setOnClickListener(new View.OnClickListener() {
|
startStopButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (serviceRunning) {
|
if (serviceRunning) {
|
||||||
LbrynetService.stop(ServiceControlActivity.this);
|
ServiceHelper.stop(ServiceControlActivity.this, LbrynetService.class);
|
||||||
} else {
|
} else {
|
||||||
LbrynetService.start(ServiceControlActivity.this, "");
|
ServiceHelper.start(ServiceControlActivity.this, "", LbrynetService.class, "lbrynetservice");
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||||
updateServiceStatus();
|
updateServiceStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
runTestsButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
boolean testsRunning = isServiceRunning(LbrynetTestRunnerService.class);
|
||||||
|
if (!testsRunning) {
|
||||||
|
ServiceHelper.start(
|
||||||
|
ServiceControlActivity.this, "", LbrynetTestRunnerService.class, "testrunnerservice");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
registerReceiver(testRunnerOutputReceiver, intentFilter);
|
||||||
|
|
||||||
|
activityInstance = this;
|
||||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||||
updateServiceStatus();
|
updateServiceStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BroadcastReceiver testRunnerOutputReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (TEST_RUNNER_OUTPUT.equals(intent.getAction())) {
|
||||||
|
String output = intent.getStringExtra("output");
|
||||||
|
updateTestRunnerOutput(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
unregisterReceiver(testRunnerOutputReceiver);
|
||||||
|
// set the activity instance to null on pause in order to prevent NullPointerException
|
||||||
|
// if the activity shuts down prematurely, for example
|
||||||
|
activityInstance = null;
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
public void updateServiceStatus() {
|
public void updateServiceStatus() {
|
||||||
new Handler().post(new Runnable() {
|
new Handler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -76,6 +133,10 @@ public class ServiceControlActivity extends Activity {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateTestRunnerOutput(String output) {
|
||||||
|
testRunnerOutput.setText(formatTestRunnerOutput(output));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isServiceRunning(Class<?> serviceClass) {
|
private boolean isServiceRunning(Class<?> serviceClass) {
|
||||||
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
for (ActivityManager.RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) {
|
for (ActivityManager.RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||||
|
@ -86,4 +147,12 @@ public class ServiceControlActivity extends Activity {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Spanned formatTestRunnerOutput(String output) {
|
||||||
|
output = output.replace("[OK]", "<font color=\"#008000\">[OK]</font>");
|
||||||
|
output = output.replace("[ERROR]", "<font color=\"#ff0000\">[ERROR]</font>");
|
||||||
|
output = output.replace("[FAILURE]", "<font color=\"#cc0000\">[FAILURE]</font>");
|
||||||
|
|
||||||
|
return Html.fromHtml(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
30
src/main/java/io/lbry/lbrynet/ServiceHelper.java
Normal file
30
src/main/java/io/lbry/lbrynet/ServiceHelper.java
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package io.lbry.lbrynet;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
public class ServiceHelper {
|
||||||
|
public static Intent buildIntent(Context ctx, String pythonServiceArgument, Class serviceClass, String pythonName) {
|
||||||
|
Intent intent = new Intent(ctx, serviceClass);
|
||||||
|
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
|
||||||
|
intent.putExtra("androidPrivate", ctx.getFilesDir().getAbsolutePath());
|
||||||
|
intent.putExtra("androidArgument", argument);
|
||||||
|
intent.putExtra("serviceEntrypoint", "./" + pythonName + ".py");
|
||||||
|
intent.putExtra("pythonName", pythonName);
|
||||||
|
intent.putExtra("pythonHome", argument);
|
||||||
|
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");
|
||||||
|
intent.putExtra("pythonServiceArgument", pythonServiceArgument);
|
||||||
|
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void start(Context ctx, String pythonServiceArgument, Class serviceClass, String pythonName) {
|
||||||
|
Intent intent = buildIntent(ctx, pythonServiceArgument, serviceClass, pythonName);
|
||||||
|
ctx.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stop(Context ctx, Class serviceClass) {
|
||||||
|
Intent intent = new Intent(ctx, serviceClass);
|
||||||
|
ctx.stopService(intent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,7 +54,8 @@ lbrynet.daemon.auth.util.save_api_keys = save_api_keys
|
||||||
lbrynet.daemon.auth.util.initialize_api_key_file = initialize_api_key_file
|
lbrynet.daemon.auth.util.initialize_api_key_file = initialize_api_key_file
|
||||||
|
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
|
if __name__ == '__main__':
|
||||||
|
ServiceApp().run()
|
||||||
from lbrynet.core import log_support
|
from lbrynet.core import log_support
|
||||||
from twisted.internet import defer, reactor
|
from twisted.internet import defer, reactor
|
||||||
from jsonrpc.proxy import JSONRPCProxy
|
from jsonrpc.proxy import JSONRPCProxy
|
||||||
|
|
76
src/main/python/testrunnerservice.py
Normal file
76
src/main/python/testrunnerservice.py
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
import sys
|
||||||
|
import StringIO
|
||||||
|
|
||||||
|
from twisted.trial.runner import (
|
||||||
|
TestLoader,
|
||||||
|
TrialRunner
|
||||||
|
)
|
||||||
|
from twisted.trial.reporter import TreeReporter
|
||||||
|
from twisted.plugin import getPlugins, IPlugin
|
||||||
|
from jnius import autoclass
|
||||||
|
import lbrynet.tests
|
||||||
|
from os import listdir
|
||||||
|
|
||||||
|
str_stream = StringIO.StringIO()
|
||||||
|
|
||||||
|
serviceClass = autoclass('io.lbry.lbrynet.LbrynetTestRunnerService')
|
||||||
|
|
||||||
|
def update_output_in_activity(str):
|
||||||
|
service = serviceClass.serviceInstance
|
||||||
|
if service is not None:
|
||||||
|
service.broadcastTestRunnerOutput(str)
|
||||||
|
|
||||||
|
class AndroidTestReporter(TreeReporter):
|
||||||
|
def addSuccess(self, test):
|
||||||
|
super(TreeReporter, self).addSuccess(test)
|
||||||
|
self.endLine('[OK]', self.SUCCESS)
|
||||||
|
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||||
|
|
||||||
|
def addError(self, *args):
|
||||||
|
super(TreeReporter, self).addError(*args)
|
||||||
|
self.endLine('[ERROR]', self.ERROR)
|
||||||
|
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||||
|
|
||||||
|
def addFailure(self, *args):
|
||||||
|
super(TreeReporter, self).addFailure(*args)
|
||||||
|
self.endLine('[FAIL]', self.FAILURE)
|
||||||
|
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||||
|
|
||||||
|
def addSkip(self, *args):
|
||||||
|
super(TreeReporter, self).addSkip(*args)
|
||||||
|
self.endLine('[SKIPPED]', self.SKIP)
|
||||||
|
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||||
|
|
||||||
|
def addExpectedFailure(self, *args):
|
||||||
|
super(TreeReporter, self).addExpectedFailure(*args)
|
||||||
|
self.endLine('[TODO]', self.TODO)
|
||||||
|
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||||
|
|
||||||
|
def addUnexpectedSuccess(self, *args):
|
||||||
|
super(TreeReporter, self).addUnexpectedSuccess(*args)
|
||||||
|
self.endLine('[SUCCESS!?!]', self.TODONE)
|
||||||
|
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||||
|
|
||||||
|
def startTest(self, test):
|
||||||
|
super(AndroidTestReporter, self).startTest(test)
|
||||||
|
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||||
|
|
||||||
|
def endLine(self, message, color):
|
||||||
|
super(AndroidTestReporter, self).endLine(message, color)
|
||||||
|
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||||
|
|
||||||
|
def str_to_basic_html(value):
|
||||||
|
return value.replace("\n", "<br>").replace(" ", ' ')
|
||||||
|
|
||||||
|
def run():
|
||||||
|
loader = TestLoader();
|
||||||
|
suite = loader.loadPackage(lbrynet.tests, True)
|
||||||
|
runner = TrialRunner(AndroidTestReporter)
|
||||||
|
runner.stream = str_stream
|
||||||
|
passFail = not runner.run(suite).wasSuccessful()
|
||||||
|
|
||||||
|
print str_stream.getvalue()
|
||||||
|
sys.exit(passFail)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run()
|
Loading…
Reference in a new issue