Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Browser performance test with locust #3887

Merged
merged 4 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ jobs:
EOF
sudo apt-get install firefox firefox-geckodriver

# Locust + Playwright setup
pip uninstall --yes trio
playwright install firefox

- name: Docker version info
run: |
docker --version
Expand Down
1 change: 1 addition & 0 deletions requirements/devel.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ isort==5.13.2
colorama
black==24.10.0
locust
locust-plugins[playwright]
parameterized
robotframework
robotframework-seleniumlibrary
Expand Down
97 changes: 97 additions & 0 deletions tests/performance/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,75 @@
# Licensed under GNU Affero General Public License v3 or later (AGPLv3+)
# https://www.gnu.org/licenses/agpl-3.0.html

import re

from locust import FastHttpUser, between, task
from locust_plugins.users.playwright import PlaywrightUser
from requests.exceptions import HTTPError
from requests.utils import dict_from_cookiejar


def response_time(request):
if request.timing["responseEnd"] > -1:
return request.timing["responseEnd"]

# this is already in milliseconds
return request.timing["responseStart"]


def exception_for_status(request, response):
http_error_msg = ""
if 400 <= response.status < 500:
http_error_msg = f"{response.status} Client Error: {response.status_text} for url: {request.url}"
elif 500 <= response.status < 600:
http_error_msg = f"{response.status} Server Error: {response.status_text} for url: {request.url}"

if http_error_msg:
return HTTPError(http_error_msg, response=response)

return request.failure


def log_response(user, response):
request = response.request

if not (request.url.startswith(user.host) or user.log_external):
return

response_length = 0
if response.headers and "content-length" in response.headers:
response_length = int(response.headers["content-length"])

# compress the actual URLs for more concise reports
target_url = request.url.replace(user.host, "")
if target_url.startswith("/static/"):
target_url = "/static/.../"
elif target_url.startswith("/cases/clone/?"):
target_url = "/cases/clone/"
elif re.match(r"/case/\d+/", target_url):
target_url = "/case/.../"
elif re.match(r"/plan/\d+/", target_url):
target_url = "/plan/.../"
elif re.match(r"/runs/\d+/", target_url):
target_url = "/runs/.../"
elif target_url.startswith("/runs/from-plan/"):
target_url = "/runs/from-plan/.../"

request_meta = {
"request_type": request.method,
"name": target_url,
"context": {**user.context()},
"response": response,
"response_length": response_length,
"start_time": request.timing["startTime"],
"response_time": response_time(request),
"url": request.url, # full URL
"exception": exception_for_status(request, response),
}

user.environment.events.request.fire(**request_meta)


class LoggedInTestCase(FastHttpUser):
abstract = True

Expand All @@ -15,6 +80,9 @@ class LoggedInTestCase(FastHttpUser):
login_url = "/accounts/login/"

def on_start(self):
self.do_login()

def do_login(self):
with self.client.get(self.login_url, catch_response=True):
cookies = dict_from_cookiejar(self.client.cookiejar)
csrf_middleware_token = cookies["csrftoken"]
Expand Down Expand Up @@ -44,6 +112,35 @@ def json_rpc(self, rpc_method, rpc_args):
return self.client.post("/json-rpc/", json=payload).json()["result"]


class BrowserTestCase(PlaywrightUser, LoggedInTestCase):
"""
Required setup:
pip uninstall trio
playwright install firefox
"""

abstract = True
browser_type = "firefox"
multiplier = 1
wait_time = between(1, 5)
session_cookie = None

log_external = True

async def setup_page(self, page):
cookies = dict_from_cookiejar(self.client.cookiejar)
await page.context.add_cookies(
[
{
"name": "sessionid",
"value": cookies["sessionid"],
"url": self.host,
}
]
)
page.on("response", lambda response: log_response(self, response))


class ExampleTestCase(LoggedInTestCase):
wait_time = between(1, 5)

Expand Down
Loading
Loading