Skip to content

Commit

Permalink
Merge pull request #7752 from drew2a/refactoring/AT
Browse files Browse the repository at this point in the history
Add ActionSelector class for selecting random actions
  • Loading branch information
drew2a committed Dec 5, 2023
2 parents 6f554e4 + 6a94861 commit 3c74b3e
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 104 deletions.
39 changes: 39 additions & 0 deletions scripts/application_tester/tribler_apptester/action_selector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import random
from pathlib import Path
from typing import Optional

from tribler_apptester.action import Action
from tribler_apptester.actions.change_anonymity_action import ChangeAnonymityAction
from tribler_apptester.actions.change_download_files_action import ChangeDownloadFilesAction
from tribler_apptester.actions.explore_download_action import ExploreDownloadAction
from tribler_apptester.actions.page_action import RandomPageAction
from tribler_apptester.actions.remove_download_action import RemoveRandomDownloadAction
from tribler_apptester.actions.screenshot_action import ScreenshotAction
from tribler_apptester.actions.search_action import RandomSearchAction
from tribler_apptester.actions.start_download_action import StartRandomDownloadAction
from tribler_apptester.actions.start_vod_action import StartVODAction
from tribler_apptester.actions.test_exception import TestExceptionAction


class ActionSelector:
""" This class is responsible for selecting a random action based on the probabilities given to each action."""

def __init__(self):
self.actions_with_probabilities = {
'test_exception': (lambda: TestExceptionAction(), 0),
'random_page': (lambda: RandomPageAction(), 20),
'search': (lambda: RandomSearchAction(), 15),
'start_download': (lambda: StartRandomDownloadAction(Path(__file__).parent / "data/torrent_links.txt"), 15),
'remove_download': (lambda: RemoveRandomDownloadAction(), 5),
'explore_download': (lambda: ExploreDownloadAction(), 10),
'screenshot': (lambda: ScreenshotAction(), 5),
'start_vod': (lambda: StartVODAction(), 5),
'change_anonymity': (lambda: ChangeAnonymityAction(allow_plain=True), 5),
'change_download_files': (lambda: ChangeDownloadFilesAction(), 10)
}

def get_random_action_with_probability(self) -> Optional[Action]:
""" Returns a random action based on the probabilities given to each action."""
actions, probabilities = zip(*self.actions_with_probabilities.values())
choices = random.choices(actions, weights=probabilities, k=1)
return choices[0]() if choices else None

This file was deleted.

107 changes: 12 additions & 95 deletions scripts/application_tester/tribler_apptester/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,13 @@
import time
from asyncio import Future, Task, create_task, get_event_loop, sleep, wait_for
from base64 import b64encode
from bisect import bisect
from distutils.version import LooseVersion
from pathlib import Path
from random import choice, randint, random
from random import choice
from typing import Dict, Optional

from tribler.core.config.tribler_config import TriblerConfig
from tribler_apptester.actions.change_anonymity_action import ChangeAnonymityAction
from tribler_apptester.actions.change_download_files_action import ChangeDownloadFilesAction
from tribler_apptester.actions.explore_download_action import ExploreDownloadAction
from tribler_apptester.actions.page_action import RandomPageAction
from tribler_apptester.actions.remove_download_action import RemoveRandomDownloadAction
from tribler_apptester.actions.screenshot_action import ScreenshotAction
from tribler_apptester.actions.search_action import RandomSearchAction
from tribler_apptester.action_selector import ActionSelector
from tribler_apptester.actions.shutdown_action import ShutdownAction
from tribler_apptester.actions.start_download_action import StartRandomDownloadAction
from tribler_apptester.actions.start_vod_action import StartVODAction
from tribler_apptester.actions.test_exception import TestExceptionAction
from tribler_apptester.actions.wait_action import WaitAction
from tribler_apptester.monitors.download_monitor import DownloadMonitor
from tribler_apptester.monitors.ipv8_monitor import IPv8Monitor
from tribler_apptester.monitors.resource_monitor import ResourceMonitor
Expand Down Expand Up @@ -58,7 +46,6 @@ def __init__(self, args, read_config_delay=2, read_config_attempts=10, check_pro
self._logger = logging.getLogger(self.__class__.__name__)
self.allow_plain_downloads = args.plain
self.pending_tasks: Dict[bytes, Future] = {} # Dictionary of pending tasks
self.probabilities = []
self.apptester_start_time = time.time()
self.tribler_start_time = None
self.tribler_is_running = False
Expand All @@ -76,6 +63,7 @@ def __init__(self, args, read_config_delay=2, read_config_attempts=10, check_pro

self.tribler_config: Optional[TriblerConfig] = None
self.request_manager = None
self.action_selector = ActionSelector()

async def start(self):
await self.start_tribler()
Expand Down Expand Up @@ -211,13 +199,11 @@ async def load_tribler_config(self):
self._logger.info(f"Loaded API key: {config.api.key}")
return True

return True
return False

def start_testing(self):
self._logger.info("Opening Tribler code socket connection to port %d" % self.code_client.port)

self.determine_probabilities()

self.start_monitors()

if not self.args.silent:
Expand All @@ -227,7 +213,7 @@ async def do_testing(self):
await asyncio.sleep(ACTIONS_WARMUP_DELAY)

while not self.shutting_down:
await self.perform_random_action()
self.perform_random_action()
if self.shutting_down:
break
await asyncio.sleep(DELAY_BETWEEN_ACTIONS)
Expand All @@ -236,24 +222,12 @@ async def do_testing(self):

if self.tribler_is_running and not self.tribler_crashed:
self._logger.info("Executing Shutdown action")
await self.perform_action(ShutdownAction())

def determine_probabilities(self):
self._logger.info("Determining probabilities of actions")
with open(Path(__file__).parent / "data/action_weights.txt", mode="r", encoding='utf-8') as f:
content = f.read()
for line in content.split('\n'):
if len(line) == 0:
continue

if line.startswith('#'):
continue

parts = line.split('=')
if len(parts) < 2:
continue
self.execute_action(ShutdownAction())

self.probabilities.append((parts[0], int(parts[1])))
def perform_random_action(self):
if action := self.action_selector.get_random_action_with_probability():
self._logger.info(f"Random action: {action}")
self.execute_action(action)

def start_monitors(self):
if self.args.monitordownloads:
Expand Down Expand Up @@ -357,31 +331,14 @@ def on_tribler_crash(self, traceback):
task.set_result(None) # should set exception instead, but it requries further refactoring
create_task(self.stop(1))

def weighted_choice(self, choices):
if len(choices) == 0:
return None
values, weights = zip(*choices)
total = 0
cum_weights = []
for w in weights:
total += w
cum_weights.append(total)
x = random() * total
i = bisect(cum_weights, x)
return values[i]

def get_rand_bool(self):
return randint(0, 1) == 0

def execute_action(self, action):
"""
Execute a given action and return a Future that fires with the result of the action.
"""
self._logger.info("Executing action: %s" % action)
self._logger.info(f"Executing action: {action}")

task_id = ''.join(choice('0123456789abcdef') for _ in range(10)).encode('utf-8')
task_future = Future()
self.pending_tasks[task_id] = task_future
self.pending_tasks[task_id] = Future()

code = """return_value = ''
app_tester_dir = %r
Expand All @@ -399,46 +356,6 @@ def exit_script():
# Let Tribler execute this code
self.execute_code(base64_code, task_id)

return task_future

def execute_code(self, base64_code, task_id):
self._logger.info("Executing code with task id: %s" % task_id.decode('utf-8'))
self.code_client.run_code(base64_code, task_id)

def get_random_action(self):
"""
This method returns a random action in Tribler.
There are various actions possible that can occur with different probabilities.
"""
action_name = self.weighted_choice(self.probabilities)
self._logger.info("Random action: %s", action_name)
actions = {
'test_exception': TestExceptionAction(),
'random_page': RandomPageAction(),
'search': RandomSearchAction(),
'start_download': StartRandomDownloadAction(Path(__file__).parent / "data/torrent_links.txt"),
'remove_download': RemoveRandomDownloadAction(),
'explore_download': ExploreDownloadAction(),
'screenshot': ScreenshotAction(),
'start_vod': StartVODAction(),
'change_anonymity': ChangeAnonymityAction(allow_plain=self.allow_plain_downloads),
'change_download_files': ChangeDownloadFilesAction()
}
return actions.get(action_name)

async def perform_random_action(self):
action = self.get_random_action()
await self.perform_action(action)

def perform_action(self, action) -> Future:
if not self.tribler_is_running:
msg = "Cannot execute action: Tribler is not running"
self._logger.error(msg)
raise RuntimeError(msg)
try:
return self.execute_action(action)
except Exception as e:
self._logger.exception(e)
dummy_future = Future()
dummy_future.set_result(None)
return dummy_future

0 comments on commit 3c74b3e

Please sign in to comment.