Skip to content

Commit 8ee36a5

Browse files
Test generator improvements
- Accept yml files as input - Output the correct type for constructors
1 parent 8372ad9 commit 8ee36a5

File tree

3 files changed

+69
-16
lines changed

3 files changed

+69
-16
lines changed

java/ql/src/utils/flowtestcasegenerator/FlowTestCase.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ class TestCase extends TTestCase {
228228
*/
229229
Type getOutputType() {
230230
if baseOutput = SummaryComponentStack::return()
231-
then result = callable.getReturnType()
231+
then result = getReturnType(callable)
232232
else
233233
exists(int i |
234234
baseOutput = SummaryComponentStack::argument(i) and

java/ql/src/utils/flowtestcasegenerator/FlowTestCaseUtils.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ Type getRootSourceDeclaration(Type t) {
1616
else result = t
1717
}
1818

19+
/** Gets the return type of the callable c, or the constructed tpe if it's a constructor */
20+
Type getReturnType(Callable c) {
21+
if c instanceof Constructor then result = c.getDeclaringType() else result = c.getReturnType()
22+
}
23+
1924
/**
2025
* Holds if type `t` does not clash with another type we want to import that has the same base name.
2126
*/

java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.py

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/python3
1+
#!/usr/bin/env python3
22

33
import errno
44
import json
@@ -47,7 +47,7 @@
4747

4848
try:
4949
os.makedirs(sys.argv[3])
50-
except Exception as e:
50+
except OSError as e:
5151
if e.errno != errno.EEXIST:
5252
print("Failed to create output directory %s: %s" % (sys.argv[3], e))
5353
sys.exit(1)
@@ -75,38 +75,86 @@
7575
(sys.argv[2], e), file=sys.stderr)
7676
sys.exit(1)
7777

78-
commentRegex = re.compile("^\s*(//|#)")
78+
commentRegex = re.compile(r"^\s*(//|#)")
7979

8080

8181
def isComment(s):
8282
return commentRegex.match(s) is not None
8383

8484

85-
try:
86-
with open(sys.argv[1], "r") as f:
87-
specs = [l for l in f if not isComment(l)]
88-
except Exception as e:
89-
print("Failed to open %s: %s\n" % (sys.argv[1], e))
85+
def readCsv(file):
86+
try:
87+
with open(file, "r") as f:
88+
specs = [l for l in f if not isComment(l)]
89+
except Exception as e:
90+
print("Failed to open %s: %s\n" % (file, e))
91+
sys.exit(1)
92+
93+
specs = [row.split(";") for row in specs]
94+
return specs
95+
96+
97+
def readYml(file):
98+
try:
99+
import yaml
100+
with open(file, "r") as f:
101+
doc = yaml.load(f.read(), yaml.Loader)
102+
specs = []
103+
for ext in doc['extensions']:
104+
if ext['addsTo']['extensible'] == 'summaryModel':
105+
for row in ext['data']:
106+
if isinstance(row[2], bool):
107+
row[2] = str(row[2]).lower()
108+
specs.append(row)
109+
return specs
110+
except ImportError:
111+
print("PyYAML not found - try \n pip install pyyaml")
112+
sys.exit(1)
113+
except ValueError as e:
114+
print("Invalid yaml model in %s: %s\n" % (file, e))
115+
sys.exit(1)
116+
except OSError as e:
117+
print("Failed to open %s: %s\n" % (file, e))
118+
sys.exit(1)
119+
120+
121+
def readYmlDir(dirname):
122+
specs = []
123+
for f in os.listdir(dirname):
124+
if f.endswith('.yml'):
125+
specs += readYml(f"{dirname}/{f}")
126+
return specs
127+
128+
129+
specsFile = sys.argv[1]
130+
if os.path.isdir(specsFile):
131+
specs = readYmlDir(specsFile)
132+
elif specsFile.endswith(".yml") or specsFile.endswith(".yaml"):
133+
specs = readYml(specsFile)
134+
elif specsFile.endswith(".csv"):
135+
spcs = readCsv(specsFile)
136+
else:
137+
print(f"Invalid specs {specsFile}. Must be a csv file, a yml file, or a directory of yml files.")
90138
sys.exit(1)
91139

140+
92141
projectTestPkgDir = os.path.join(projectDir, "src", "main", "java", "test")
93142
projectTestFile = os.path.join(projectTestPkgDir, "Test.java")
94143

95144
os.makedirs(projectTestPkgDir)
96145

97146

98-
def qualifiedOuterNameFromCsvRow(row):
99-
cells = row.split(";")
100-
if len(cells) < 2:
147+
def qualifiedOuterNameFromRow(row):
148+
if len(row) < 2:
101149
return None
102-
return cells[0] + "." + cells[1].replace("$", ".")
150+
return row[0] + "." + row[1].replace("$", ".")
103151

104152

105153
with open(projectTestFile, "w") as testJava:
106154
testJava.write("package test;\n\npublic class Test {\n\n")
107155

108156
for i, spec in enumerate(specs):
109-
outerName = qualifiedOuterNameFromCsvRow(spec)
157+
outerName = qualifiedOuterNameFromRow(spec)
110158
if outerName is None:
111159
print("A taint specification has the wrong format: should be 'package;classname;methodname....'", file=sys.stderr)
112160
print("Mis-formatted row: " + spec, file=sys.stderr)
@@ -140,7 +188,7 @@ def qualifiedOuterNameFromCsvRow(row):
140188
with open(qlFile, "w") as f:
141189
f.write(
142190
"import java\nimport utils.flowtestcasegenerator.GenerateFlowTestCase\n\nclass GenRow extends TargetSummaryModelCsv {\n\n\toverride predicate row(string r) {\n\t\tr = [\n")
143-
f.write(",\n".join('\t\t\t"%s"' % spec.strip() for spec in specs))
191+
f.write(",\n".join('\t\t\t"%s"' % ';'.join(spec) for spec in specs))
144192
f.write("\n\t\t]\n\t}\n}\n")
145193

146194
print("Generating tests")
@@ -221,7 +269,7 @@ def copyfile(fromName, toFileHandle):
221269
# Make a test extension file
222270
with open(resultYml, "w") as f:
223271
models = "\n".join(' - [%s]' %
224-
modelSpecRow[0].strip() for modelSpecRow in supportModelRows)
272+
modelSpecRow[0].strip() for modelSpecRow in supportModelRows)
225273
dataextensions = f"""extensions:
226274
- addsTo:
227275
pack: codeql/java-tests

0 commit comments

Comments
 (0)