From 919ae4786173501ce9cb5cf130edf59bee3eb1d6 Mon Sep 17 00:00:00 2001
From: Valentin Syrovatskiy <v.syrovatskiy@picodata.io>
Date: Fri, 22 Jul 2022 19:40:24 +0300
Subject: [PATCH] test: randomized testing

---
 test/rand/.gitignore         |   1 +
 test/rand/test_randomized.py | 125 ++++++++++++++++++++++-------------
 2 files changed, 79 insertions(+), 47 deletions(-)
 create mode 100644 test/rand/.gitignore

diff --git a/test/rand/.gitignore b/test/rand/.gitignore
new file mode 100644
index 0000000000..b5b55d57fa
--- /dev/null
+++ b/test/rand/.gitignore
@@ -0,0 +1 @@
+seeds.txt
diff --git a/test/rand/test_randomized.py b/test/rand/test_randomized.py
index d1dcbec819..546415ed59 100644
--- a/test/rand/test_randomized.py
+++ b/test/rand/test_randomized.py
@@ -1,8 +1,10 @@
+import sys
 import time
 import random
 import os
 import signal
-from conftest import Cluster, Instance
+from conftest import Cluster, Instance, ReturnError
+import pathlib
 
 
 def int_to_base_62(num: int):
@@ -15,79 +17,108 @@ def int_to_base_62(num: int):
     return rete
 
 
-def add(c: Cluster, _):
-    return c.add_instance()
+def create(c: Cluster, istate):
+    i = c.add_instance(wait_ready=False)
+    istate[i.instance_id] = {"instance": i, "started": False}
+    return i, istate
 
 
-def stop(_, i: Instance):
+def stop(i: Instance, istate):
     assert i.process, f"Instance {i.instance_id} expected has process"
     assert i.process.pid, f"Instance {i.instance_id} expected process to be alive"
-    return os.kill(i.process.pid, signal.SIGTERM)
+    istate[i.instance_id]["started"] = False
+    return os.kill(i.process.pid, signal.SIGTERM), istate
 
 
-def start(_, i: Instance):
-    return i.start()
+def start(i: Instance, istate):
+    istate[i.instance_id]["started"] = True
+    return i.start(), istate
 
 
+ADD = "add"
+STOP = "stop"
+START = "start"
 ACTIONS = {
-    "add": {
-        "repr": "Add",
-        "fn": add,
+    ADD: {
+        "name": ADD,
+        "repr_fn": lambda i: f"Add new instance {i}",
+        "pre_fn": create,
+        "exec_fn": start,
     },
-    "stop": {
-        "repr": "Stop",
-        "fn": stop,
+    STOP: {
+        "name": STOP,
+        "repr_fn": lambda i: f"Stop {i}",
+        "exec_fn": stop,
     },
-    "start": {
-        "repr": "Start",
-        "fn": start,
+    START: {
+        "name": START,
+        "repr_fn": lambda i: f"Start {i}",
+        "exec_fn": start,
     },
 }
 BASE = len(ACTIONS)
 
+SEEDLOG = "seeds.txt"
 
-def possible_actions(i: Instance | None):
-    if i is None:
-        return ["add"]
-    elif i.process is not None:
-        return ["stop"]
-    else:
-        return ["start"]
 
+def possible_actions(c: Cluster, istate):
+    actions = []
+    actions.append([ADD, None])
+    for i in c.instances:
+        if istate[i.instance_id]["started"]:
+            actions.append([STOP, i])
+        else:
+            actions.append([START, i])
+    return actions
 
-def choose_instance(cluster: Cluster):
-    if len(cluster.instances) < 2:
-        return None
-    new_instance_chance = random.randint(0, 3) % 3 == 0
-    if new_instance_chance:
-        return None
-    else:
-        return cluster.instances[random.randint(0, len(cluster.instances) - 1)]
 
-
-def choose_action(i: Instance | None):
-    actions = possible_actions(i)
-    return ACTIONS[actions[random.randint(0, len(actions) - 1)]]
+def choose_action(c: Cluster, istate):
+    actions = possible_actions(c, istate)
+    a = actions[random.randint(0, len(actions) - 1)]
+    return ACTIONS[a[0]], a[1]
 
 
 def step_msg(step: int, action, i: Instance):
-    if i is None:
-        msg = f"action {action['repr']}"
-    else:
-        msg = f"action {action['repr']} for {i.instance_id}"
+    msg = action["repr_fn"](i.instance_id if i and i.instance_id else None)
     return f"Step {step}: {msg}"
 
 
-def test_randomized(cluster: Cluster, seed: int):
-    seed = seed if seed else int_to_base_62(time.time_ns())
-    print(f"Seed: {seed}")
+def get_seed():
+    return int_to_base_62(time.time_ns())
+
+
+def log_seed(seed):
+    dir = pathlib.Path(__file__).parent.absolute()
+    with open(os.path.join(dir, SEEDLOG), "a") as f:
+        f.write(seed + "\n")
+
+
+def test_randomized(cluster: Cluster, seed: int, capsys):
+    cluster.deploy(instance_count=3)
+
+    seed = seed if seed else get_seed()
+    log_seed(seed)
+    with capsys.disabled():
+        print(f"Seed: {seed}")
+
     random.seed(seed)
 
-    steps_count = random.randint(1, 5)
+    steps_count = random.randint(5, 10)
     print(f"Do {steps_count} steps...")
 
+    istate = {}
+    for i in cluster.instances:
+        istate[i.instance_id] = {"instance": i, "started": True}
+
     for step in range(steps_count):
-        i = choose_instance(cluster)
-        a = choose_action(i)
-        print(step_msg(step, a, i))
-        a["fn"](cluster, i)
+        a, i = choose_action(cluster, istate)
+        if "pre_fn" in a.keys():
+            i, istate = a["pre_fn"](cluster, istate)
+        print(step_msg(step + 1, a, i))
+        _, istate = a["exec_fn"](i, istate)
+        time.sleep(0.1)
+
+    for instance_id in istate:
+        ist = istate[instance_id]
+        if ist["started"]:
+            ist["instance"].wait_ready()
-- 
GitLab