Remove WIP performance tests - they were never used and are outdated (#20471)

This commit is contained in:
Mathieu Pillard 2023-03-15 11:40:37 +01:00 коммит произвёл GitHub
Родитель 1f247e0d8f
Коммит 37f65c46b8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 0 добавлений и 645 удалений

Просмотреть файл

@ -1,11 +0,0 @@
FROM python:2.7-alpine
COPY . /code
WORKDIR /code
RUN apk --no-cache add --virtual=.build-dep build-base git \
&& pip install --no-cache-dir -r /code/requirements/perftests.txt \
&& apk del .build-dep \
&& rm -f /tmp/* /etc/apk/cache/*
EXPOSE 8089 5557 5558

Просмотреть файл

@ -1,7 +0,0 @@
# Required by locust, not using hashes for now. We'll use them once we can
# use a tagged locust release
pyzmq==25.0.0
# We need this specific commit until there is a new locust release. Once this happens
# This can be pinned to a specific version.
-e git+https://github.com/locustio/locust@524ab5203ebc7c4c5c108b641773262ae53fbc40#egg=locustio

Просмотреть файл

Двоичный файл не отображается.

Просмотреть файл

@ -1,146 +0,0 @@
import logging
import random
import collections
import os
import string
import re
import tempfile
import uuid
from contextlib import contextmanager
from shutil import make_archive, rmtree
from zipfile import ZipFile
import lxml
from fxa.constants import ENVIRONMENT_URLS
from fxa.core import Client
from fxa.tests.utils import TestEmailAccount
NAME_REGEX = re.compile('THIS_IS_THE_NAME')
root_path = os.path.dirname(__file__)
data_dir = os.path.join(root_path, 'fixtures')
xpis = [os.path.join(data_dir, xpi) for xpi in os.listdir(data_dir)]
log = logging.getLogger(__name__)
def get_random():
return str(uuid.uuid4())
def submit_url(step):
return f'/en-US/developers/addon/submit/{step}/'
def get_xpi():
return uniqueify_xpi(random.choice(xpis))
@contextmanager
def uniqueify_xpi(path):
output_dir = tempfile.mkdtemp()
try:
data_dir = os.path.join(output_dir, 'xpi')
output_path = os.path.join(output_dir, 'addon')
xpi_name = os.path.basename(path)
xpi_path = os.path.join(output_dir, xpi_name)
with ZipFile(path) as original:
original.extractall(data_dir)
with open(os.path.join(data_dir, 'manifest.json')) as f:
manifest_json = f.read()
manifest_json = NAME_REGEX.sub(get_random(), manifest_json)
with open(os.path.join(data_dir, 'manifest.json'), 'w') as f:
f.write(manifest_json)
archive_path = make_archive(output_path, 'zip', data_dir)
os.rename(archive_path, xpi_path)
with open(xpi_path) as f:
yield f
finally:
rmtree(output_dir)
class EventMarker:
"""
Simple event marker that logs on every call.
"""
def __init__(self, name):
self.name = name
def _generate_log_message(self):
log.info(f'locust event: {self.name}')
def __call__(self, *args, **kwargs):
self._generate_log_message()
def install_event_markers():
# "import locust" within this scope so that this module is importable by
# code running in environments which do not have locust installed.
import locust
# The locust logging format is not necessarily stable, so we use the event
# hooks API to implement our own "stable" logging for later programmatic
# reference.
# The events are:
# * locust_start_hatching
# * master_start_hatching
# * quitting
# * hatch_complete
# install simple event markers
locust.events.locust_start_hatching += EventMarker('locust_start_hatching')
locust.events.master_start_hatching += EventMarker('master_start_hatching')
locust.events.quitting += EventMarker('quitting')
locust.events.hatch_complete += EventMarker('hatch_complete')
def get_fxa_client():
fxa_env = os.getenv('FXA_ENV', 'stable')
return Client(ENVIRONMENT_URLS[fxa_env]['authentication'])
def get_fxa_account():
fxa_client = get_fxa_client()
account = TestEmailAccount()
password = ''.join([random.choice(string.ascii_letters) for i in range(8)])
FxAccount = collections.namedtuple('FxAccount', 'email password')
fxa_account = FxAccount(email=account.email, password=password)
session = fxa_client.create_account(fxa_account.email, fxa_account.password)
account.fetch()
message = account.wait_for_email(lambda m: 'x-verify-code' in m['headers'])
session.verify_email_code(message['headers']['x-verify-code'])
return fxa_account, account
def destroy_fxa_account(fxa_account, email_account):
email_account.clear()
get_fxa_client().destroy_account(fxa_account.email, fxa_account.password)
def get_the_only_form_without_id(response_content):
"""
Gets the only form on the page that doesn't have an ID.
A lot of pages (login, registration) have a single form without an ID.
This is the one we want. The other forms on the page have IDs so we
can ignore them. I'm sure this will break one day.
"""
html = lxml.html.fromstring(response_content)
target_form = None
for form in html.forms:
if not form.attrib.get('id'):
target_form = form
if target_form is None:
raise ValueError(
f'Could not find only one form without an ID; found: {html.forms}'
)
return target_form

Просмотреть файл

@ -1,34 +0,0 @@
import logging
import os
import sys
import time
# due to locust sys.path manipulation, we need to re-add the project root.
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
import olympia # noqa
from locust import HttpLocust # noqa
import tasks.user # noqa
import helpers # noqa
logging.Formatter.converter = time.gmtime
log = logging.getLogger(__name__)
helpers.install_event_markers()
class WebsiteUser(HttpLocust):
weight = 1
task_set = tasks.user.UserTaskSet
min_wait = 120
max_wait = 240
# class Developer(HttpLocust):
# weight = 10
# task_set = tasks.developer.DeveloperTaskSet
# min_wait = 120
# max_wait = 240

Просмотреть файл

@ -1,45 +0,0 @@
#!/bin/sh
# Example usage:
#
# $ docker build -t amoloadtests:latest -f Dockerfile.perftests
# $ docker run -ti -e LOCUST_OPTS="-c 1 --no-web" \
# -e ATTACKED_HOST="https://addons.allizom.org" \
# amoloadtests:latest
# /code/tests/performance/run-locust.sh
#
# If you're running this against addons.allizom.org make sure to configure
# the correct FxA environment variables:
#
# -e FXA_CLIENT_ID="..." -e FXA_CLIENT_SECRET="..."
#
# To run this locally, use
# $ LOCUST_OPTS="-c 3 --no-web" ATTACKED_HOST="https://addons.allizom.org" tests/performance/run-locust.sh
set -e
LOCUST_MODE=${LOCUST_MODE:-standalone}
LOCUST_MASTER_BIND_PORT=${LOCUST_MASTER_BIND_PORT:-5557}
CURRENT_FOLDER=$(dirname $(realpath $0))
DEFAULT_LOCUST_FILE="$CURRENT_FOLDER/locustfile.py"
LOCUST_FILE=${LOCUST_FILE:-$DEFAULT_LOCUST_FILE}
if [ -z ${ATTACKED_HOST+x} ] ; then
echo "You need to set the URL of the host to be tested (ATTACKED_HOST)."
exit 1
fi
LOCUST_OPTS="-f ${LOCUST_FILE} --host=${ATTACKED_HOST} --no-reset-stats $LOCUST_OPTS"
case `echo ${LOCUST_MODE} | tr 'a-z' 'A-Z'` in
"MASTER")
LOCUST_OPTS="--master --master-bind-port=${LOCUST_MASTER_BIND_PORT} $LOCUST_OPTS"
;;
"SLAVE")
LOCUST_OPTS="--slave --master-host=${LOCUST_MASTER} --master-port=${LOCUST_MASTER_BIND_PORT} $LOCUST_OPTS"
if [ -z ${LOCUST_MASTER+x} ] ; then
echo "You need to set LOCUST_MASTER."
exit 1
fi
;;
esac
locust ${LOCUST_OPTS}

Просмотреть файл

@ -1 +0,0 @@
from . import developer, user # noqa

Просмотреть файл

@ -1,130 +0,0 @@
import os
import logging
import gevent
from django.conf import settings
from locust import task
import lxml.html
from lxml.html import submit_form
import helpers
from .user import BaseUserTaskSet
log = logging.getLogger(__name__)
MAX_UPLOAD_POLL_ATTEMPTS = 200
FXA_CONFIG = settings.FXA_CONFIG[settings.DEFAULT_FXA_CONFIG_NAME]
class DeveloperTaskSet(BaseUserTaskSet):
def submit_form(self, form=None, url=None, extra_values=None):
if form is None:
raise ValueError(f'form cannot be None; url={url}')
def submit(method, form_action_url, values):
values = dict(values)
if 'csrfmiddlewaretoken' not in values:
raise ValueError(
'Possibly the wrong form. Could not find '
'csrfmiddlewaretoken: {}'.format(repr(values))
)
response = self.client.post(
url or form_action_url,
values,
allow_redirects=False,
catch_response=True,
)
if response.status_code not in (301, 302):
# This probably means the form failed and is displaying
# errors.
response.failure(
'Form submission did not redirect; status={}'.format(
response.status_code
)
)
return submit_form(form, open_http=submit, extra_values=extra_values)
def load_upload_form(self):
url = helpers.submit_url('upload-unlisted')
response = self.client.get(url, allow_redirects=False, catch_response=True)
if response.status_code == 200:
response.success()
html = lxml.html.fromstring(response.content)
return html.get_element_by_id('create-addon')
else:
more_info = ''
if response.status_code in (301, 302):
more_info = 'Location: {}'.format(response.headers['Location'])
response.failure(f'Unexpected status: {response.status_code}; {more_info}')
def upload_addon(self, form):
url = helpers.submit_url('upload-unlisted')
csrfmiddlewaretoken = form.fields['csrfmiddlewaretoken']
with helpers.get_xpi() as addon_file:
response = self.client.post(
'/en-US/developers/upload/',
{'csrfmiddlewaretoken': csrfmiddlewaretoken},
files={'upload': addon_file},
name=f'devhub.upload {os.path.basename(addon_file.name)}',
allow_redirects=False,
catch_response=True,
)
if response.status_code == 302:
poll_url = response.headers['location']
upload_uuid = gevent.spawn(self.poll_upload_until_ready, poll_url).get()
if upload_uuid:
form.fields['upload'] = upload_uuid
self.submit_form(form=form, url=url)
else:
response.failure(f'Unexpected status: {response.status_code}')
@task(1)
def upload(self):
self.login(self.fxa_account)
form = self.load_upload_form()
if form:
self.upload_addon(form)
self.logout(self.fxa_account)
def poll_upload_until_ready(self, url):
for i in range(MAX_UPLOAD_POLL_ATTEMPTS):
response = self.client.get(
url,
allow_redirects=False,
name='/en-US/developers/upload/:uuid',
catch_response=True,
)
try:
data = response.json()
except ValueError:
return response.failure(
'Failed to parse JSON when polling. '
'Status: {} content: {}'.format(
response.status_code, response.content
)
)
if response.status_code == 200:
if data['error']:
return response.failure(
'Unexpected error: {}'.format(data['error'])
)
elif data['validation']:
response.success()
return data['upload']
else:
return response.failure(f'Unexpected status: {response.status_code}')
gevent.sleep(1)
else:
response.failure(
f'Upload did not complete in {MAX_UPLOAD_POLL_ATTEMPTS} tries'
)

Просмотреть файл

@ -1,271 +0,0 @@
import logging
import urlparse
import random
from django.conf import settings
from locust import TaskSet, task
import lxml.html
from fxa import oauth as fxa_oauth
import helpers
log = logging.getLogger(__name__)
MAX_UPLOAD_POLL_ATTEMPTS = 200
FXA_CONFIG = settings.FXA_CONFIG[settings.DEFAULT_FXA_CONFIG_NAME]
class BaseUserTaskSet(TaskSet):
def on_start(self):
self.fxa_account, self.email_account = helpers.get_fxa_account()
log.info(f'Created {self.fxa_account} for load-tests')
def is_legacy_page(self, app):
return app in ('thunderbird', 'seamonkey')
def get_app(self):
# Slightly weighted
app = random.choice(['firefox'] * 20 + ['thunderbird'] * 5 + ['seamonkey'] * 1)
return app
def get_url(self, url, app):
# Only take a sub-set of languages, doesn't really matter only
# increases variance and may circumvent some caches here and there
user_language = random.choice(
('af', 'de', 'dsb', 'en-US', 'hsb', 'ru', 'tr', 'zh-CN', 'zh-TW')
)
return url.format(app=app, language=user_language)
def on_stop(self):
log.info(f'Cleaning up and destroying {self.fxa_account}')
helpers.destroy_fxa_account(self.fxa_account, self.email_account)
def login(self, fxa_account):
log.info('calling login/start to generate fxa_state')
response = self.client.get(
'/api/v3/accounts/login/start/', allow_redirects=True
)
params = dict(urlparse.parse_qsl(response.url))
fxa_state = params['state']
log.info('Get browser id session token')
fxa_session = helpers.get_fxa_client().login(
email=fxa_account.email, password=fxa_account.password
)
oauth_client = fxa_oauth.Client(
client_id=FXA_CONFIG['client_id'],
client_secret=FXA_CONFIG['client_secret'],
server_url=FXA_CONFIG['oauth_host'],
)
log.info('convert browser id session token into oauth code')
oauth_code = oauth_client.authorize_code(fxa_session, scope='profile')
# Now authenticate the user, this will verify the user on the server
response = self.client.get(
'/api/v3/accounts/authenticate/',
params={
'state': fxa_state,
'code': oauth_code,
},
name='/api/v3/accounts/authenticate/?state=:state',
)
def logout(self, account):
log.info(f'Logging out {account}')
self.client.get('/en-US/firefox/users/logout/')
class UserTaskSet(BaseUserTaskSet):
def _browse_listing_and_click_detail(
self,
listing_url,
app,
detail_selector,
legacy_selector=None,
name=None,
force_legacy=False,
):
# TODO: This should hit pagination automatically if there is any
response = self.client.get(
self.get_url(listing_url, app), allow_redirects=False, catch_response=True
)
if (self.is_legacy_page(app) or force_legacy) and not legacy_selector:
log.warning(
'Received legacy url without legacy selector. {} :: {}'.format(
listing_url, detail_selector
)
)
return
if response.status_code == 200:
html = lxml.html.fromstring(response.content)
selector = (
detail_selector
if not (self.is_legacy_page(app) or force_legacy)
else legacy_selector
)
collection_links = html.cssselect(selector)
if not collection_links:
log.warning(
'No selectable links on page. {} :: {}'.format(
listing_url, selector
)
)
url = random.choice(collection_links).get('href')
kwargs = {}
if name is not None:
if self.is_legacy_page(app) or force_legacy:
name = name.replace(':app', ':legacy_app')
kwargs['name'] = name
self.client.get(url, **kwargs)
response.success()
else:
response.failure(f'Unexpected status code {response.status_code}')
@task(1)
def browse(self):
app = self.get_app()
self.client.get(self.get_url('/{language}/{app}/', app))
self._browse_listing_and_click_detail(
listing_url='/{language}/{app}/extensions/',
app=app,
detail_selector='a.SearchResult-link',
legacy_selector='.items .item.addon a',
)
@task(10)
def search(self):
app = self.get_app()
term_choices = ('Spam', 'Privacy', 'Download')
term = random.choice(term_choices)
self.client.get(
self.get_url('/{language}/{app}/search/?platform=linux&q=' + term, app)
)
@task(6)
def browse_and_download_addon(self):
app = self.get_app()
self._browse_listing_and_click_detail(
listing_url='/{language}/{app}/extensions/',
app=app,
detail_selector='a.SearchResult-link',
legacy_selector='.items .item.addon a',
)
@task(5)
def browse_collections(self):
app = self.get_app()
# detail and legacy selector match both, themes and regular add-ons
self._browse_listing_and_click_detail(
listing_url='/{language}/{app}/',
app=app,
detail_selector='a.Home-SubjectShelf-link',
legacy_selector='.listing-grid .hovercard .summary>a',
)
@task(4)
def browse_categories(self):
app = self.get_app()
self._browse_listing_and_click_detail(
listing_url='/{language}/{app}/extensions/',
app=app,
detail_selector='a.Categories-link',
legacy_selector='ul#side-categories li a',
)
@task(4)
def browse_reviews(self):
app = self.get_app()
# TODO: Get add-ons more generalized by looking at collections
# pages but for now that'll suffice.
addons = (
'grammarly-spell-checker',
'clip-to-onenote',
'evernote-web-clipper',
'reader',
'fractal-summer-colors',
'abstract-splash',
'colorful-fractal',
'tab-mix-plus',
)
for addon in addons:
self.client.get(
self.get_url('/{language}/{app}/addon/%s/reviews/' % addon, app)
)
@task(4)
def browse_theme_categories(self):
app = self.get_app()
self._browse_listing_and_click_detail(
listing_url='/{language}/{app}/complete-themes/',
app=app,
detail_selector=None,
legacy_selector='.listing-grid .hovercard>a',
name='/:lang/:app/complete-themes/:slug/',
force_legacy=True,
)
self._browse_listing_and_click_detail(
listing_url='/{language}/{app}/themes/',
app=app,
detail_selector='a.SearchResult-link',
legacy_selector='ul#side-categories li a',
name='/:lang/:app/themes/:slug/',
)
@task(3)
def test_user_profile(self):
app = self.get_app()
# TODO: Generalize by actually creating a user-profile and uploading
# some data.
usernames = (
'giorgio-maone',
'wot-services',
'onemen',
'gary-reyes',
'mozilla-labs5133025',
'gregglind',
# Has many ratings
'daveg',
)
for user in usernames:
self.client.get(self.get_url('/{language}/{app}/user/%s/' % user, app))
@task(2)
def test_rss_feeds(self):
app = self.get_app()
urls = (
# Add-on Category RSS Feed
'/{language}/firefox/extensions/alerts-updates/format:rss',
'/{language}/firefox/extensions/appearance/format:rss',
'/{language}/firefox/extensions/bookmarks/format:rss',
'/{language}/firefox/extensions/language-support/format:rss',
# Collection RSS Feed
'/{language}/firefox/collections/Vivre/ploaia/format:rss',
# Featured Add-ons
'/{language}/{app}/featured/format:rss',
# Search tools RSS Feed
'/{language}/{app}/search-tools/format:rss',
)
self.client.get(self.get_url(random.choice(urls), app))