Skip to content

Commit ce782c0

Browse files
authored
Merge pull request #6 from blsemo/master
Merge over to blackseamonster
2 parents 231715a + 6654c0c commit ce782c0

File tree

68 files changed

+2814
-347
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+2814
-347
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
.idea
12
.DS_Store
23
depthmapX-build
34
build
45
RegressionTest/rundir
56
__pycache__
67
depthmapX.pro.user
8+
*_BACKUP_*
9+
*_BASE_*
10+
*_LOCAL_*
11+
*_REMOTE_*
12+

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ script:
1818
- mkdir build && cd build
1919
- /opt/qt57/bin/qmake ../depthmapX.pro
2020
- make
21-
- ./cliTest/cliTest && ./GuiUnitTest/GuiUnitTest && ./salaTest/salaTest
22-
- cd ../RegressionTest && python3.5 test_main.py && python3.5 RegressionTestRunner.py
21+
- ./cliTest/cliTest && ./GuiUnitTest/GuiUnitTest && ./salaTest/salaTest && ./genlibTest/genlibTest
22+
- cd ../RegressionTest/test && python3.5 test_main.py && cd .. && python3.5 RegressionTestRunner.py

RegressionTest/RegressionTestRunner.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import runhelpers
22
import config
33
import depthmaprunner
4+
import performancerunner
45
import os
6+
import sys
57

68
defaultConfigFile = "regressionconfig.json"
79

@@ -17,12 +19,17 @@ def __init__(self, configfile, runfunc):
1719
def run(self):
1820
if not os.path.exists(self.config.rundir):
1921
os.makedirs(self.config.rundir)
20-
runner = depthmaprunner.DepthmapRegressionRunner( self.runfunc, self.baseBinary, self.testBinary, self.config.rundir )
22+
if self.config.performanceRegression.enabled:
23+
print("Performance regression runs enabled")
24+
runner = performancerunner.PerformanceRunner(self.runfunc, self.baseBinary, self.testBinary, self.config.rundir,self.config.performanceRegression )
25+
else:
26+
print("Default regression runs - no performance")
27+
runner = depthmaprunner.DepthmapRegressionRunner( self.runfunc, self.baseBinary, self.testBinary, self.config.rundir )
2128

2229
good = True
2330
for name, case in self.config.testcases.items():
2431
print("Running test case " + name)
25-
success, output = runner.runTestCase(name, case.cmd.infile, case.cmd.outfile, case.cmd.mode, case.cmd.simpleMode, case.cmd.modeLines)
32+
success, output = runner.runTestCase(name, case)
2633
if not success:
2734
good = False
2835
print ("Failed:\n" + output)
@@ -31,7 +38,10 @@ def run(self):
3138
return good
3239

3340
if __name__ == "__main__":
34-
r = RegressionTestRunner(defaultConfigFile, runhelpers.runExecutable)
41+
configFile = defaultConfigFile
42+
if len(sys.argv) == 2:
43+
configFile = sys.argv[1]
44+
r = RegressionTestRunner(configFile, runhelpers.runExecutable)
3545
if not r.run():
3646
exit(-1)
3747

RegressionTest/cmdlinewrapper.py

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def __init__(self):
99
self.outfile = None
1010
self.simpleMode = False
1111
self.mode = None
12-
self.modeLines = []
12+
self.extraArgs = {}
1313
self.timingFile = None
1414

1515

@@ -27,42 +27,15 @@ def toCmdArray(self):
2727
if self.timingFile:
2828
args.extend(["-t", self.timingFile])
2929

30-
for modeLine in self.modeLines:
31-
args.extend(modeLine.toCmdArray())
30+
for key, value in self.extraArgs.items():
31+
if isinstance(value, list):
32+
for v in value:
33+
args.append(key)
34+
args.append(v)
35+
else:
36+
args.append(key)
37+
if value:
38+
args.append(value)
3239

3340
return args
3441

35-
class VisibilityCmd():
36-
visibilityMode = None
37-
globalMeasures = False
38-
localMeasures = False
39-
radius = None
40-
41-
def toCmdArray(self):
42-
if self.visibilityMode == None:
43-
raise CommandLineError("visibility mode must be defined")
44-
args = ["-vm", self.visibilityMode ]
45-
if self.globalMeasures:
46-
args.append("-vg")
47-
if self.localMeasures:
48-
args.append("-vl")
49-
if not self.radius == None:
50-
args.extend(["-vr", self.radius])
51-
return args
52-
53-
class LinkCmd():
54-
linksFile = None
55-
manualLinks = None
56-
57-
def toCmdArray(self):
58-
if self.linksFile == None and self.manualLinks == None:
59-
raise CommandLineError("links must be given as a file or each one manually")
60-
if self.linksFile != None and self.manualLinks != None:
61-
raise CommandLineError("links must be given as a file or each one manually")
62-
args = [];
63-
if self.manualLinks:
64-
for manualLink in self.manualLinks:
65-
args.extend(["-lnk", manualLink])
66-
if self.linksFile:
67-
args.extend(["-lf", self.linksFile]);
68-
return args

RegressionTest/config.py

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,23 @@
11
import json
22
import os.path
33
import cmdlinewrapper
4+
from performanceregressionconfig import PerformanceRegressionConfig
5+
46

57
class ConfigError(Exception):
68
def __init__(self, message):
79
self.message = message
810

9-
def buildSubCmd( subType, subConfig ):
10-
if subType == "Visibility":
11-
scmd = cmdlinewrapper.VisibilityCmd()
12-
scmd.visibilityMode = subConfig["visibilityMode"]
13-
if "globalMeasures" in subConfig:
14-
scmd.globalMeasures = True
15-
if "localMeasures" in subConfig:
16-
scmd.localMeasures = True
17-
if "radius" in subConfig:
18-
scmd.radius = subConfig["radius"]
19-
return scmd;
20-
elif subType == "Link":
21-
scmd = cmdlinewrapper.LinkCmd()
22-
if "linksFile" in subConfig:
23-
scmd.linksFile = subConfig["linksFile"]
24-
if "manualLinks" in subConfig:
25-
scmd.manualLinks = subConfig["manualLinks"]
26-
return scmd;
27-
else:
28-
raise ConfigError("Unknown sub commandline config " + subType)
29-
3011
def buildCmd(testcase):
3112
cmd = cmdlinewrapper.DepthmapCmd()
3213
cmd.infile = testcase["infile"]
3314
cmd.outfile = testcase["outfile"]
3415
cmd.mode = testcase["mode"]
3516
if "simple" in testcase and not testcase["simple"] == "false":
3617
cmd.simpleMode = True
37-
if "subcmds" in testcase:
38-
for (key, value) in testcase["subcmds"].items():
39-
cmd.modeLines.append(buildSubCmd(key, value))
18+
cmd.extraArgs = testcase.get("extraArgs", {})
4019
return cmd
4120

42-
class TestCase():
43-
def __init__(self, cmd):
44-
self.cmd = cmd
45-
46-
def buildTestcase(testcase, rundir, configdir):
47-
return TestCase(buildCmd(testcase))
48-
49-
5021
class RegressionConfig():
5122
def __init__(self, filename):
5223
with open(filename, "r") as f:
@@ -55,8 +26,9 @@ def __init__(self, filename):
5526
self.rundir = config["rundir"]
5627
self.basebinlocation = config["basebinlocation"]
5728
self.testbinlocation = config["testbinlocation"]
29+
self.performanceRegression = PerformanceRegressionConfig(config.get("performance", None))
5830
self.testcases = {}
5931
for (name, tc) in config["testcases"].items():
60-
self.testcases[name] = buildTestcase(tc, self.rundir, configdir)
32+
self.testcases[name] = buildCmd(tc)
6133

6234

RegressionTest/depthmaprunner.py

Lines changed: 19 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
import cmdlinewrapper
33
import difflib
44
import pprint
5-
import csv
5+
6+
import runhelpers
7+
68

79
class DepthmapRunner():
810
def __init__(self, runFunc, binary ):
@@ -22,88 +24,40 @@ def diffBinaryFiles(file1, file2):
2224
gen = difflib.diff_bytes(difflib.unified_diff, [content1], [content2])
2325
return not(list(gen))
2426

25-
def checkPerformance(baseFile, testFile):
26-
"""
27-
Check the performance of 2 depthmap runs against each other
28-
This function expects the timing from a base and test run and parses them
29-
as CSV. For now, it expects the entries to be the same. It will return an
30-
error message if
31-
* one or both of the files are missing
32-
* the number of lines or the labels don't match
33-
* the test run is more than 5 seconds or 5% slower than the baseline
34-
(whatever is greater)
35-
"""
36-
if not os.path.exists(baseFile):
37-
return "Base performance timing file {0} is missing".format(baseFile)
38-
if not os.path.exists(testFile):
39-
return "Test performance timing file {0} is missing".format(testFile)
40-
with open(baseFile) as baseHandle, open(testFile) as testHandle:
41-
baseReader = csv.DictReader(baseHandle)
42-
testReader = csv.DictReader(testHandle)
43-
44-
baseDone = False
45-
testDone = False
46-
47-
while True:
48-
try:
49-
baseLine = next(baseReader)
50-
except StopIteration:
51-
baseDone = True
52-
53-
try:
54-
testLine = next(testReader)
55-
except StopIteration:
56-
testDone = True
57-
58-
if baseDone and testDone:
59-
return ""
60-
if baseDone and not testDone:
61-
return "baseline performance file {0} has fewer lines than the test one {1}".format(baseFile, testFile)
62-
if testDone and not baseDone:
63-
return "baseline performance file {0} has more lines than the test one {1}".format(baseFile, testFile)
64-
65-
if not baseLine["action"] == testLine["action"]:
66-
return "performance line mismatch: base '{0}', test '{1}'".format(baseLine["action"], testLine["action"])
67-
68-
baseTime = float(baseLine["duration"])
69-
testTime = float(testLine["duration"])
70-
71-
allowance = max(5.0, baseTime / 20.0 )
72-
if testTime > baseTime + allowance:
73-
return "Performance regression: {0} took {1}s instead of {2}s".format(baseLine["action"], testLine["duration"], baseLine["duration"])
74-
75-
76-
7727
class DepthmapRegressionRunner():
7828
def __init__(self, runFunc, baseBinary, testBinary, workingDir):
7929
self.__baseRunner = DepthmapRunner(runFunc, baseBinary)
8030
self.__testRunner = DepthmapRunner(runFunc, testBinary)
8131
self.__workingDir = workingDir
8232

83-
def runTestCase(self, name, infile, outfile, mode, simpleMode = False, subcmds = [], timingFile = "runtimes.csv"):
84-
cmd = cmdlinewrapper.DepthmapCmd()
85-
cmd.infile = infile
86-
cmd.outfile = outfile
87-
cmd.mode = mode
88-
cmd.simpleMode = simpleMode
89-
cmd.modeLines = subcmds
90-
cmd.timingFile = timingFile
33+
def makeBaseDir(self, name):
34+
return os.path.join(self.__workingDir, name + "_base")
35+
36+
def makeTestDir(self, name):
37+
return os.path.join(self.__workingDir, name + "_test")
38+
39+
def runTestCase(self, name, cmd):
40+
runhelpers.prepareDirectory(self.makeBaseDir(name))
41+
runhelpers.prepareDirectory(self.makeTestDir(name))
42+
return self.runTestCaseImpl(name, cmd)
9143

92-
baseDir = os.path.join(self.__workingDir, name + "_base")
44+
def runTestCaseImpl(self, name, cmd):
45+
baseDir = self.makeBaseDir(name)
9346
(baseSuccess, baseOut) = self.__baseRunner.runDepthmap(cmd, baseDir)
9447
if not baseSuccess:
9548
print("Baseline run failed with arguments " + pprint.pformat(cmd.toCmdArray()))
9649
print(baseOut)
9750
return (False, "Baseline run failed")
98-
testDir = os.path.join(self.__workingDir, name + "_test")
51+
52+
testDir = self.makeTestDir(name)
9953
(testSuccess, testOut) = self.__testRunner.runDepthmap(cmd, testDir)
10054
if not testSuccess:
10155
print("Test run failed with arguments " + pprint.pformat(cmd.toCmdArray()))
10256
print(testOut)
10357
return (False, "Test run failed")
10458

105-
baseFile = os.path.join(baseDir, outfile)
106-
testFile = os.path.join(testDir, outfile)
59+
baseFile = os.path.join(baseDir, cmd.outfile)
60+
testFile = os.path.join(testDir, cmd.outfile)
10761
if not os.path.exists(baseFile):
10862
message = "Baseline output {0} does not exist".format(baseFile)
10963
print (message)
@@ -118,11 +72,6 @@ def runTestCase(self, name, infile, outfile, mode, simpleMode = False, subcmds =
11872
print (message)
11973
return (False, message)
12074

121-
message = checkPerformance( os.path.join(baseDir, timingFile), os.path.join(testDir, timingFile))
122-
if message:
123-
print(message)
124-
return (False,message)
125-
12675
return (True, "")
12776

12877

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"rundir": "rundir",
3+
"basebinlocation": "../../BaselineBinaries",
4+
"testbinlocation": "../../../build",
5+
"performance":{},
6+
"testcases": {
7+
"visibility_global_n": {
8+
"infile": "../../../testdata/gallery_connected.graph",
9+
"outfile": "out.graph",
10+
"mode": "VGA",
11+
"extraArgs": {
12+
"-vm": "visibility",
13+
"-vg": "1",
14+
"-vr": "n"
15+
}
16+
}
17+
}
18+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
3+
class PerformanceRegressionConfig:
4+
""" Encapsulate performance regression config
5+
This takes an optional performance regression config. All Elements
6+
in the performance regression config are optional. If it is None
7+
completly, performance regresssion will be disabled
8+
"peformanceRegression":
9+
{
10+
enabled = 1, <- enabling performance regression - only required if nothing else is set, the existence of a non null config will turn on performance regression
11+
runsPerInstance = 3, <- how many runs of each run def to run and average over
12+
relativeThresholdInPercent = 1.5, <- how many percent can the new version be slower without failing
13+
absoluteThresholdInSeconds = 2.3, <- how many seconds the new version can be slower without failing
14+
of the two above, breaching the lower one will lead to failure.
15+
}
16+
"""
17+
def __init__(self, perfConfig):
18+
if None == perfConfig or ("enabled" in perfConfig and perfConfig["enabled"] not in ["True", "true", "1", "yes"]):
19+
self.enabled = False
20+
return
21+
self.enabled = True;
22+
self.runsPerInstance = int(perfConfig.get("runsPerInstance", 3))
23+
self.relativeThresholdInPercent = float(perfConfig.get("relativeThresholdInPercent", 1))
24+
self.absoluteThresholdInSeconds = float(perfConfig.get("absoluteThresholdInSeconds",1))

0 commit comments

Comments
 (0)