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

BenchWeb/benchmarks add #1

Open
wants to merge 4 commits into
base: BenchWeb/frameworks
Choose a base branch
from
Open
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
20 changes: 20 additions & 0 deletions benchmarks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import importlib
import re

from glob import glob

test_types = {}
test_type_folders = glob("/BenchWeb/benchmarks/*/")

# Loads all the test_types from the folders in this directory
for folder in test_type_folders:
# regex that grabs the characters between "benchmarks/"
# and the final "/" in the folder string to get the name
test_type_name = re.findall(r'.+\/(.+)\/$', folder, re.M)[0]
# ignore generated __pycache__ folder
if test_type_name == '__pycache__':
continue
spec = importlib.util.spec_from_file_location("TestType", "%s%s.py" % (folder, test_type_name))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Replace interpolated string formatting with f-string (replace-interpolation-with-fstring)

Suggested change
spec = importlib.util.spec_from_file_location("TestType", "%s%s.py" % (folder, test_type_name))
spec = importlib.util.spec_from_file_location(
"TestType", f"{folder}{test_type_name}.py"
)

test_type = importlib.util.module_from_spec(spec)
spec.loader.exec_module(test_type)
test_types[test_type_name] = test_type.TestType
132 changes: 132 additions & 0 deletions benchmarks/abstract_test_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import abc
import copy
import requests

from colorama import Fore
from utils.output_helper import log

class AbstractTestType(metaclass=abc.ABCMeta):
'''
Interface between a test type (json, query, plaintext, etc) and
the rest of BW. A test type defines a number of keys it expects
to find in the benchmark_config.json, and this base class handles extracting
those keys and injecting them into the test. For example, if
benchmark_config.json contains a line `"spam" : "foobar"` and a subclasses X
passes an argument list of ['spam'], then after parsing there will
exist a member `X.spam = 'foobar'`.
'''

def __init__(self,
config,
name,
requires_db=False,
accept_header=None,
args=[]):
Comment on lines +19 to +24
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Replace mutable default arguments with None (default-mutable-arg)

Suggested change
def __init__(self,
config,
name,
requires_db=False,
accept_header=None,
args=[]):
def __init__(self, config, name, requires_db=False, accept_header=None, args=None):
if args is None:
args = []

self.config = config
self.name = name
self.requires_db = requires_db
self.args = args
self.headers = ""
self.body = ""

if accept_header is None:
self.accept_header = self.accept('json')
else:
self.accept_header = accept_header

self.passed = None
self.failed = None
self.warned = None

@classmethod
def accept(self, content_type):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): The first argument to class methods should be cls (class-method-first-arg-name)

Suggested change
def accept(self, content_type):
def accept(cls, content_type):

return {
'json':
'application/json,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7',
'html':
'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'plaintext':
'text/plain,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7'
}[content_type]

def parse(self, test_keys):
'''
Takes the dict of key/value pairs describing a FrameworkTest
and collects all variables needed by this AbstractTestType

Raises AttributeError if required keys are missing
'''
if all(arg in test_keys for arg in self.args):
self.__dict__.update({arg: test_keys[arg] for arg in self.args})
return self
else: # This is quite common - most tests don't support all types
raise AttributeError(
"A %s requires the benchmark_config.json to contain %s" %
(self.name, self.args))
Comment on lines +64 to +65
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Replace interpolated string formatting with f-string (replace-interpolation-with-fstring)

Suggested change
"A %s requires the benchmark_config.json to contain %s" %
(self.name, self.args))
f"A {self.name} requires the benchmark_config.json to contain {self.args}"
)


def request_headers_and_body(self, url):
'''
Downloads a URL and returns the HTTP response headers
and body content as a tuple
'''
log("Accessing URL {!s}: ".format(url), color=Fore.CYAN)

headers = {'Accept': self.accept_header}
r = requests.get(url, timeout=15, headers=headers)

self.headers = r.headers
self.body = r.content
return self.headers, self.body

def output_headers_and_body(self):
log(str(self.headers))
log(self.body)

def verify(self, base_url):
'''
Accesses URL used by this test type and checks the return
values for correctness. Most test types run multiple checks,
so this returns a list of results. Each result is a 3-tuple
of (String result, String reason, String urlTested).

- result : 'pass','warn','fail'
- reason : Short human-readable reason if result was
warn or fail. Please do not print the response as part of this,
other parts of BW will do that based upon the current logging
settings if this method indicates a failure happened
- urlTested: The exact URL that was queried

Subclasses should make a best-effort attempt to report as many
failures and warnings as they can to help users avoid needing
to run BW repeatedly while debugging
'''
# TODO make String result into an enum to enforce
raise NotImplementedError("Subclasses must provide verify")

def get_url(self):
'''
Returns the URL for this test, like '/json'
'''
# This is a method because each test type uses a different key
# for their URL so the base class can't know which arg is the URL
raise NotImplementedError("Subclasses must provide get_url")

def get_script_name(self):
'''
Returns the remote script name for running the benchmarking process.
'''
raise NotImplementedError("Subclasses must provide get_script_name")

def get_script_variables(self, name, url, port):
'''
Returns the remote script variables for running the benchmarking process.
'''
raise NotImplementedError(
"Subclasses must provide get_script_variables")

def copy(self):
'''
Returns a copy that can be safely modified.
Use before calling parse
'''
return copy.copy(self)
Loading