From 1032af6238916c757e4cd21ecd5b60f44dbb9cc0 Mon Sep 17 00:00:00 2001
From: Fabian Krueger <fabian.krueger@fau.de>
Date: Sun, 22 Sep 2019 14:51:14 +0200
Subject: [PATCH] add multithreaded stresstest

---
 store/stresstest.py | 91 +++++++++++++++++++++++++++++++++++++++++++++
 store/urls.py       |  2 +-
 store/views.py      | 19 ++++++++--
 3 files changed, 108 insertions(+), 4 deletions(-)
 create mode 100644 store/stresstest.py

diff --git a/store/stresstest.py b/store/stresstest.py
new file mode 100644
index 0000000..8487560
--- /dev/null
+++ b/store/stresstest.py
@@ -0,0 +1,91 @@
+from threading import Thread, current_thread, Barrier, Lock
+import time
+from random import randint
+from store.models import *
+from store.backend import *
+from store.store_exceptions import *
+from math import ceil, floor
+from decimal import Decimal
+
+class StressTester(Thread):
+    def __init__(self, user, min_sleep, max_sleep, barrier, lock, iterations):
+        super().__init__()
+        self.min_sleep = min_sleep
+        self.max_sleep = max_sleep
+        self.barrier = barrier
+        self.lock = lock
+        self.iterations = iterations
+        self.user = user
+        self.sequence = []
+        self.products = []
+        self.users = []
+
+    def run(self):
+        self.buildLists()
+        self.generateSequence()
+        user = list(User.objects.filter(id=self.user))[0]
+        self.barrier.wait()
+        actions = []
+        for action in self.sequence:
+            if action == 'buy':
+                product = self.products[randint(0, len(self.products) - 1)]
+                product = list(Product.objects.filter(id=product))[0]
+                try:
+                    self.buy(product.id)
+                except UserNotEnoughMoney:
+                    charge_amount = Decimal(str(randint(ceil(product.price), 50)) + "." + str(randint(0, 99)))
+                    self.charge(charge_amount)
+                    self.buy(product.id)
+                    actions.append('charged {}'.format(charge_amount))
+                actions.append('bought {} for {}'.format(product.name, product.price))
+            else:
+                receiver = self.users[randint(0, len(self.users) - 1)]
+                transfer_amount = randint(1, floor(user.money))
+                try:
+                    self.transfer(receiver, transfer_amount)
+                except UserNotEnoughMoney:
+                    charge_amount = Decimal(str(randint(ceil(transfer_amount), 50)) + "." + str(randint(0, 99)))
+                    self.charge(charge_amount)
+                    self.transfer(receiver, randint(1, floor(user.money)))
+                    actions.append('charged {}'.format(charge_amount))
+                actions.append('transfered {} to {}'.format(transfer_amount, receiver))
+        self.lock.acquire()
+        print("Thread {} running for user '{}' (id: {}):".format(current_thread().ident, user.nickname, user.id), actions)
+        self.lock.release()
+
+
+    def buildLists(self):
+        users = User.objects.exclude(id=self.user).values('id')
+        self.users = [u['id'] for u in users]
+        products = Product.objects.all().values('id')
+        self.products = [p['id'] for p in products]
+
+
+    def generateSequence(self):
+        for i in range(0, self.iterations):
+            if randint(0, 4) <= 2:
+                self.sequence.append('buy')
+            else:
+                self.sequence.append('transfer')
+
+    def buy(self, product):
+        token = TokenLogic.get_token()
+        self.wait()
+        PurchaseLogic.purchase(self.user, product, token)
+        self.wait()
+
+    def transfer(self, receiver, amount):
+        token = TokenLogic.get_token()
+        self.wait()
+        TransferLogic.transfer(self.user, receiver, amount, token)
+        self.wait()
+
+    def charge(self, amount):
+        token = TokenLogic.get_token()
+        self.wait()
+        ChargeLogic.charge(self.user, amount, token)
+        self.wait()
+    
+    def wait(self):
+        time.sleep(randint(self.min_sleep, self.max_sleep + 1))
+
diff --git a/store/urls.py b/store/urls.py
index f2a6279..c106c67 100644
--- a/store/urls.py
+++ b/store/urls.py
@@ -23,6 +23,6 @@ urlpatterns = [
     path('revert_transfer', views.revert_transfer, name="revert_transfer"),
 
     # Test
-    path('test', views.test, name="test"),
+    path('stresstest', views.stressTest, name="stresstest"),
     path('test2', views.test2, name="test2"),
 ]
diff --git a/store/views.py b/store/views.py
index 98ce7df..5bbd0a2 100644
--- a/store/views.py
+++ b/store/views.py
@@ -7,6 +7,7 @@ from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
 from django.contrib.auth import logout as auth_logout
 from .backend import *
 from .store_exceptions import *
+from .stresstest import *
 
 
 # Rendered pages
@@ -183,13 +184,25 @@ def revert_transfer(request):
 # Test
 
 
-def test(request):
-    return render(request, "test.html", {})
+def stressTest(request):
+    nthreads = 10
+    barrier = Barrier(nthreads + 1) 
+    lock = Lock()
 
+    for i in range(0, nthreads):
+        st = StressTester(i + 1, 0, 4, barrier, lock, 60)
+        st.start()
 
-def test2(request):
+    tmp = input("press any key to start")
+    barrier.wait()
+
+
+def test(request):
     transfers = TransferLogic.getFreuquentTransferTargeds(1)
     for transfer in transfers:
         print(transfer)
     print("n_transfers:", len(transfers))
 
+def test2(request):
+    pass
+
-- 
GitLab