#! /usr/bin/env python2
#
# Main driver for benchmarks
#
# Copyright (C) 2016, Robert Cowham, Perforce
#
from __future__ import print_function
import logging
import os
import P4
import platform
import sys
import random
import string
import subprocess
from argparse import ArgumentParser
import yaml
SVN = "svn" # SVN command
def readConfig(startdir):
config = {}
with open(os.path.join(startdir, "benchmark.yaml"), "r") as f:
config = yaml.load(f)
return config
logger = logging.getLogger("console_logger")
if not (0x02070000 <= sys.hexversion < 0x02080000):
sys.exit("Python 2.7 is required to run this program.")
LINE_LENGTH = 80
BLOCK_SIZE = 65536
# Random generators
try:
import numpy as np
def create_random(size):
return bytes((int(x) for x in np.random.random_integers(1, 254, size)))
def generator(size, eol="\n"):
s = string.ascii_letters + string.digits
return "".join(np.random.choice(list(s), size - 1)) + eol
except ImportError:
print("No numpy installed, falling back to standard Python random. Prepare to wait ...", file=sys.stderr)
def create_random(size):
return bytes((random.randint(1,254) for x in range(size)))
def generator(size, eol="\n"):
s = string.ascii_letters + string.digits
return "".join((random.choice(s) for x in range(size - 1))) + eol
def create_file(fileSize, filename, binary=False):
mode = "wb" if binary else "w"
with open(filename, mode) as f:
if binary:
blocks = int(fileSize / BLOCK_SIZE)
for unused in range(blocks):
b = create_random(BLOCK_SIZE)
f.write( b )
rest = fileSize % BLOCK_SIZE
if rest:
b = create_random(rest)
f.write( b )
else:
lines = int(fileSize / LINE_LENGTH)
for unused in range(lines):
f.write(generator(LINE_LENGTH))
rest = fileSize % LINE_LENGTH
if rest:
f.write(generator(rest))
class RepoBenchmark(object):
"""Generic benchmarking class - must be subclassed"""
def __init__(self, startdir):
self.id = id(self) # Object id - unique enough for now.
self.root = os.path.join(startdir, "testdir")
self.localfilelist = []
def createWorkspace(self, name):
raise NotImplementedError("createWorkspace")
def syncWorkspace(self):
raise NotImplementedError("syncWorkspace")
def addFile(self, filename):
raise NotImplementedError("addFile")
def editFile(self, filename):
raise NotImplementedError("editFile")
def deleteFile(self, filename):
raise NotImplementedError("deleteFile")
def basicFileActions(self):
"""Randomly edit/delete/add files in workspace"""
for i in range(random.randint(1, 20)):
action = random.choice(["add", "edit", "edit", "edit", "edit", "delete"])
filename = random.choice(self.localfilelist)
if action == "add":
addfilename = os.path.join(os.path.dirname(filename), generator(20, eol=""))
create_file(random.randint(100, 1000000), addfilename, random.choice([True, False]))
self.addFile(addfilename)
elif action == "edit":
self.editFile(filename)
with open(filename, "a") as f:
f.write("Some extra data\n")
else:
self.deleteFile(filename)
def commit(self):
"""Commit/Submit changes"""
raise NotImplementedError("commit")
def run(self):
"""Run the appropriate test - basically a sync/edit/add/delete and submit"""
try:
self.createWorkspace("bench_ws")
self.syncWorkspace()
self.basicFileActions()
self.commit()
except Exception as e:
logger.exception(e)
raise e
class P4Benchmark(RepoBenchmark):
"""Performs basic benchmark test - Perforce specific subclass"""
def __init__(self, startdir, config):
super(P4Benchmark, self).__init__(startdir)
self.p4 = P4.P4()
self.p4.logger = logger
p4config = config["perforce"]
self.p4.port = p4config["port"]
self.repoPath = p4config["repoPath"]
self.p4.user = p4config["user"]
logger.debug(self.p4.connect())
if p4config["password"]:
self.p4.run_login(p4config["password"])
def createWorkspace(self, name):
if not os.path.isdir(self.root):
os.makedirs(self.root, 0o777)
self.workspace_name = "{}.{}.{}".format(self.p4.user, self.id, platform.node())
ws = self.p4.fetch_client(self.workspace_name)
ws._root = os.path.join(self.root, self.workspace_name)
ws._view = ["//depot/Jam/MAIN/... //{}/...".format(self.workspace_name)]
ws._options = ws._options.replace("normdir", "rmdir")
result = self.p4.save_client(ws)
self.p4.client = self.workspace_name
return result
def syncWorkspace(self):
with self.p4.at_exception_level(P4.P4.RAISE_ERRORS):
self.p4.run_sync()
havelist = self.p4.run_have()
self.localfilelist = [f["path"] for f in havelist]
def addFile(self, filename):
self.p4.run_add(filename)
def editFile(self, filename):
self.p4.run_edit(filename)
def deleteFile(self, filename):
self.p4.run_delete(filename)
def commit(self):
"""Submit our change"""
if len(self.p4.run_opened()) > 0:
chg = self.p4.fetch_change()
chg._description = "A test change"
self.p4.save_submit(chg)
class SvnBenchmark(RepoBenchmark):
"""Performs basic benchmark test - SVN specific subclass"""
def __init__(self, startdir, config):
super(SvnBenchmark, self).__init__(startdir)
svnconfig = config["svn"]
self.serverURL = svnconfig["serverURL"]
self.repoPath = svnconfig["repoPath"]
self.fullURL = "{}/{}".format(self.serverURL, self.repoPath)
self.user = svnconfig["user"]
self.password = svnconfig["password"]
def svncmd(self, cmd, args, special = []):
command = [SVN, cmd]
for arg in args:
command.append(arg)
print("cmd:", command)
logger.info(", ".join(command))
p = subprocess.Popen(command, stdout = subprocess.PIPE)
result = p.stdout.read()
logger.debug(result)
return result
def createWorkspace(self, name):
if not os.path.isdir(self.root):
os.makedirs(self.root, 0o777)
self.workspace_name = "{}.{}.{}".format(self.user, self.id, platform.node())
self.workspace_root = os.path.join(self.root, self.workspace_name)
def syncWorkspace(self):
cmd = "co"
if os.path.isdir(self.workspace_root):
cmd = "update"
self.svncmd(cmd,
["--username", self.user, "--password", self.password,
self.fullURL,
self.workspace_root
])
self.localfilelist = []
for root, dirs, files in os.walk(self.workspace_root):
if ".svn" in dirs:
dirs.remove(".svn")
self.localfilelist.extend([os.path.join(root, f) for f in files])
def addFile(self, filename):
os.chdir(self.workspace_root)
self.svncmd("add", [filename])
def editFile(self, filename):
logger.info("svn edit")
pass # SVN doesn't do edits
def deleteFile(self, filename):
os.chdir(self.workspace_root)
self.svncmd("delete", [filename])
def commit(self):
description = "SVN checkin"
os.chdir(self.workspace_root)
self.svncmd("ci", ["-m", description])
# For testing purposes - normally expected to be imported
if __name__ == "__main__":
global logger
parser = ArgumentParser(
description="Benchmark Testing",
epilog="Copyright (C) 2016 Robert Cowham, Perforce Software Ltd")
parser.add_argument("-s","--svn", action='store_true', default=False, help="Run SVN")
parser.add_argument("-p","--p4", action='store_true', default=True, help="Run P4")
options = parser.parse_args()
# create console handler
console_logger = logging.getLogger("console_logger")
sh = logging.StreamHandler()
sh.setLevel(logging.DEBUG)
console_logger.addHandler(sh)
console_logger.info("running at top level")
console_logger.propagate = False
logger = console_logger
startdir = os.getcwd()
if options.svn:
b = SvnBenchmark(startdir, readConfig(startdir))
else:
b = P4Benchmark(startdir, readConfig(startdir))
try:
b.run()
except Exception as e:
pass
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #11 | 24711 | Robert Cowham | Restructure and tidy up | ||
| #10 | 21717 | Robert Cowham | Trying nested tasks | ||
| #9 | 21692 | Robert Cowham | MAke more robust for concurrent usage | ||
| #8 | 21665 | Robert Cowham | Make binary files really binary with python2 | ||
| #7 | 20615 | Robert Cowham | Remove hardcoded path | ||
| #6 | 19798 | Robert Cowham |
Log exceptions. Refactor Svn as well |
||
| #5 | 19796 | Robert Cowham | Refactored into taskset | ||
| #4 | 19791 | Robert Cowham |
Fix logging. Unset P4CONFIG in env to avoid DVCS issues |
||
| #3 | 19779 | Robert Cowham | Refactor - move test root | ||
| #2 | 19778 | Robert Cowham | Renamed config file and added svnexe to it | ||
| #1 | 19773 | Robert Cowham | Initial version of benchmark scripts |