From 3080f31d1ee74104640dcff451922cd0ae88ee22 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Isma=C3=ABl=20Bouya?= Date: Fri, 20 Apr 2018 20:09:13 +0200 Subject: [PATCH] Move acceptance tests to common directory --- test.py | 17 +++- test_acceptance.py => tests/acceptance.py | 119 +++++++++++----------- tests/helper.py | 12 ++- tests/test_acceptance.py | 25 +++++ tests/test_ccxt_wrapper.py | 3 +- tests/test_main.py | 1 + tests/test_market.py | 2 + tests/test_portfolio.py | 6 ++ tests/test_store.py | 6 ++ 9 files changed, 123 insertions(+), 68 deletions(-) rename test_acceptance.py => tests/acceptance.py (90%) create mode 100644 tests/test_acceptance.py diff --git a/test.py b/test.py index 8b9d35b..d7743b2 100644 --- a/test.py +++ b/test.py @@ -1,10 +1,17 @@ import unittest +from tests.acceptance import TimeMock -from tests.test_ccxt_wrapper import * -from tests.test_main import * -from tests.test_market import * -from tests.test_store import * -from tests.test_portfolio import * +from tests.helper import limits + +if "unit" in limits: + from tests.test_ccxt_wrapper import * + from tests.test_main import * + from tests.test_market import * + from tests.test_store import * + from tests.test_portfolio import * + +if "acceptance" in limits: + from tests.test_acceptance import * if __name__ == '__main__': unittest.main() diff --git a/test_acceptance.py b/tests/acceptance.py similarity index 90% rename from test_acceptance.py rename to tests/acceptance.py index 3633928..66014ca 100644 --- a/test_acceptance.py +++ b/tests/acceptance.py @@ -2,7 +2,6 @@ import requests import requests_mock import sys, os import time, datetime -import unittest from unittest import mock from ssl import SSLError from decimal import Decimal @@ -11,8 +10,56 @@ import psycopg2 from io import StringIO import re import functools -import glob +import threading +class TimeMock: + delta = {} + delta_init = 0 + true_time = time.time + true_sleep = time.sleep + time_patch = None + datetime_patch = None + + @classmethod + def travel(cls, start_date): + cls.delta = {} + cls.delta_init = (datetime.datetime.now() - start_date).total_seconds() + + @classmethod + def start(cls): + cls.delta = {} + cls.delta_init = 0 + + class fake_datetime(datetime.datetime): + @classmethod + def now(cls, tz=None): + if tz is None: + return cls.fromtimestamp(time.time()) + else: + return tz.fromutc(cls.utcfromtimestamp(time.time()).replace(tzinfo=tz)) + + cls.time_patch = mock.patch.multiple(time, time=cls.fake_time, sleep=cls.fake_sleep) + cls.datetime_patch = mock.patch.multiple(datetime, datetime=fake_datetime) + cls.time_patch.start() + cls.datetime_patch.start() + + @classmethod + def stop(cls): + cls.delta = {} + cls.delta_init = 0 + + @classmethod + def fake_time(cls): + cls.delta.setdefault(threading.current_thread(), cls.delta_init) + return cls.true_time() - cls.delta[threading.current_thread()] + + @classmethod + def fake_sleep(cls, duration): + cls.delta.setdefault(threading.current_thread(), cls.delta_init) + cls.delta[threading.current_thread()] -= float(duration) + cls.true_sleep(min(float(duration), 0.1)) + +TimeMock.start() import main class FileMock: @@ -51,7 +98,6 @@ class FileMock: if not line.startswith("[Worker] "): self.tester.fail("« {} » not found in log file {}".format(line, split_logs)) # Le fichier de log est écrit - # Le fichier de log est printed uniquement si non quiet # Le rapport est écrit si pertinent # Le rapport contient le bon nombre de lignes @@ -142,11 +188,16 @@ class RequestsMock: def stop(self): self.mocker.stop() + lazy_calls = [ + "https://cryptoportfolio.io/wp-content/uploads/portfolio/json/cryptoportfolio.json", + "https://poloniex.com/public?command=returnTicker", + ] def check_calls(self): self.tester.assertEqual([], self.error_calls) for (method, url), elements in self.mocks.items(): for market_id, element in elements.items(): - self.tester.assertEqual(0, len(element), "Missing calls to {} {}, market_id {}".format(method, url, market_id)) + if url not in self.lazy_calls: + self.tester.assertEqual(0, len(element), "Missing calls to {} {}, market_id {}".format(method, url, market_id)) def clean_body(self, body): if body is None: @@ -168,6 +219,8 @@ def callback(self, elements, request, context): elif element["response"] is not None: self.last_https[element["date"]] = element["response"] + time.sleep(element.get("duration", 0)) + assert self.clean_body(request.body) == \ self.clean_body(element["body"]), "Body does not match" context.status_code = element["status"] @@ -202,45 +255,6 @@ class GlobalVariablesMock: pass -class TimeMock: - delta = 0 - true_time = time.time - true_sleep = time.sleep - time_patch = None - datetime_patch = None - - @classmethod - def start(cls, start_date): - cls.delta = (datetime.datetime.now() - start_date).total_seconds() - - class fake_datetime(datetime.datetime): - @classmethod - def now(cls, tz=None): - if tz is None: - return cls.fromtimestamp(time.time()) - else: - return tz.fromutc(cls.utcfromtimestamp(time.time()).replace(tzinfo=tz)) - - cls.time_patch = mock.patch.multiple(time, time=cls.fake_time, sleep=cls.fake_sleep) - cls.datetime_patch = mock.patch.multiple(datetime, datetime=fake_datetime) - cls.time_patch.start() - cls.datetime_patch.start() - - @classmethod - def stop(cls): - cls.delta = 0 - cls.datetime_patch.stop() - cls.time_patch.stop() - - @classmethod - def fake_time(cls): - return cls.true_time() - cls.delta - - @classmethod - def fake_sleep(cls, duration): - cls.delta -= duration - cls.true_sleep(0.2) - class AcceptanceTestCase(): def parse_file(self, report_file): with open(report_file, "rb") as f: @@ -327,7 +341,7 @@ class AcceptanceTestCase(): self.requests_mock.start() self.file_mock.start() self.global_variables_mock.start() - TimeMock.start(self.start_date) + TimeMock.travel(self.start_date) def base_test(self): main.main(self.config) @@ -342,20 +356,3 @@ class AcceptanceTestCase(): self.requests_mock.stop() self.database_mock.stop() -for dirfile in glob.glob("tests/acceptance/**/*/", recursive=True): - json_files = glob.glob("{}/*.json".format(dirfile)) - log_files = glob.glob("{}/*.log".format(dirfile)) - if len(json_files) > 0: - name = dirfile.replace("tests/acceptance/", "").replace("/", "_")[0:-1] - cname = "".join(list(map(lambda x: x.capitalize(), name.split("_")))) - - globals()[cname] = type(cname, - (AcceptanceTestCase,unittest.TestCase), { - "log_files": log_files, - "files": json_files, - "test_{}".format(name): AcceptanceTestCase.base_test - }) - -if __name__ == '__main__': - unittest.main() - diff --git a/tests/helper.py b/tests/helper.py index 4548b16..b85bf3a 100644 --- a/tests/helper.py +++ b/tests/helper.py @@ -6,9 +6,19 @@ import requests_mock from io import StringIO import portfolio, market, main, store -__all__ = ["unittest", "WebMockTestCase", "mock", "D", +__all__ = ["limits", "unittest", "WebMockTestCase", "mock", "D", "StringIO"] +limits = ["acceptance", "unit"] +for test_type in limits: + if "--no{}".format(test_type) in sys.argv: + sys.argv.remove("--no{}".format(test_type)) + limits.remove(test_type) + if "--only{}".format(test_type) in sys.argv: + sys.argv.remove("--only{}".format(test_type)) + limits = [test_type] + break + class WebMockTestCase(unittest.TestCase): import time diff --git a/tests/test_acceptance.py b/tests/test_acceptance.py new file mode 100644 index 0000000..77a6cca --- /dev/null +++ b/tests/test_acceptance.py @@ -0,0 +1,25 @@ +from .helper import limits +from tests.acceptance import AcceptanceTestCase + +import unittest +import glob + +__all__ = [] + +for dirfile in glob.glob("tests/acceptance/**/*/", recursive=True): + json_files = glob.glob("{}/*.json".format(dirfile)) + log_files = glob.glob("{}/*.log".format(dirfile)) + if len(json_files) > 0: + name = dirfile.replace("tests/acceptance/", "").replace("/", "_")[0:-1] + cname = "".join(list(map(lambda x: x.capitalize(), name.split("_")))) + + globals()[cname] = unittest.skipUnless("acceptance" in limits, "Acceptance skipped")( + type(cname, (AcceptanceTestCase, unittest.TestCase), { + "log_files": log_files, + "files": json_files, + "test_{}".format(name): AcceptanceTestCase.base_test + }) + ) + __all__.append(cname) + + diff --git a/tests/test_ccxt_wrapper.py b/tests/test_ccxt_wrapper.py index 18feab3..10e334d 100644 --- a/tests/test_ccxt_wrapper.py +++ b/tests/test_ccxt_wrapper.py @@ -1,7 +1,8 @@ -from .helper import unittest, mock, D +from .helper import limits, unittest, mock, D import requests_mock import market +@unittest.skipUnless("unit" in limits, "Unit skipped") class poloniexETest(unittest.TestCase): def setUp(self): super().setUp() diff --git a/tests/test_main.py b/tests/test_main.py index cee89ce..d2f8029 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,6 +1,7 @@ from .helper import * import main, market +@unittest.skipUnless("unit" in limits, "Unit skipped") class MainTest(WebMockTestCase): def test_make_order(self): self.m.get_ticker.return_value = { diff --git a/tests/test_market.py b/tests/test_market.py index fd23162..14b23b5 100644 --- a/tests/test_market.py +++ b/tests/test_market.py @@ -2,6 +2,7 @@ from .helper import * import market, store, portfolio import datetime +@unittest.skipUnless("unit" in limits, "Unit skipped") class MarketTest(WebMockTestCase): def setUp(self): super().setUp() @@ -729,6 +730,7 @@ class MarketTest(WebMockTestCase): store_report.assert_called_once() +@unittest.skipUnless("unit" in limits, "Unit skipped") class ProcessorTest(WebMockTestCase): def test_values(self): processor = market.Processor(self.m) diff --git a/tests/test_portfolio.py b/tests/test_portfolio.py index 14dc995..4d78996 100644 --- a/tests/test_portfolio.py +++ b/tests/test_portfolio.py @@ -2,6 +2,7 @@ from .helper import * import portfolio import datetime +@unittest.skipUnless("unit" in limits, "Unit skipped") class ComputationTest(WebMockTestCase): def test_compute_value(self): compute = mock.Mock() @@ -25,6 +26,7 @@ class ComputationTest(WebMockTestCase): portfolio.Computation.compute_value("foo", "bid", compute_value="test") compute.assert_called_with("foo", "bid") +@unittest.skipUnless("unit" in limits, "Unit skipped") class TradeTest(WebMockTestCase): def test_values_assertion(self): @@ -609,6 +611,7 @@ class TradeTest(WebMockTestCase): self.assertEqual("ETH", as_json["currency"]) self.assertEqual("BTC", as_json["base_currency"]) +@unittest.skipUnless("unit" in limits, "Unit skipped") class BalanceTest(WebMockTestCase): def test_values(self): balance = portfolio.Balance("BTC", { @@ -684,6 +687,7 @@ class BalanceTest(WebMockTestCase): self.assertEqual(D(0), as_json["margin_available"]) self.assertEqual(D(0), as_json["margin_borrowed"]) +@unittest.skipUnless("unit" in limits, "Unit skipped") class OrderTest(WebMockTestCase): def test_values(self): order = portfolio.Order("buy", portfolio.Amount("ETH", 10), @@ -1745,6 +1749,7 @@ class OrderTest(WebMockTestCase): result = order.retrieve_order() self.assertFalse(result) +@unittest.skipUnless("unit" in limits, "Unit skipped") class MouvementTest(WebMockTestCase): def test_values(self): mouvement = portfolio.Mouvement("ETH", "BTC", { @@ -1802,6 +1807,7 @@ class MouvementTest(WebMockTestCase): self.assertEqual("BTC", as_json["base_currency"]) self.assertEqual("ETH", as_json["currency"]) +@unittest.skipUnless("unit" in limits, "Unit skipped") class AmountTest(WebMockTestCase): def test_values(self): amount = portfolio.Amount("BTC", "0.65") diff --git a/tests/test_store.py b/tests/test_store.py index e281adb..ffd2645 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -4,12 +4,14 @@ import datetime import threading import market, portfolio, store +@unittest.skipUnless("unit" in limits, "Unit skipped") class NoopLockTest(unittest.TestCase): def test_with(self): noop_lock = store.NoopLock() with noop_lock: self.assertTrue(True) +@unittest.skipUnless("unit" in limits, "Unit skipped") class LockedVarTest(unittest.TestCase): def test_values(self): @@ -61,6 +63,7 @@ class LockedVarTest(unittest.TestCase): thread3.join() self.assertEqual("Bar", locked_var.get()[0:3]) +@unittest.skipUnless("unit" in limits, "Unit skipped") class TradeStoreTest(WebMockTestCase): def test_compute_trades(self): self.m.balances.currencies.return_value = ["XMR", "DASH", "XVG", "BTC", "ETH"] @@ -285,6 +288,7 @@ class TradeStoreTest(WebMockTestCase): self.assertEqual([trade_mock1, trade_mock2], trade_store.pending) +@unittest.skipUnless("unit" in limits, "Unit skipped") class BalanceStoreTest(WebMockTestCase): def setUp(self): super().setUp() @@ -437,6 +441,7 @@ class BalanceStoreTest(WebMockTestCase): self.assertEqual(1, as_json["BTC"]) self.assertEqual(2, as_json["ETH"]) +@unittest.skipUnless("unit" in limits, "Unit skipped") class ReportStoreTest(WebMockTestCase): def test_add_log(self): with self.subTest(market=self.m): @@ -997,6 +1002,7 @@ class ReportStoreTest(WebMockTestCase): 'action': 'Hey' }) +@unittest.skipUnless("unit" in limits, "Unit skipped") class PortfolioTest(WebMockTestCase): def setUp(self): super().setUp() -- 2.41.0