summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/CrypterPluginTester.py81
-rw-r--r--tests/HosterPluginTester.py152
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/api/ApiProxy.py68
-rw-r--r--tests/api/ApiTester.py35
-rw-r--r--tests/api/__init__.py0
-rw-r--r--tests/api/test_JSONBackend.py31
-rw-r--r--tests/api/test_WebSocketBackend.py33
-rw-r--r--tests/api/test_api.py39
-rw-r--r--tests/api/test_noargs.py29
-rwxr-xr-xtests/clonedigger.sh2
-rw-r--r--tests/config/plugin.conf138
-rw-r--r--tests/config/pyload.conf.org75
-rw-r--r--tests/config/pyload.db.orgbin0 -> 13312 bytes
-rw-r--r--tests/crypterlinks.txt5
-rw-r--r--tests/helper/BenchmarkTest.py66
-rw-r--r--tests/helper/PluginTester.py151
-rw-r--r--tests/helper/Stubs.py141
-rw-r--r--tests/helper/__init__.py0
-rwxr-xr-xtests/hosterlinks.txt50
-rw-r--r--tests/manager/__init__.py0
-rw-r--r--tests/manager/test_configManager.py130
-rw-r--r--tests/manager/test_filemanager.py223
-rw-r--r--tests/manager/test_interactionManager.py81
-rwxr-xr-xtests/nosetests.sh5
-rw-r--r--tests/other/__init__.py0
-rw-r--r--tests/other/test_configparser.py43
-rw-r--r--tests/other/test_filedatabase.py215
-rw-r--r--tests/other/test_syntax.py43
-rwxr-xr-xtests/plugin_tests.sh7
-rwxr-xr-xtests/pyflakes.sh5
-rwxr-xr-xtests/quit_pyload.sh7
-rwxr-xr-xtests/run_pyload.sh17
-rwxr-xr-xtests/sloccount.sh2
-rw-r--r--tests/test_api.py20
-rw-r--r--tests/test_json.py48
36 files changed, 1874 insertions, 68 deletions
diff --git a/tests/CrypterPluginTester.py b/tests/CrypterPluginTester.py
new file mode 100644
index 000000000..67e5ddebc
--- /dev/null
+++ b/tests/CrypterPluginTester.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+
+from os.path import dirname, join
+from nose.tools import nottest
+
+from logging import log, DEBUG
+
+from helper.Stubs import Core
+from helper.PluginTester import PluginTester
+
+from module.plugins.Base import Fail
+from module.utils import accumulate, to_int
+
+class CrypterPluginTester(PluginTester):
+ @nottest
+ def test_plugin(self, name, url, flag):
+
+ print "%s: %s" % (name, url.encode("utf8"))
+ log(DEBUG, "%s: %s", name, url.encode("utf8"))
+
+ plugin = self.core.pluginManager.getPluginClass(name)
+ p = plugin(self.core, None, "")
+ self.thread.plugin = p
+
+ try:
+ result = p._decrypt([url])
+
+ if to_int(flag):
+ assert to_int(flag) == len(result)
+
+ except Exception, e:
+ if isinstance(e, Fail) and flag == "fail":
+ pass
+ else:
+ raise
+
+
+# setup methods
+
+c = Core()
+
+f = open(join(dirname(__file__), "crypterlinks.txt"))
+links = [x.strip() for x in f.readlines()]
+urls = []
+flags = {}
+
+for l in links:
+ if not l or l.startswith("#"): continue
+ if l.startswith("http"):
+ if "||" in l:
+ l, flag = l.split("||")
+ flags[l] = flag
+
+ urls.append(l)
+
+h, crypter = c.pluginManager.parseUrls(urls)
+plugins = accumulate(crypter)
+for plugin, urls in plugins.iteritems():
+
+ def meta_class(plugin):
+ class _testerClass(CrypterPluginTester):
+ pass
+ _testerClass.__name__ = plugin
+ return _testerClass
+
+ _testerClass = meta_class(plugin)
+
+ for i, url in enumerate(urls):
+ def meta(plugin, url, flag, sig):
+ def _test(self):
+ self.test_plugin(plugin, url, flag)
+
+ _test.func_name = sig
+ return _test
+
+ sig = "test_LINK%d" % i
+ setattr(_testerClass, sig, meta(plugin, url, flags.get(url, None), sig))
+ print url
+
+ locals()[plugin] = _testerClass
+ del _testerClass
diff --git a/tests/HosterPluginTester.py b/tests/HosterPluginTester.py
new file mode 100644
index 000000000..627494a3f
--- /dev/null
+++ b/tests/HosterPluginTester.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+
+from os.path import dirname
+from logging import log, DEBUG
+from hashlib import md5
+from time import time
+from shutil import move
+import codecs
+
+from nose.tools import nottest
+
+from helper.Stubs import Core
+from helper.PluginTester import PluginTester
+
+from module.datatypes.PyFile import PyFile
+from module.plugins.Base import Fail
+from module.utils import accumulate
+from module.utils.fs import save_join, join, exists, listdir, remove, stat
+
+DL_DIR = join("Downloads", "tmp")
+
+class HosterPluginTester(PluginTester):
+ files = {}
+
+ def setUp(self):
+ PluginTester.setUp(self)
+ for f in self.files:
+ if exists(save_join(DL_DIR, f)): remove(save_join(DL_DIR, f))
+
+ # folder for reports
+ report = join("tmp", self.__class__.__name__)
+ if exists(report):
+ for f in listdir(report):
+ remove(join(report, f))
+
+
+ @nottest
+ def test_plugin(self, name, url, flag):
+ # Print to stdout to see whats going on
+ print "%s: %s, %s" % (name, url.encode("utf8"), flag)
+ log(DEBUG, "%s: %s, %s", name, url.encode("utf8"), flag)
+
+ # url and plugin should be only important thing
+ pyfile = PyFile(self.core, -1, url, url, 0, 0, "", name, 0, 0)
+ pyfile.initPlugin()
+
+ self.thread.pyfile = pyfile
+ self.thread.plugin = pyfile.plugin
+
+ try:
+ a = time()
+ pyfile.plugin.preprocessing(self.thread)
+
+ log(DEBUG, "downloading took %ds" % (time() - a))
+ log(DEBUG, "size %d kb" % (pyfile.size / 1024))
+
+ if flag == "offline":
+ raise Exception("No offline Exception raised.")
+
+ if pyfile.name not in self.files:
+ raise Exception("Filename %s not recognized." % pyfile.name)
+
+ if not exists(save_join(DL_DIR, pyfile.name)):
+ raise Exception("File %s does not exists." % pyfile.name)
+
+ hash = md5()
+ f = open(save_join(DL_DIR, pyfile.name), "rb")
+ while True:
+ buf = f.read(4096)
+ if not buf: break
+ hash.update(buf)
+ f.close()
+
+ if hash.hexdigest() != self.files[pyfile.name]:
+ log(DEBUG, "Hash is %s" % hash.hexdigest())
+
+ size = stat(f.name).st_size
+ if size < 1024 * 1024 * 10: # 10MB
+ # Copy for debug report
+ log(DEBUG, "Downloaded file copied to report")
+ move(f.name, join("tmp", plugin, f.name))
+
+ raise Exception("Hash does not match.")
+
+
+
+ except Exception, e:
+ if isinstance(e, Fail) and flag == "fail":
+ pass
+ elif isinstance(e, Fail) and flag == "offline" and e.message == "offline":
+ pass
+ else:
+ raise
+
+
+# setup methods
+
+c = Core()
+
+# decode everything as unicode
+f = codecs.open(join(dirname(__file__), "hosterlinks.txt"), "r", "utf_8")
+links = [x.strip() for x in f.readlines()]
+urls = []
+flags = {}
+
+for l in links:
+ if not l or l.startswith("#"): continue
+ if l.startswith("http"):
+ if "||" in l:
+ l, flag = l.split("||")
+ flags[l] = str(flag.strip())
+ urls.append(l)
+
+ elif len(l.rsplit(" ", 1)) == 2:
+ name, hash = l.rsplit(" ", 1)
+ HosterPluginTester.files[name] = str(hash)
+
+hoster, c = c.pluginManager.parseUrls(urls)
+
+plugins = accumulate(hoster)
+for plugin, urls in plugins.iteritems():
+ # closure functions to retain local scope
+ def meta_class(plugin):
+ class _testerClass(HosterPluginTester):
+ pass
+ _testerClass.__name__ = plugin
+ return _testerClass
+
+ _testerClass = meta_class(plugin)
+
+ for i, url in enumerate(urls):
+ def meta(__plugin, url, flag, sig):
+ def _test(self):
+ self.test_plugin(__plugin, url, flag)
+
+ _test.func_name = sig
+ return _test
+
+ tmp_flag = flags.get(url, None)
+ if flags.get(url, None):
+ sig = "test_LINK%d_%s" % (i, tmp_flag)
+ else:
+ sig = "test_LINK%d" % i
+
+ # set test method
+ setattr(_testerClass, sig, meta(plugin, url, tmp_flag, sig))
+
+
+ #register class
+ locals()[plugin] = _testerClass
+ # remove from locals, or tested twice
+ del _testerClass
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/api/ApiProxy.py b/tests/api/ApiProxy.py
new file mode 100644
index 000000000..5a091dbee
--- /dev/null
+++ b/tests/api/ApiProxy.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+
+from module.remote.apitypes_debug import classes, methods
+
+class ApiProxy:
+ """ Proxy that does type checking on the api """
+
+ def __init__(self, api, user="User", pw="test"):
+ self.api = api
+ self.user = user
+ self.pw = pw
+
+ if user and pw is not None:
+ self.api.login(user, pw)
+
+ def assert_type(self, result, type):
+ if not type: return # void
+ try:
+ # Complex attribute
+ if isinstance(type, tuple):
+ # Optional result
+ if type[0] is None:
+ # Only check if not None
+ if result is not None: self.assert_type(result, type[1])
+
+ # List
+ elif type[0] == list:
+ assert isinstance(result, list)
+ for item in result:
+ self.assert_type(item, type[1])
+ # Dict
+ elif type[0] == dict:
+ assert isinstance(result, dict)
+ for k, v in result.iteritems():
+ self.assert_type(k, type[1])
+ self.assert_type(v, type[2])
+
+ # Struct - Api class
+ elif hasattr(result, "__name__") and result.__name__ in classes:
+ for attr, atype in zip(result.__slots__, classes[result.__name__]):
+ self.assert_type(getattr(result, attr), atype)
+ else: # simple object
+ assert isinstance(result, type)
+ except AssertionError:
+ print "Assertion for %s as %s failed" % (result, type)
+ raise
+
+
+ def call(self, func, *args, **kwargs):
+ result = getattr(self.api, func)(*args, **kwargs)
+ self.assert_type(result, methods[func])
+
+ return result
+
+
+ def __getattr__(self, item):
+ def call(*args, **kwargs):
+ return self.call(item, *args, **kwargs)
+
+ return call
+
+if __name__ == "__main__":
+
+ from module.remote.JSONClient import JSONClient
+
+ api = ApiProxy(JSONClient(), "User", "test")
+ api.getServerVersion() \ No newline at end of file
diff --git a/tests/api/ApiTester.py b/tests/api/ApiTester.py
new file mode 100644
index 000000000..b17a7655e
--- /dev/null
+++ b/tests/api/ApiTester.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+
+from module.remote.JSONClient import JSONClient
+from module.remote.WSClient import WSClient
+
+from ApiProxy import ApiProxy
+
+class ApiTester:
+
+ tester= []
+
+ @classmethod
+ def register(cls, tester):
+ cls.tester.append(tester)
+
+ @classmethod
+ def get_methods(cls):
+ """ All available methods for testing """
+ methods = []
+ for t in cls.tester:
+ methods.extend(getattr(t, attr) for attr in dir(t) if attr.startswith("test_"))
+ return methods
+
+ def __init__(self):
+ ApiTester.register(self)
+ self.api = None
+
+ def setApi(self, api):
+ self.api = api
+
+ def enableJSON(self):
+ self.api = ApiProxy(JSONClient())
+
+ def enableWS(self):
+ self.api = ApiProxy(WSClient())
diff --git a/tests/api/__init__.py b/tests/api/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/api/__init__.py
diff --git a/tests/api/test_JSONBackend.py b/tests/api/test_JSONBackend.py
new file mode 100644
index 000000000..b6509a52a
--- /dev/null
+++ b/tests/api/test_JSONBackend.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+
+from nose.tools import raises
+
+from module.remote.apitypes import Forbidden
+from module.remote.JSONClient import JSONClient
+
+class TestJSONBackend:
+
+ def setUp(self):
+ self.client = JSONClient()
+
+ def test_login(self):
+ self.client.login("User", "test")
+ self.client.getServerVersion()
+ self.client.logout()
+
+ def test_wronglogin(self):
+ ret = self.client.login("WrongUser", "wrongpw")
+ assert ret == False
+
+ @raises(Forbidden)
+ def test_access(self):
+ self.client.getServerVersion()
+
+ @raises(AttributeError)
+ def test_unknown_method(self):
+ self.client.login("User", "test")
+ self.client.sdfdsg()
+
+ # TODO: test raising of exceptions, also in WSBackend \ No newline at end of file
diff --git a/tests/api/test_WebSocketBackend.py b/tests/api/test_WebSocketBackend.py
new file mode 100644
index 000000000..3c714a5e2
--- /dev/null
+++ b/tests/api/test_WebSocketBackend.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+from nose.tools import raises
+
+from module.remote.apitypes import Forbidden
+from module.remote.WSClient import WSClient
+
+class TestWebSocketBackend:
+
+ def setUp(self):
+ self.client = WSClient()
+ self.client.connect()
+
+ def tearDown(self):
+ self.client.close()
+
+ def test_login(self):
+ self.client.login("User", "test")
+ self.client.getServerVersion()
+ self.client.logout()
+
+ def test_wronglogin(self):
+ ret = self.client.login("WrongUser", "wrongpw")
+ assert ret == False
+
+ @raises(Forbidden)
+ def test_access(self):
+ self.client.getServerVersion()
+
+ @raises(AttributeError)
+ def test_unknown_method(self):
+ self.client.login("User", "test")
+ self.client.sdfdsg()
diff --git a/tests/api/test_api.py b/tests/api/test_api.py
new file mode 100644
index 000000000..68448b891
--- /dev/null
+++ b/tests/api/test_api.py
@@ -0,0 +1,39 @@
+
+from unittest import TestCase
+from random import choice
+
+from pyLoadCore import Core
+
+from ApiTester import ApiTester
+
+class TestAPI(TestCase):
+ """
+ Test all available testers randomly and on all backends
+ """
+ core = None
+
+ @classmethod
+ def setUpClass(cls):
+ from test_noargs import TestNoArgs
+
+ cls.core = Core()
+ cls.core.start(False, False, True)
+ for Test in (TestNoArgs,):
+ t = Test()
+ t.enableJSON()
+ t = Test()
+ t.enableWS()
+ t = Test()
+ t.setApi(cls.core.api)
+
+ cls.methods = ApiTester.get_methods()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.core.shutdown()
+
+ def test_random(self, n=10000):
+
+ for i in range(n):
+ func = choice(self.methods)
+ func()
diff --git a/tests/api/test_noargs.py b/tests/api/test_noargs.py
new file mode 100644
index 000000000..12da0b417
--- /dev/null
+++ b/tests/api/test_noargs.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+
+import inspect
+
+from ApiTester import ApiTester
+
+from module.remote.apitypes import Iface
+
+IGNORE = ('quit', 'restart')
+
+class TestNoArgs(ApiTester):
+ def setUp(self):
+ self.enableJSON()
+
+# Setup test_methods dynamically, only these which require no arguments
+for name in dir(Iface):
+ if name.startswith("_") or name in IGNORE: continue
+
+ spec = inspect.getargspec(getattr(Iface, name))
+ if len(spec.args) == 1 and (not spec.varargs or len(spec.varargs) == 0):
+ def meta_test(name): #retain local scope
+ def test(self):
+ getattr(self.api, name)()
+ test.func_name = "test_%s" % name
+ return test
+
+ setattr(TestNoArgs, "test_%s" % name, meta_test(name))
+
+ del meta_test \ No newline at end of file
diff --git a/tests/clonedigger.sh b/tests/clonedigger.sh
new file mode 100755
index 000000000..93c1cb323
--- /dev/null
+++ b/tests/clonedigger.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+clonedigger -o cpd.xml --cpd-output --fast --ignore-dir=lib --ignore-dir=remote module
diff --git a/tests/config/plugin.conf b/tests/config/plugin.conf
new file mode 100644
index 000000000..5e7ee3858
--- /dev/null
+++ b/tests/config/plugin.conf
@@ -0,0 +1,138 @@
+version: 2
+
+[MultiuploadCom]
+preferedHoster = multiupload
+ignoredHoster =
+
+[SerienjunkiesOrg]
+preferredHoster = RapidshareCom,UploadedTo,NetloadIn,FilefactoryCom,FreakshareNet,FilebaseTo,MegauploadCom,HotfileCom,DepositfilesCom,EasyshareCom,KickloadCom
+changeName = True
+
+[EmbeduploadCom]
+preferedHoster = embedupload
+ignoredHoster =
+
+[MultiloadCz]
+usedHoster =
+ignoredHoster =
+
+[WiiReloadedOrg]
+changeName = True
+
+[Xdcc]
+nick = pyload
+ident = pyloadident
+realname = pyloadreal
+
+[UlozTo]
+reuseCaptcha = True
+captchaUser =
+captchaNb =
+
+[YoutubeCom]
+quality = hd
+fmt = 0
+.mp4 = True
+.flv = True
+.webm = False
+.3gp = False
+
+[RapidshareCom]
+server = None
+
+[VeehdCom]
+filename_spaces = False
+replacement_char = _
+
+[RealdebridCom]
+https = False
+
+[ClickAndLoad]
+activated = True
+extern = False
+
+[ExtractArchive]
+activated = True
+fullpath = True
+overwrite = True
+passwordfile = unrar_passwords.txt
+deletearchive = False
+subfolder = False
+destination =
+queue = True
+renice = 0
+
+[CaptchaTrader]
+activated = True
+username =
+force = False
+passkey =
+
+[MergeFiles]
+activated = False
+
+[IRCInterface]
+activated = False
+host = Enter your server here!
+port = 6667
+ident = pyload-irc
+realname = pyload-irc
+nick = pyLoad-IRC
+owner = Enter your nick here!
+info_file = False
+info_pack = True
+captcha = True
+
+[Ev0InFetcher]
+activated = False
+interval = 10
+queue = False
+shows =
+quality = xvid
+hoster = NetloadIn,RapidshareCom,MegauploadCom,HotfileCom
+
+[EasybytezCom]
+activated = False
+includeHoster =
+excludeHoster =
+
+[XMPPInterface]
+activated = False
+jid = user@exmaple-jabber-server.org
+pw =
+tls = False
+owners = me@icq-gateway.org;some@msn-gateway.org
+info_file = False
+info_pack = True
+captcha = True
+
+[RehostTo]
+activated = False
+
+[MultiHoster]
+activated = True
+
+[MultiHome]
+activated = False
+interfaces = None
+
+[MultishareCz]
+activated = False
+includeHoster =
+excludeHoster = rapidshare.com|uloz.to
+
+[HotFolder]
+activated = False
+folder = container
+watch_file = False
+keep = True
+file = links.txt
+
+[ExternalScripts]
+activated = True
+
+[UpdateManager]
+activated = True
+interval = 360
+debug = False
+
diff --git a/tests/config/pyload.conf.org b/tests/config/pyload.conf.org
new file mode 100644
index 000000000..7fb1c8c87
--- /dev/null
+++ b/tests/config/pyload.conf.org
@@ -0,0 +1,75 @@
+version: 2
+
+[remote]
+nolocalauth = False
+activated = True
+port = 7227
+listenaddr = 127.0.0.1
+
+[log]
+log_size = 100
+log_folder = Logs
+file_log = False
+log_count = 5
+log_rotate = True
+
+[permission]
+group = users
+change_dl = False
+change_file = False
+user = user
+file = 0644
+change_group = False
+folder = 0755
+change_user = False
+
+[general]
+language = en
+download_folder = Downloads
+checksum = False
+folder_per_package = True
+debug_mode = True
+min_free_space = 200
+renice = 0
+
+[ssl]
+cert = ssl.crt
+activated = False
+key = ssl.key
+
+[webinterface]
+template = default
+activated = True
+prefix =
+server = builtin
+host = 127.0.0.1
+https = False
+port = 8001
+
+[proxy]
+username =
+proxy = False
+address = localhost
+password =
+type = http
+port = 7070
+
+[reconnect]
+endTime = 0:00
+activated = False
+method = ./reconnect.sh
+startTime = 0:00
+
+[download]
+max_downloads = 3
+limit_speed = False
+interface =
+skip_existing = False
+max_speed = -1
+ipv6 = False
+chunks = 3
+
+[downloadTime]
+start = 0:00
+end = 0:00
+
diff --git a/tests/config/pyload.db.org b/tests/config/pyload.db.org
new file mode 100644
index 000000000..d340531c5
--- /dev/null
+++ b/tests/config/pyload.db.org
Binary files differ
diff --git a/tests/crypterlinks.txt b/tests/crypterlinks.txt
new file mode 100644
index 000000000..4ff651888
--- /dev/null
+++ b/tests/crypterlinks.txt
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+
+# Crypter links, append ||fail or ||#N to mark error or number of expected results (single links+packages)
+
+http://www.filesonic.com/folder/19906605||2
diff --git a/tests/helper/BenchmarkTest.py b/tests/helper/BenchmarkTest.py
new file mode 100644
index 000000000..d28c52959
--- /dev/null
+++ b/tests/helper/BenchmarkTest.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+
+from time import time
+
+
+class BenchmarkTest:
+
+ bench = []
+ results = {}
+
+ @classmethod
+ def timestamp(cls, name, a):
+ t = time()
+ r = cls.results.get(name, [])
+ r.append((t-a) * 1000)
+ cls.results[name] = r
+
+ @classmethod
+ def benchmark(cls, n=1):
+
+ print "Benchmarking %s" % cls.__name__
+ print
+
+ for i in range(n):
+ cls.collect_results()
+
+ if "setUpClass" in cls.results:
+ cls.bench.insert(0, "setUpClass")
+
+ if "tearDownClass" in cls.results:
+ cls.bench.append("tearDownClass")
+
+ length = str(max([len(k) for k in cls.bench]) + 1)
+ total = 0
+
+ for k in cls.bench:
+ v = cls.results[k]
+
+ if len(v) > 1:
+ print ("%" + length +"s: %s | average: %.2f ms") % (k, ", ".join(["%.2f" % x for x in v]), sum(v)/len(v))
+ total += sum(v)/len(v)
+ else:
+ print ("%" + length +"s: %.2f ms") % (k, v[0])
+ total += v[0]
+
+ print "\ntotal: %.2f ms" % total
+
+
+ @classmethod
+ def collect_results(cls):
+ if hasattr(cls, "setUpClass"):
+ a = time()
+ cls.setUpClass()
+ cls.timestamp("setUpClass", a)
+
+ obj = cls()
+
+ for f in cls.bench:
+ a = time()
+ getattr(obj, "test_" + f)()
+ cls.timestamp(f, a)
+
+ if hasattr(cls, "tearDownClass"):
+ a = time()
+ cls.tearDownClass()
+ cls.timestamp("tearDownClass", a) \ No newline at end of file
diff --git a/tests/helper/PluginTester.py b/tests/helper/PluginTester.py
new file mode 100644
index 000000000..ef61385be
--- /dev/null
+++ b/tests/helper/PluginTester.py
@@ -0,0 +1,151 @@
+# -*- coding: utf-8 -*-
+
+from unittest import TestCase
+from os import makedirs, remove
+from os.path import exists, join, expanduser
+from shutil import move
+from sys import exc_clear, exc_info
+from logging import log, DEBUG
+from time import sleep, time
+from random import randint
+from glob import glob
+
+from pycurl import LOW_SPEED_TIME, FORM_FILE
+from json import loads
+
+from Stubs import Thread, Core, noop
+
+from module.network.RequestFactory import getRequest, getURL
+from module.plugins.Hoster import Hoster, Abort, Fail
+
+def _wait(self):
+ """ waits the time previously set """
+ self.waiting = True
+
+ waittime = self.pyfile.waitUntil - time()
+ log(DEBUG, "waiting %ss" % waittime)
+
+ if self.wantReconnect and waittime > 300:
+ raise Fail("Would wait for reconnect %ss" % waittime)
+ elif waittime > 300:
+ raise Fail("Would wait %ss" % waittime)
+
+ while self.pyfile.waitUntil > time():
+ sleep(1)
+ if self.pyfile.abort: raise Abort
+
+ self.waiting = False
+ self.pyfile.setStatus("starting")
+
+Hoster.wait = _wait
+
+
+def decryptCaptcha(self, url, get={}, post={}, cookies=False, forceUser=False, imgtype='jpg',
+ result_type='textual'):
+ img = self.load(url, get=get, post=post, cookies=cookies)
+
+ id = ("%.2f" % time())[-6:].replace(".", "")
+ temp_file = open(join("tmp", "tmpCaptcha_%s_%s.%s" % (self.__name__, id, imgtype)), "wb")
+ temp_file.write(img)
+ temp_file.close()
+
+ Ocr = self.core.pluginManager.loadClass("captcha", self.__name__)
+
+ if Ocr:
+ log(DEBUG, "Using tesseract for captcha")
+ sleep(randint(3000, 5000) / 1000.0)
+ if self.pyfile.abort: raise Abort
+
+ ocr = Ocr()
+ result = ocr.get_captcha(temp_file.name)
+ else:
+ log(DEBUG, "Using ct for captcha")
+ # put username and passkey into two lines in ct.conf
+ conf = join(expanduser("~"), "ct.conf")
+ if not exists(conf): raise Exception("CaptchaTrader config %s not found." % conf)
+ f = open(conf, "rb")
+ req = getRequest()
+
+ #raise timeout threshold
+ req.c.setopt(LOW_SPEED_TIME, 80)
+
+ try:
+ json = req.load("http://captchatrader.com/api/submit",
+ post={"api_key": "9f65e7f381c3af2b076ea680ae96b0b7",
+ "username": f.readline().strip(),
+ "password": f.readline().strip(),
+ "value": (FORM_FILE, temp_file.name),
+ "type": "file"}, multipart=True)
+ finally:
+ f.close()
+ req.close()
+
+ response = loads(json)
+ log(DEBUG, str(response))
+ result = response[1]
+
+ self.cTask = response[0]
+
+ return result
+
+Hoster.decryptCaptcha = decryptCaptcha
+
+
+def respond(ticket, value):
+ conf = join(expanduser("~"), "ct.conf")
+ f = open(conf, "rb")
+ try:
+ getURL("http://captchatrader.com/api/respond",
+ post={"is_correct": value,
+ "username": f.readline().strip(),
+ "password": f.readline().strip(),
+ "ticket": ticket})
+ except Exception, e :
+ print "CT Exception:", e
+ log(DEBUG, str(e))
+ finally:
+ f.close()
+
+
+
+def invalidCaptcha(self):
+ log(DEBUG, "Captcha invalid")
+ if self.cTask:
+ respond(self.cTask, 0)
+
+Hoster.invalidCaptcha = invalidCaptcha
+
+def correctCaptcha(self):
+ log(DEBUG, "Captcha correct")
+ if self.cTask:
+ respond(self.cTask, 1)
+
+Hoster.correctCaptcha = correctCaptcha
+
+Hoster.checkForSameFiles = noop
+
+class PluginTester(TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls.core = Core()
+ name = "%s.%s" % (cls.__module__, cls.__name__)
+ for f in glob(join(name, "debug_*")):
+ remove(f)
+
+ # Copy debug report to attachment dir for jenkins
+ @classmethod
+ def tearDownClass(cls):
+ name = "%s.%s" % (cls.__module__, cls.__name__)
+ if not exists(name): makedirs(name)
+ for f in glob("debug_*"):
+ move(f, join(name, f))
+
+ def setUp(self):
+ self.thread = Thread(self.core)
+ exc_clear()
+
+ def tearDown(self):
+ exc = exc_info()
+ if exc != (None, None, None):
+ debug = self.thread.writeDebugReport()
+ log(DEBUG, debug)
diff --git a/tests/helper/Stubs.py b/tests/helper/Stubs.py
new file mode 100644
index 000000000..f0e8d0614
--- /dev/null
+++ b/tests/helper/Stubs.py
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+
+import sys
+from os.path import abspath, dirname, join
+from time import strftime
+from traceback import format_exc
+from collections import defaultdict
+
+sys.path.append(abspath(join(dirname(__file__), "..", "..", "module", "lib")))
+sys.path.append(abspath(join(dirname(__file__), "..", "..")))
+
+import __builtin__
+
+from module.Api import Role
+from module.datatypes.User import User
+from module.datatypes.PyPackage import PyPackage
+from module.threads.BaseThread import BaseThread
+from module.config.ConfigParser import ConfigParser
+from module.network.RequestFactory import RequestFactory
+from module.PluginManager import PluginManager
+from module.common.JsEngine import JsEngine
+
+from logging import log, DEBUG, INFO, WARN, ERROR
+
+
+# Do nothing
+def noop(*args, **kwargs):
+ pass
+
+ConfigParser.save = noop
+
+class LogStub:
+ def debug(self, *args):
+ log(DEBUG, *args)
+
+ def info(self, *args):
+ log(INFO, *args)
+
+ def error(self, *args):
+ log(ERROR, *args)
+
+ def warning(self, *args):
+ log(WARN, *args)
+
+
+class NoLog:
+ def debug(self, *args):
+ pass
+
+ def info(self, *args):
+ pass
+
+ def error(self, *args):
+ log(ERROR, *args)
+
+ def warning(self, *args):
+ log(WARN, *args)
+
+
+class Core:
+ def __init__(self):
+ self.log = NoLog()
+
+ self.api = self.core = self
+ self.threadManager = self
+ self.debug = True
+ self.captcha = True
+ self.config = ConfigParser()
+ self.pluginManager = PluginManager(self)
+ self.requestFactory = RequestFactory(self)
+ __builtin__.pyreq = self.requestFactory
+ self.accountManager = AccountManager()
+ self.addonManager = AddonManager()
+ self.eventManager = self.evm = NoopClass()
+ self.interActionManager = self.im = NoopClass()
+ self.js = JsEngine()
+ self.cache = {}
+ self.packageCache = {}
+
+ self.statusMsg = defaultdict(lambda: "statusmsg")
+
+ self.log = LogStub()
+
+ def getServerVersion(self):
+ return "TEST_RUNNER on %s" % strftime("%d %h %Y")
+
+ def path(self, path):
+ return path
+
+ def updateLink(self, *args):
+ pass
+
+ def updatePackage(self, *args):
+ pass
+
+ def processingIds(self, *args):
+ return []
+
+ def getPackage(self, id):
+ return PyPackage(self, 0, "tmp", "tmp", "", "", 0, 0)
+
+ def print_exc(self):
+ log(ERROR, format_exc())
+
+
+class NoopClass:
+ def __getattr__(self, item):
+ return noop
+
+
+class AddonManager(NoopClass):
+ def activePlugins(self):
+ return []
+
+
+class AccountManager:
+ def getAccountForPlugin(self, name):
+ return None
+
+
+class Thread(BaseThread):
+ def __init__(self, core):
+ BaseThread.__init__(self, core)
+ self.plugin = None
+
+
+ def writeDebugReport(self):
+ if hasattr(self, "pyfile"):
+ dump = BaseThread.writeDebugReport(self, self.plugin.__name__, pyfile=self.pyfile)
+ else:
+ dump = BaseThread.writeDebugReport(self, self.plugin.__name__, plugin=self.plugin)
+
+ return dump
+
+__builtin__._ = lambda x: x
+__builtin__.pypath = abspath(join(dirname(__file__), "..", ".."))
+__builtin__.addonManager = AddonManager()
+__builtin__.pyreq = None
+
+adminUser = User(None, uid=0, role=Role.Admin)
+normalUser = User(None, uid=1, role=Role.User) \ No newline at end of file
diff --git a/tests/helper/__init__.py b/tests/helper/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/helper/__init__.py
diff --git a/tests/hosterlinks.txt b/tests/hosterlinks.txt
new file mode 100755
index 000000000..70dcc44f6
--- /dev/null
+++ b/tests/hosterlinks.txt
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+
+# Valid files, with md5 hash
+# Please only use files around 5-15 MB and with explicit permission for redistribution
+
+http://download.pyload.org/random.bin
+random.bin d76505d0869f9f928a17d42d66326307
+Mořská želva ( Зелёная черепаха .&+ 綠蠵龜 _@- Đồi mồi dứa ).tar 932212256dc0b0a1e71c0944eef633a4
+Mořská_želva_(_Зелёная_черепаха_.&+_綠蠵龜__@-_Đồi_mồi_dứa_).tar 932212256dc0b0a1e71c0944eef633a4
+
+# Hoster links, append ||offline or ||fail to mark your expectation
+
+http://netload.in/datei9XirAJZs79/random.bin.htm
+http://rapidshare.com/files/445996776/random.bin
+http://hotfile.com/dl/101569859/2e01f04/random.bin.html
+http://www.megaupload.com/?d=1JZLOP3B
+http://www.shragle.com/files/f899389b/random.bin
+http://www10.zippyshare.com/v/76557688/file.html
+http://yourfiles.to/?d=312EC6E911
+http://depositfiles.com/files/k8la98953
+http://uploading.com/files/3896f5a1/random.bin/
+
+
+http://ul.to/file/o41isx||offline
+http://www.4shared.com/file/-O5CBhQV/random.html||offline
+http://www.4shared.com/file/-O5CBhQV/random.html||offline
+http://www.fileserve.com/file/MxjZXjX||offline
+http://www.share-online.biz/download.php?id=PTCOX1GL6XAH||offline
+http://dl.free.fr/d4aL5dyXY||offline
+http://files.mail.ru/32EW66||offline
+http://www.shragle.com/files/f899389b/random.bin||offline
+
+
+# Hoster links with fancy unicode filenames:
+http://vs3iaw.1fichier.com/fr/
+http://www.4shared.com/file/rQltf2Fr/Mosk_elva___Зелная_черепаха___.html
+http://bezvadata.cz/stahnout/99273_morska-zelva-.-d-i-m-i-d-a-.tar
+http://www.crocko.com/A524453DA89841B4BFC4FB9125D6F186/
+http://czshare.com/2483034/zelva
+http://www.easybytez.com/etvhltkg0d05
+http://www.filejungle.com/f/qX5fxT/
+http://fp.io/43798f2b/
+http://www.filesonic.com/file/yU2cU6s
+http://www.fshare.vn/file/A7H8LSTP7Z/
+http://ifile.it/muwgivz
+http://letitbit.net/download/67793.60a7d3745791db7271a6e6c92cfe/Mořská_želva_(_Зелёная_черепаха_.___綠蠵龜___-_Đồi_mồi_dứa_).tar.html
+http://www.mediafire.com/file/n09th58z1x5r585
+http://www.quickshare.cz/stahnout-soubor/676150:morska-zelva----_-oi-moi-dua-tar_6MB
+http://www.uloz.to/12553820/morska-zelva-oi-moi-dua-tar
+http://www.wupload.com/file/2642593407/ \ No newline at end of file
diff --git a/tests/manager/__init__.py b/tests/manager/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/manager/__init__.py
diff --git a/tests/manager/test_configManager.py b/tests/manager/test_configManager.py
new file mode 100644
index 000000000..8ab607a87
--- /dev/null
+++ b/tests/manager/test_configManager.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+
+from unittest import TestCase
+from collections import defaultdict
+
+from nose.tools import raises
+
+from tests.helper.Stubs import Core
+
+from module.Api import InvalidConfigSection
+from module.database import DatabaseBackend
+from module.config.ConfigParser import ConfigParser
+from module.config.ConfigManager import ConfigManager
+
+adminUser = None
+normalUser = 1
+
+class TestConfigManager(TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.core = Core()
+ cls.db = DatabaseBackend(cls.core)
+ cls.core.db = cls.db
+ cls.db.manager = cls.core
+ cls.db.manager.statusMsg = defaultdict(lambda: "statusmsg")
+ cls.parser = ConfigParser()
+ cls.config = ConfigManager(cls.core, cls.parser)
+ cls.db.setup()
+
+ def setUp(self):
+ self.db.clearAllConfigs()
+ # used by some tests, needs to be deleted
+ self.config.delete("plugin", adminUser)
+
+
+ def addConfig(self):
+ self.config.addConfigSection("plugin", "Name", "desc", "something",
+ [("value", "str", "label", "desc", "default")])
+
+ def test_db(self):
+
+ self.db.saveConfig("plugin", "some value", 0)
+ self.db.saveConfig("plugin", "some other value", 1)
+
+ assert self.db.loadConfig("plugin", 0) == "some value"
+ assert self.db.loadConfig("plugin", 1) == "some other value"
+
+ d = self.db.loadAllConfigs()
+ assert d[0]["plugin"] == "some value"
+ assert self.db.loadConfigsForUser(0)["plugin"] == "some value"
+
+ self.db.deleteConfig("plugin", 0)
+
+ assert 0 not in self.db.loadAllConfigs()
+ assert "plugin" not in self.db.loadConfigsForUser(0)
+
+ self.db.deleteConfig("plugin")
+
+ assert not self.db.loadAllConfigs()
+ assert self.db.loadConfig("plugin") == ""
+
+ def test_parser(self):
+ assert self.config.get("general", "language")
+ self.config["general"]["language"] = "de"
+ assert self.config["general"]["language"] == "de"
+ assert self.config.get("general", "language", adminUser) == "de"
+
+ def test_user(self):
+ self.addConfig()
+
+ assert self.config["plugin"]["value"] == "default"
+ assert self.config.get("plugin", "value", adminUser) == "default"
+ assert self.config.get("plugin", "value", normalUser) == "default"
+
+ assert self.config.set("plugin", "value", False, user=normalUser)
+ assert self.config.get("plugin", "value", normalUser) is False
+ assert self.config["plugin"]["value"] == "default"
+
+ assert self.config.set("plugin", "value", True, user=adminUser)
+ assert self.config.get("plugin", "value", adminUser) is True
+ assert self.config["plugin"]["value"] is True
+ assert self.config.get("plugin", "value", normalUser) is False
+
+ self.config.delete("plugin", normalUser)
+ assert self.config.get("plugin", "value", normalUser) == "default"
+
+ self.config.delete("plugin")
+ assert self.config.get("plugin", "value", adminUser) == "default"
+ assert self.config["plugin"]["value"] == "default"
+
+ # should not trigger something
+ self.config.delete("foo")
+
+ def test_sections(self):
+ self.addConfig()
+
+ i = 0
+ # there should be only one section, with no values
+ for name, config, values in self.config.iterSections(adminUser):
+ assert name == "plugin"
+ assert values == {}
+ i +=1
+ assert i == 1
+
+ assert self.config.set("plugin", "value", True, user=adminUser)
+
+ i = 0
+ # now we assert the correct values
+ for name, config, values in self.config.iterSections(adminUser):
+ assert name == "plugin"
+ assert values == {"value":True}
+ i +=1
+ assert i == 1
+
+ def test_get_section(self):
+ self.addConfig()
+ assert self.config.getSection("plugin")[0].name == "Name"
+
+ @raises(InvalidConfigSection)
+ def test_restricted_access(self):
+ self.config.get("general", "language", normalUser)
+
+ @raises(InvalidConfigSection)
+ def test_error(self):
+ self.config.get("foo", "bar")
+
+ @raises(InvalidConfigSection)
+ def test_error_set(self):
+ self.config.set("foo", "bar", True) \ No newline at end of file
diff --git a/tests/manager/test_filemanager.py b/tests/manager/test_filemanager.py
new file mode 100644
index 000000000..5b9fbb567
--- /dev/null
+++ b/tests/manager/test_filemanager.py
@@ -0,0 +1,223 @@
+# -*- coding: utf-8 -*-
+
+from random import choice
+
+from tests.helper.Stubs import Core
+from tests.helper.BenchmarkTest import BenchmarkTest
+
+from module.database import DatabaseBackend
+# disable asyncronous queries
+DatabaseBackend.async = DatabaseBackend.queue
+
+from module.Api import DownloadState
+from module.FileManager import FileManager
+
+
+class TestFileManager(BenchmarkTest):
+ bench = ["add_packages", "add_files", "get_files_root", "get",
+ "get_package_content", "get_package_tree",
+ "order_package", "order_files", "move"]
+
+ pids = [-1]
+ count = 100
+
+ @classmethod
+ def setUpClass(cls):
+ c = Core()
+ # db needs seperate initialisation
+ cls.db = c.db = DatabaseBackend(c)
+ cls.db.setup()
+ cls.db.purgeAll()
+
+ cls.m = cls.db.manager = FileManager(c)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.db.purgeAll()
+ cls.db.shutdown()
+
+
+ # benchmarker ignore setup
+ def setUp(self):
+ self.db.purgeAll()
+ self.pids = [-1]
+
+ self.count = 20
+ self.test_add_packages()
+ self.test_add_files()
+
+ def test_add_packages(self):
+ for i in range(100):
+ pid = self.m.addPackage("name", "folder", choice(self.pids), "", "", "", False)
+ self.pids.append(pid)
+
+ if -1 in self.pids:
+ self.pids.remove(-1)
+
+ def test_add_files(self):
+ for pid in self.pids:
+ self.m.addLinks([("plugin %d" % i, "url %s" % i) for i in range(self.count)], pid)
+
+ count = self.m.getQueueStats()[0]
+ files = self.count * len(self.pids)
+ # in test runner files get added twice
+ assert count == files or count == files * 2
+
+ def test_get(self):
+ info = self.m.getPackageInfo(choice(self.pids))
+ assert info.stats.linkstotal == self.count
+
+ fid = choice(info.fids)
+ f = self.m.getFile(fid)
+ assert f.fid in self.m.files
+
+ f.name = "new name"
+ f.sync()
+ finfo = self.m.getFileInfo(fid)
+ assert finfo is not None
+ assert finfo.name == "new name"
+
+ p = self.m.getPackage(choice(self.pids))
+ assert p is not None
+ assert p.pid in self.m.packages
+ p.sync()
+
+ p.delete()
+
+ self.m.getTree(-1, True, None)
+
+ def test_get_filtered(self):
+ all = self.m.getTree(-1, True, None)
+ finished = self.m.getTree(-1, True, DownloadState.Finished)
+ unfinished = self.m.getTree(-1, True, DownloadState.Unfinished)
+
+ assert len(finished.files) + len(unfinished.files) == len(all.files) == self.m.db.filecount()
+
+
+ def test_get_files_root(self):
+ view = self.m.getTree(-1, True, None)
+
+ for pid in self.pids:
+ assert pid in view.packages
+
+ assert len(view.packages) == len(self.pids)
+
+ p = choice(view.packages.values())
+ assert len(p.fids) == self.count
+ assert p.stats.linkstotal == self.count
+
+
+ def test_get_package_content(self):
+ view = self.m.getTree(choice(self.pids), False, None)
+ p = view.root
+
+ assert len(view.packages) == len(p.pids)
+ for pid in p.pids: assert pid in view.packages
+
+ def test_get_package_tree(self):
+ view = self.m.getTree(choice(self.pids), True, None)
+ for pid in view.root.pids: assert pid in view.packages
+ for fid in view.root.fids: assert fid in view.files
+
+ def test_delete(self):
+ self.m.deleteFile(self.count * 5)
+ self.m.deletePackage(choice(self.pids))
+
+ def test_order_package(self):
+ parent = self.m.addPackage("order", "", -1, "", "", "", False)
+ self.m.addLinks([("url", "plugin") for i in range(100)], parent)
+
+ pids = [self.m.addPackage("c", "", parent, "", "", "", False) for i in range(5)]
+ v = self.m.getTree(parent, False, None)
+ self.assert_ordered(pids, 0, 5, v.root.pids, v.packages, True)
+
+ pid = v.packages.keys()[0]
+ self.assert_pack_ordered(parent, pid, 3)
+ self.assert_pack_ordered(parent, pid, 0)
+ self.assert_pack_ordered(parent, pid, 0)
+ self.assert_pack_ordered(parent, pid, 4)
+ pid = v.packages.keys()[2]
+ self.assert_pack_ordered(parent, pid, 4)
+ self.assert_pack_ordered(parent, pid, 3)
+ self.assert_pack_ordered(parent, pid, 2)
+
+
+ def test_order_files(self):
+ parent = self.m.addPackage("order", "", -1, "", "", "", False)
+ self.m.addLinks([("url", "plugin") for i in range(100)], parent)
+ v = self.m.getTree(parent, False, None)
+
+ fids = v.root.fids[10:20]
+ v = self.assert_files_ordered(parent, fids, 0)
+
+ fids = v.root.fids[20:30]
+
+ self.m.orderFiles(fids, parent, 99)
+ v = self.m.getTree(parent, False, None)
+ assert fids[-1] == v.root.fids[-1]
+ assert fids[0] == v.root.fids[90]
+ self.assert_ordered(fids, 90, 100, v.root.fids, v.files)
+
+ fids = v.root.fids[80:]
+ v = self.assert_files_ordered(parent, fids, 20)
+
+ self.m.orderFiles(fids, parent, 80)
+ v = self.m.getTree(parent, False, None)
+ self.assert_ordered(fids, 61, 81, v.root.fids, v.files)
+
+ fids = v.root.fids[50:51]
+ self.m.orderFiles(fids, parent, 99)
+ v = self.m.getTree(parent, False, None)
+ self.assert_ordered(fids, 99, 100, v.root.fids, v.files)
+
+ fids = v.root.fids[50:51]
+ v = self.assert_files_ordered(parent, fids, 0)
+
+
+ def assert_files_ordered(self, parent, fids, pos):
+ fs = [self.m.getFile(choice(fids)) for i in range(5)]
+ self.m.orderFiles(fids, parent, pos)
+ v = self.m.getTree(parent, False, False)
+ self.assert_ordered(fids, pos, pos+len(fids), v.root.fids, v.files)
+
+ return v
+
+ def assert_pack_ordered(self, parent, pid, pos):
+ self.m.orderPackage(pid, pos)
+ v = self.m.getTree(parent, False, False)
+ self.assert_ordered([pid], pos, pos+1, v.root.pids, v.packages, True)
+
+ # assert that ordering is total, complete with no gaps
+ def assert_ordered(self, part, start, end, data, dict, pack=False):
+ assert data[start:end] == part
+ if pack:
+ assert sorted([p.packageorder for p in dict.values()]) == range(len(dict))
+ assert [dict[pid].packageorder for pid in part] == range(start, end)
+ else:
+ assert sorted([f.fileorder for f in dict.values()]) == range(len(dict))
+ assert [dict[fid].fileorder for fid in part] == range(start, end)
+
+
+ def test_move(self):
+
+ pid = self.pids[-1]
+ pid2 = self.pids[1]
+
+ self.m.movePackage(pid, -1)
+ v = self.m.getTree(-1, False, False)
+
+ assert v.root.pids[-1] == pid
+ assert sorted([p.packageorder for p in v.packages.values()]) == range(len(v.packages))
+
+ v = self.m.getTree(pid, False, False)
+ fids = v.root.fids[10:20]
+ self.m.moveFiles(fids, pid2)
+ v = self.m.getTree(pid2, False, False)
+
+ assert sorted([f.fileorder for f in v.files.values()]) == range(len(v.files))
+ assert len(v.files) == self.count + len(fids)
+
+
+
+if __name__ == "__main__":
+ TestFileManager.benchmark() \ No newline at end of file
diff --git a/tests/manager/test_interactionManager.py b/tests/manager/test_interactionManager.py
new file mode 100644
index 000000000..0ab7af80d
--- /dev/null
+++ b/tests/manager/test_interactionManager.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+
+from unittest import TestCase
+
+from tests.helper.Stubs import Core
+
+from module.Api import InputType, Interaction
+from module.interaction.InteractionManager import InteractionManager
+
+
+class TestInteractionManager(TestCase):
+ ADMIN = None
+ USER = 1
+
+ def assertEmpty(self, list1):
+ return self.assertListEqual(list1, [])
+
+ @classmethod
+ def setUpClass(cls):
+ cls.core = Core()
+
+ def setUp(self):
+ self.im = InteractionManager(self.core)
+
+ self.assertFalse(self.im.isClientConnected(self.ADMIN))
+ self.assertFalse(self.im.isTaskWaiting(self.ADMIN))
+ self.assertEmpty(self.im.getTasks(self.ADMIN))
+
+ def test_notifications(self):
+ n = self.im.createNotification("test", "notify")
+
+ self.assertTrue(self.im.isTaskWaiting(self.ADMIN))
+ self.assertListEqual(self.im.getTasks(self.ADMIN), [n])
+
+ n.seen = True
+ self.assertFalse(self.im.isTaskWaiting(self.ADMIN))
+
+ for i in range(10):
+ self.im.createNotification("title", "test")
+
+ self.assertEqual(len(self.im.getTasks(self.ADMIN)), 11)
+ self.assertFalse(self.im.getTasks(self.USER))
+ self.assertFalse(self.im.getTasks(self.ADMIN, Interaction.Query))
+
+
+ def test_captcha(self):
+ t = self.im.createCaptchaTask("1", "png", "", owner=self.ADMIN)
+
+ self.assertEqual(t.type, Interaction.Captcha)
+ self.assertListEqual(self.im.getTasks(self.ADMIN), [t])
+ self.assertEmpty(self.im.getTasks(self.USER))
+ t.setShared()
+ self.assertListEqual(self.im.getTasks(self.USER), [t])
+
+ t2 = self.im.createCaptchaTask("2", "png", "", owner=self.USER)
+ self.assertTrue(self.im.isTaskWaiting(self.USER))
+ self.assertEmpty(self.im.getTasks(self.USER, Interaction.Query))
+ self.im.removeTask(t)
+
+ self.assertListEqual(self.im.getTasks(self.ADMIN), [t2])
+ self.assertIs(self.im.getTaskByID(t2.iid), t2)
+
+
+ def test_query(self):
+ t = self.im.createQueryTask(InputType.Text, "text", owner=self.ADMIN)
+
+ self.assertEqual(t.description, "text")
+ self.assertListEqual(self.im.getTasks(self.ADMIN, Interaction.Query), [t])
+ self.assertEmpty(self.im.getTasks(Interaction.Captcha))
+
+
+ def test_clients(self):
+ self.im.getTasks(self.ADMIN, Interaction.Captcha)
+
+ self.assertTrue(self.im.isClientConnected(self.ADMIN))
+ self.assertFalse(self.im.isClientConnected(self.USER))
+
+
+ def test_users(self):
+ t = self.im.createCaptchaTask("1", "png", "", owner=self.USER)
+ self.assertListEqual(self.im.getTasks(self.ADMIN), [t])
diff --git a/tests/nosetests.sh b/tests/nosetests.sh
new file mode 100755
index 000000000..c68861b90
--- /dev/null
+++ b/tests/nosetests.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+NS=nosetests
+which nosetests2 > /dev/null && NS=nosetests2
+$NS tests/ --with-coverage --with-xunit --cover-package=module --cover-erase
+coverage xml
diff --git a/tests/other/__init__.py b/tests/other/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/other/__init__.py
diff --git a/tests/other/test_configparser.py b/tests/other/test_configparser.py
new file mode 100644
index 000000000..7f34e64d3
--- /dev/null
+++ b/tests/other/test_configparser.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+
+from nose.tools import raises
+
+from tests.helper.Stubs import Core
+
+from module.config.ConfigParser import ConfigParser
+
+class TestConfigParser():
+
+ @classmethod
+ def setUpClass(cls):
+ # Only needed to get imports right
+ cls.core = Core()
+ cls.config = ConfigParser()
+
+ def test_dict(self):
+
+ assert self.config["general"]["language"]
+ self.config["general"]["language"] = "de"
+ assert self.config["general"]["language"] == "de"
+
+ def test_contains(self):
+
+ assert "general" in self.config
+ assert "foobaar" not in self.config
+
+ def test_iter(self):
+ for section, config, values in self.config.iterSections():
+ assert isinstance(section, basestring)
+ assert isinstance(config.config, dict)
+ assert isinstance(values, dict)
+
+ def test_get(self):
+ assert self.config.getSection("general")[0].config
+
+ @raises(KeyError)
+ def test_invalid_config(self):
+ print self.config["invalid"]["config"]
+
+ @raises(KeyError)
+ def test_invalid_section(self):
+ print self.config["general"]["invalid"] \ No newline at end of file
diff --git a/tests/other/test_filedatabase.py b/tests/other/test_filedatabase.py
new file mode 100644
index 000000000..9a5b236a8
--- /dev/null
+++ b/tests/other/test_filedatabase.py
@@ -0,0 +1,215 @@
+# -*- coding: utf-8 -*-
+
+from tests.helper.Stubs import Core
+from tests.helper.BenchmarkTest import BenchmarkTest
+
+from module.Api import DownloadState, PackageInfo, FileInfo
+from module.database import DatabaseBackend
+
+# disable asyncronous queries
+DatabaseBackend.async = DatabaseBackend.queue
+
+from random import choice
+
+class TestDatabase(BenchmarkTest):
+ bench = ["insert", "insert_links", "insert_many", "get_packages",
+ "get_files", "get_files_queued", "get_package_childs", "get_package_files",
+ "get_package_data", "get_file_data", "find_files", "collector", "purge"]
+ pids = None
+ fids = None
+ owner = 123
+ pstatus = 0
+
+ @classmethod
+ def setUpClass(cls):
+ cls.pids = [-1]
+ cls.fids = []
+
+ cls.db = DatabaseBackend(Core())
+ cls.db.manager = cls.db.core
+
+ cls.db.setup()
+ cls.db.purgeAll()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.db.purgeAll()
+ cls.db.shutdown()
+
+ # benchmarker ignore setup
+ def setUp(self):
+ self.db.purgeAll()
+ self.pids = [-1]
+ self.fids = []
+
+ self.test_insert(20)
+ self.test_insert_many()
+ self.fids = self.db.getAllFiles().keys()
+
+
+ def test_insert(self, n=200):
+ for i in range(n):
+ pid = self.db.addPackage("name", "folder", choice(self.pids), "password", "site", "comment", self.pstatus,
+ self.owner)
+ self.pids.append(pid)
+
+ def test_insert_links(self):
+ for i in range(10000):
+ fid = self.db.addLink("url %s" % i, "name", "plugin", choice(self.pids), self.owner)
+ self.fids.append(fid)
+
+ def test_insert_many(self):
+ for pid in self.pids:
+ self.db.addLinks([("url %s" % i, "plugin") for i in range(50)], pid, self.owner)
+
+ def test_get_packages(self):
+ packs = self.db.getAllPackages()
+ n = len(packs)
+ assert n == len(self.pids) - 1
+
+ print "Fetched %d packages" % n
+ self.assert_pack(choice(packs.values()))
+
+ def test_get_files(self):
+ files = self.db.getAllFiles()
+ n = len(files)
+ assert n >= len(self.pids)
+
+ print "Fetched %d files" % n
+ self.assert_file(choice(files.values()))
+
+ def test_get_files_queued(self):
+ files = self.db.getAllFiles(state=DownloadState.Unfinished)
+ print "Fetched %d files queued" % len(files)
+
+ def test_delete(self):
+ pid = choice(self.pids)
+ self.db.deletePackage(pid)
+ self.pids.remove(pid)
+
+ def test_get_package_childs(self):
+ pid = choice(self.pids)
+ packs = self.db.getAllPackages(root=pid)
+
+ print "Package %d has %d packages" % (pid, len(packs))
+ self.assert_pack(choice(packs.values()))
+
+ def test_get_package_files(self):
+ pid = choice(self.pids)
+ files = self.db.getAllFiles(package=pid)
+
+ print "Package %d has %d files" % (pid, len(files))
+ self.assert_file(choice(files.values()))
+
+ def test_get_package_data(self, stats=False):
+ pid = choice(self.pids)
+ p = self.db.getPackageInfo(pid, stats)
+ self.assert_pack(p)
+ # test again with stat
+ if not stats:
+ self.test_get_package_data(True)
+
+ def test_get_file_data(self):
+ fid = choice(self.fids)
+ f = self.db.getFileInfo(fid)
+ self.assert_file(f)
+
+ def test_find_files(self):
+ files = self.db.getAllFiles(search="1")
+ print "Found %s files" % len(files)
+ f = choice(files.values())
+
+ assert "1" in f.name
+ names = self.db.getMatchingFilenames("1")
+ for name in names:
+ assert "1" in name
+
+ def test_collector(self):
+ self.db.saveCollector(0, "data")
+ assert self.db.retrieveCollector(0) == "data"
+ self.db.deleteCollector(0)
+
+ def test_purge(self):
+ self.db.purgeLinks()
+
+
+ def test_user_context(self):
+ self.db.purgeAll()
+
+ p1 = self.db.addPackage("name", "folder", 0, "password", "site", "comment", self.pstatus, 0)
+ self.db.addLink("url", "name", "plugin", p1, 0)
+ p2 = self.db.addPackage("name", "folder", 0, "password", "site", "comment", self.pstatus, 1)
+ self.db.addLink("url", "name", "plugin", p2, 1)
+
+ assert len(self.db.getAllPackages(owner=0)) == 1 == len(self.db.getAllFiles(owner=0))
+ assert len(self.db.getAllPackages(root=0, owner=0)) == 1 == len(self.db.getAllFiles(package=p1, owner=0))
+ assert len(self.db.getAllPackages(owner=1)) == 1 == len(self.db.getAllFiles(owner=1))
+ assert len(self.db.getAllPackages(root=0, owner=1)) == 1 == len(self.db.getAllFiles(package=p2, owner=1))
+ assert len(self.db.getAllPackages()) == 2 == len(self.db.getAllFiles())
+
+ self.db.deletePackage(p1, 1)
+ assert len(self.db.getAllPackages(owner=0)) == 1 == len(self.db.getAllFiles(owner=0))
+ self.db.deletePackage(p1, 0)
+ assert len(self.db.getAllPackages(owner=1)) == 1 == len(self.db.getAllFiles(owner=1))
+ self.db.deletePackage(p2)
+
+ assert len(self.db.getAllPackages()) == 0
+
+ def test_count(self):
+ self.db.purgeAll()
+
+ assert self.db.downloadstats() == (0,0)
+ assert self.db.queuestats() == (0,0)
+ assert self.db.processcount() == 0
+
+ def test_update(self):
+ p1 = self.db.addPackage("name", "folder", 0, "password", "site", "comment", self.pstatus, 0)
+ pack = self.db.getPackageInfo(p1)
+ assert isinstance(pack, PackageInfo)
+
+ pack.folder = "new folder"
+ pack.comment = "lol"
+ pack.tags.append("video")
+
+ self.db.updatePackage(pack)
+
+ pack = self.db.getPackageInfo(p1)
+ assert pack.folder == "new folder"
+ assert pack.comment == "lol"
+ assert "video" in pack.tags
+
+ def assert_file(self, f):
+ try:
+ assert f is not None
+ assert isinstance(f, FileInfo)
+ self.assert_int(f, ("fid", "status", "size", "media", "fileorder", "added", "package", "owner"))
+ assert f.status in range(5)
+ assert f.owner == self.owner
+ assert f.media in range(1024)
+ assert f.package in self.pids
+ assert f.added > 10 ** 6 # date is usually big integer
+ except:
+ print f
+ raise
+
+ def assert_pack(self, p):
+ try:
+ assert p is not None
+ assert isinstance(p, PackageInfo)
+ self.assert_int(p, ("pid", "root", "added", "status", "packageorder", "owner"))
+ assert p.pid in self.pids
+ assert p.owner == self.owner
+ assert p.status in range(5)
+ assert p.root in self.pids
+ assert p.added > 10 ** 6
+ assert isinstance(p.tags, list)
+ assert p.shared in (0, 1)
+ except:
+ print p
+ raise
+
+ def assert_int(self, obj, list):
+ for attr in list: assert type(getattr(obj, attr)) == int
+
+if __name__ == "__main__":
+ TestDatabase.benchmark() \ No newline at end of file
diff --git a/tests/other/test_syntax.py b/tests/other/test_syntax.py
new file mode 100644
index 000000000..fbf7edf8f
--- /dev/null
+++ b/tests/other/test_syntax.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+
+from os import walk
+from os.path import abspath, dirname, join
+
+from unittest import TestCase
+
+PATH = abspath(join(dirname(abspath(__file__)), "..", "..", ""))
+
+# needed to register globals
+from tests.helper import Stubs
+
+class TestSyntax(TestCase):
+ pass
+
+
+for path, dirs, files in walk(join(PATH, "module")):
+
+ for f in files:
+ if not f.endswith(".py") or f.startswith("__"): continue
+ fpath = join(path, f)
+ pack = fpath.replace(PATH, "")[1:-3] #replace / and .py
+ imp = pack.replace("/", ".")
+ packages = imp.split(".")
+ #__import__(imp)
+
+ # to much sideeffect when importing
+ if "web" in packages or "lib" in packages: continue
+ if "ThriftTest" in packages: continue
+
+ # currying
+ def meta(imp, sig):
+ def _test(self=None):
+ __import__(imp)
+
+ _test.func_name = sig
+ return _test
+
+ # generate test methods
+ sig = "test_%s_%s" % (packages[-2], packages[-1])
+
+
+ setattr(TestSyntax, sig, meta(imp, sig)) \ No newline at end of file
diff --git a/tests/plugin_tests.sh b/tests/plugin_tests.sh
new file mode 100755
index 000000000..be06c0dc5
--- /dev/null
+++ b/tests/plugin_tests.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+NS=nosetests
+which nosetests2 > /dev/null && NS=nosetests2
+# must be executed within tests dir
+cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+$NS HosterPluginTester.py CrypterPluginTester.py -s --with-xunit --with-coverage --cover-erase --cover-package=module.plugins --with-id
+coverage xml
diff --git a/tests/pyflakes.sh b/tests/pyflakes.sh
new file mode 100755
index 000000000..0c7e03891
--- /dev/null
+++ b/tests/pyflakes.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+find -name '*.py' |egrep -v '^.(/tests/|/module/lib)'|xargs pyflakes > pyflakes.log || :
+# Filter warnings and strip ./ from path
+cat pyflakes.log | awk -F\: '{printf "%s:%s: [E]%s\n", $1, $2, $3}' | grep -i -E -v "'_'|pypath|webinterface|pyreq|hookmanager" > pyflakes.txt
+sed -i 's/^.\///g' pyflakes.txt
diff --git a/tests/quit_pyload.sh b/tests/quit_pyload.sh
new file mode 100755
index 000000000..e466bcb31
--- /dev/null
+++ b/tests/quit_pyload.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+PYTHON=python
+which python2 > /dev/null && PYTHON=python2
+$PYTHON pyLoadCore.py --configdir=tests/config --quit
+if [ -d userplugins ]; then
+ rm -r userplugins
+fi \ No newline at end of file
diff --git a/tests/run_pyload.sh b/tests/run_pyload.sh
new file mode 100755
index 000000000..66498cd10
--- /dev/null
+++ b/tests/run_pyload.sh
@@ -0,0 +1,17 @@
+#/usr/bin/env bash
+cp tests/config/pyload.db.org tests/config/pyload.db
+cp tests/config/pyload.conf.org tests/config/pyload.conf
+
+PYTHON=python
+which python2 > /dev/null && PYTHON=python2
+
+touch pyload.out
+$PYTHON pyLoadCore.py -d --configdir=tests/config > pyload.out 2> pyload.err &
+
+for i in {1..30}; do
+ grep 8001 pyload.out > /dev/null && echo "pyLoad started" && break
+ sleep 1
+done
+
+echo "pyLoad start script finished"
+
diff --git a/tests/sloccount.sh b/tests/sloccount.sh
new file mode 100755
index 000000000..0dab4164e
--- /dev/null
+++ b/tests/sloccount.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+sloccount --duplicates --wide --details module > sloccount.sc
diff --git a/tests/test_api.py b/tests/test_api.py
deleted file mode 100644
index f8901f731..000000000
--- a/tests/test_api.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common import APIExerciser
-from nose.tools import nottest
-
-
-class TestApi:
-
- def __init__(self):
- self.api = APIExerciser.APIExerciser(None, True, "TestUser", "pwhere")
-
- def test_login(self):
- assert self.api.api.login("crapp", "wrong pw") is False
-
- #takes really long, only test when needed
- @nottest
- def test_random(self):
-
- for i in range(0, 100):
- self.api.testAPI()
diff --git a/tests/test_json.py b/tests/test_json.py
deleted file mode 100644
index ff56e8f5a..000000000
--- a/tests/test_json.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from urllib import urlencode
-from urllib2 import urlopen, HTTPError
-from json import loads
-
-from logging import log
-
-url = "http://localhost:8001/api/%s"
-
-class TestJson:
-
- def call(self, name, post=None):
- if not post: post = {}
- post["session"] = self.key
- u = urlopen(url % name, data=urlencode(post))
- return loads(u.read())
-
- def setUp(self):
- u = urlopen(url % "login", data=urlencode({"username": "TestUser", "password": "pwhere"}))
- self.key = loads(u.read())
- assert self.key is not False
-
- def test_wronglogin(self):
- u = urlopen(url % "login", data=urlencode({"username": "crap", "password": "wrongpw"}))
- assert loads(u.read()) is False
-
- def test_access(self):
- try:
- urlopen(url % "getServerVersion")
- except HTTPError, e:
- assert e.code == 403
- else:
- assert False
-
- def test_status(self):
- ret = self.call("statusServer")
- log(1, str(ret))
- assert "pause" in ret
- assert "queue" in ret
-
- def test_unknown_method(self):
- try:
- self.call("notExisting")
- except HTTPError, e:
- assert e.code == 404
- else:
- assert False \ No newline at end of file