Skip to content

Commit 59725d6

Browse files
committed
Test case generator: improve error reporting
We now distinguish cases where SSV rows are not in scope at all from those where they don't identify a known type or method, or where input or output specs could not be parsed.
1 parent dff9c71 commit 59725d6

File tree

2 files changed

+55
-15
lines changed

2 files changed

+55
-15
lines changed

java/ql/src/utils/GenerateFlowTestCase.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
print("""Usage:
1616
GenerateFlowTestCase.py specsToTest.ssv projectPom.xml outdir
1717
18-
This generates test cases exercising taint flow specifications found in specsToTest.ssv
18+
This generates test cases exercising function model specifications found in specsToTest.ssv
1919
producing files Test.java, test.ql and test.expected in outdir.
2020
2121
projectPom.xml should be a Maven pom sufficient to resolve the classes named in specsToTest.ssv.
@@ -115,11 +115,6 @@ def qualifiedOuterNameFromSsvRow(row):
115115
f.write("import java\nimport utils.GenerateFlowTestCase\n\nclass GenRow extends CsvRow {\n\n\tGenRow() {\n\t\tthis = [\n")
116116
f.write(",\n".join('\t\t\t"%s"' % spec.strip() for spec in specs))
117117
f.write("\n\t\t]\n\t}\n}\n")
118-
f.write("""
119-
query string getAFailedRow() {
120-
result = any(GenRow row | not exists(RowTestSnippet r | exists(r.getATestSnippetForRow(row))) | row)
121-
}
122-
""")
123118

124119
print("Generating tests")
125120
generatedBqrs = os.path.join(queryDir, "out.bqrs")
@@ -144,17 +139,24 @@ def getTuples(queryName, jsonResult, fname):
144139

145140
with open(generatedJson, "r") as f:
146141
generateOutput = json.load(f)
147-
testCaseRows = getTuples("getTestCase", generateOutput, generatedJson)
148-
supportModelRows = getTuples("getASupportMethodModel", generateOutput, generatedJson)
149-
failedRows = getTuples("getAFailedRow", generateOutput, generatedJson)
142+
expectedTables = ("getTestCase", "getASupportMethodModel", "missingSummaryModelCsv", "getAParseFailure")
143+
144+
testCaseRows, supportModelRows, missingSummaryModelCsvRows, parseFailureRows = \
145+
tuple([getTuples(k, generateOutput, generatedJson) for k in expectedTables])
146+
150147
if len(testCaseRows) != 1 or len(testCaseRows[0]) != 1:
151148
print("Expected exactly one getTestCase result with one column (got: %s)" % json.dumps(testCaseRows), file = sys.stderr)
152149
if any(len(row) != 1 for row in supportModelRows):
153150
print("Expected exactly one column in getASupportMethodModel relation (got: %s)" % json.dumps(supportModelRows), file = sys.stderr)
154-
if any(len(row) != 1 for row in failedRows):
155-
print("Expected exactly one column in getAFailedRow relation (got: %s)" % json.dumps(failedRows), file = sys.stderr)
156-
if len(failedRows) != 0:
157-
print("The following rows failed to generate any test case. Check package, class and method name spelling, and argument and result specifications:\n%s" % "\n".join(r[0] for r in failedRows), file = sys.stderr)
151+
if any(len(row) != 2 for row in parseFailureRows):
152+
print("Expected exactly two columns in parseFailureRows relation (got: %s)" % json.dumps(parseFailureRows), file = sys.stderr)
153+
154+
if len(missingSummaryModelCsvRows) != 0:
155+
print("Tests for some SSV rows were requested that were not in scope (SummaryModelCsv.row does not hold):\n" + "\n".join(r[0] for r in missingSummaryModelCsvRows))
156+
sys.exit(1)
157+
if len(parseFailureRows) != 0:
158+
print("The following rows failed to generate any test case. Check package, class and method name spelling, and argument and result specifications:\n%s" % "\n".join(r[0] + ": " + r[1] for r in parseFailureRows), file = sys.stderr)
159+
sys.exit(1)
158160

159161
with open(resultJava, "w") as f:
160162
f.write(generateOutput["getTestCase"]["tuples"][0][0])

java/ql/src/utils/GenerateFlowTestCase.qll

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,48 @@ import semmle.code.java.dataflow.FlowSummary
55
import semmle.code.java.dataflow.internal.FlowSummaryImpl
66

77
/**
8-
* A CSV row to generate tests for. Users should extend this to define their input rows.
8+
* A CSV row to generate tests for. Users should extend this to define which
9+
* tests to generate. Rows specified here should also satisfy `SummaryModelCsv.row`.
910
*/
1011
bindingset[this]
11-
abstract class CsvRow extends string { }
12+
abstract class TargetSummaryModelCsv extends string {
13+
predicate modelRowExists() { any(SummaryModelCsv smc).row(this) }
14+
}
15+
16+
/**
17+
* Gets a CSV row for which a test has been requested, but `SummaryModelCsv.row` does not hold of it.
18+
*/
19+
query TargetSummaryModelCsv missingSummaryModelCsv() { not result.modelRowExists() }
20+
21+
/**
22+
* Gets a CSV row for which a test has been requested, and `SummaryModelCsv.row` does hold, but
23+
* nonetheless we can't generate a test case for it, indicating we cannot resolve either the callable
24+
* spec or an input or output spec.
25+
*/
26+
query TargetSummaryModelCsv getAParseFailure(string reason) {
27+
result.modelRowExists() and
28+
(
29+
exists(
30+
string namespace, string type, boolean subtypes, string name, string signature, string ext
31+
|
32+
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, result) and
33+
not exists(interpretElement(namespace, type, subtypes, name, signature, ext)) and
34+
reason = "callable could not be resolved"
35+
)
36+
or
37+
exists(string inputSpec |
38+
summaryModel(_, _, _, _, _, _, inputSpec, _, _, result) and
39+
not Private::External::interpretSpec(inputSpec, _) and
40+
reason = "input spec could not be parsed"
41+
)
42+
or
43+
exists(string outputSpec |
44+
summaryModel(_, _, _, _, _, _, _, outputSpec, _, result) and
45+
not Private::External::interpretSpec(outputSpec, _) and
46+
reason = "output spec could not be parsed"
47+
)
48+
)
49+
}
1250

1351
/**
1452
* Returns type of parameter `i` of `callable`, including the type of `this` for parameter -1.

0 commit comments

Comments
 (0)