Skip to content

Commit 95e6534

Browse files
authored
Merge pull request github#11455 from michaelnebel/java/flowtestcaseextensions
Java: Update the flow test case generator to produce data extensions.
2 parents 24be481 + 73b171e commit 95e6534

File tree

9 files changed

+71
-49
lines changed

9 files changed

+71
-49
lines changed

csharp/ql/lib/ext/generated/dotnet_runtime.model.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
21
# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
3-
# Definitions of taint steps in the dotnet_runtime framework.
2+
# Definitions of models for the dotnet_runtime framework.
43

54
extensions:
65

java/ql/lib/ext/generated/org.apache.commons.io.model.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
21
# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
3-
# Definitions of taint steps in the org.apache.commons.io framework.
2+
# Definitions of models for the org.apache.commons.io framework.
43

54
extensions:
65
- addsTo:

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ private import FlowTestCaseSupportMethods
1212

1313
/**
1414
* A CSV row to generate tests for. Users should extend this to define which
15-
* tests to generate. Rows specified here should also satisfy `SummaryModelCsv.row`.
15+
* tests to generate. There should already exist summaries for the rows specified here.
1616
*/
1717
class TargetSummaryModelCsv extends Unit {
1818
/**
@@ -42,11 +42,11 @@ predicate summaryModelRow(
4242
}
4343

4444
/**
45-
* Gets a CSV row for which a test has been requested, but `SummaryModelCsv.row` does not hold of it.
45+
* Gets a CSV row for which a test has been requested, but where a summary has not already been defined.
4646
*/
47-
query string missingSummaryModelCsv() {
47+
query string missingSummaryModel() {
4848
any(TargetSummaryModelCsv target).row(result) and
49-
not any(SummaryModelCsv model).row(result)
49+
not summaryModelRow(_, _, _, _, _, _, _, _, _, _, result)
5050
}
5151

5252
/**

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,12 @@ abstract class SupportMethod extends string {
9797
int getPriority() { result = 50 }
9898

9999
/**
100-
* Gets the CSV row describing this support method if it is needed to set up the output for this test.
100+
* Gets the data extension row describing this support method if it is needed to set up the output for this test.
101101
*
102-
* For example, `newWithMapValue` will propagate a value from `Argument[0]` to `MapValue of ReturnValue`, and `getMapValue`
102+
* For example, `newWithMapValue` will propagate a value from `Argument[0]` to `ReturnValue.MapValue`, and `getMapValue`
103103
* will do the opposite.
104104
*/
105-
string getCsvModel() { none() }
105+
string getDataExtensionModel() { none() }
106106
}
107107

108108
/**
@@ -162,10 +162,11 @@ private class DefaultGetMethod extends GetMethod {
162162
result = "Object get" + contentToken(c) + "Default(Object container) { return null; }"
163163
}
164164

165-
override string getCsvModel() {
165+
override string getDataExtensionModel() {
166166
result =
167-
"generatedtest;Test;false;" + this.getName() + ";(Object);;Argument[0]." +
168-
getComponentSpec(SummaryComponent::content(c)) + ";ReturnValue;value;manual"
167+
"\"generatedtest\", \"Test\", False, \"" + this.getName() +
168+
"\", \"(Object)\", \"\", \"Argument[0]." + getComponentSpec(SummaryComponent::content(c)) +
169+
"\", \"ReturnValue\", \"value\", \"manual\""
169170
}
170171
}
171172

@@ -358,10 +359,11 @@ private class DefaultGenMethod extends GenMethod {
358359
result = "Object newWith" + contentToken(c) + "Default(Object element) { return null; }"
359360
}
360361

361-
override string getCsvModel() {
362+
override string getDataExtensionModel() {
362363
result =
363-
"generatedtest;Test;false;" + this.getName() + ";(Object);;Argument[0];ReturnValue." +
364-
getComponentSpec(SummaryComponent::content(c)) + ";value;manual"
364+
"\"generatedtest\", \"Test\", False, \"" + this.getName() +
365+
"\", \"(Object)\", \"\", \"Argument[0]\", \"ReturnValue." +
366+
getComponentSpec(SummaryComponent::content(c)) + "\", \"value\", \"manual\""
365367
}
366368
}
367369

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

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
2525
If --force is present, existing files may be overwritten.
2626
27-
Requirements: `mvn` and `codeql` should both appear on your path.
27+
Requirements:
28+
- `mvn` and `codeql` should both appear on your path.
29+
- `--additional-packs /path/to/semmle-code/ql` should be added to your `.config/codeql/config` file.
2830
2931
After test generation completes, any lines in specsToTest.csv that didn't produce tests are output.
3032
If this happens, check the spelling of class and method names, and the syntax of input and output specifications.
@@ -52,10 +54,12 @@
5254

5355
resultJava = os.path.join(sys.argv[3], "Test.java")
5456
resultQl = os.path.join(sys.argv[3], "test.ql")
57+
resultYml = os.path.join(sys.argv[3], "test.model.yml")
58+
resultPack = os.path.join(sys.argv[3], "qlpack.yml")
5559

56-
if not force and (os.path.exists(resultJava) or os.path.exists(resultQl)):
57-
print("Won't overwrite existing files '%s' or '%s'" %
58-
(resultJava, resultQl), file=sys.stderr)
60+
if not force and (os.path.exists(resultJava) or os.path.exists(resultQl) or os.path.exists(resultYml) or os.path.exists(resultPack)):
61+
print("Won't overwrite existing files '%s', '%s', '%s' or '%s'." %
62+
(resultJava, resultQl, resultYml, resultPack), file=sys.stderr)
5963
sys.exit(1)
6064

6165
workDir = tempfile.mkdtemp()
@@ -127,7 +131,13 @@ def qualifiedOuterNameFromCsvRow(row):
127131
os.makedirs(queryDir)
128132
qlFile = os.path.join(queryDir, "gen.ql")
129133
with open(os.path.join(queryDir, "qlpack.yml"), "w") as f:
130-
f.write("name: test-generation-query\nversion: 0.0.0\nlibraryPathDependencies: codeql/java-queries")
134+
f.write("""name: test-generation-query
135+
version: 0.0.0
136+
dependencies:
137+
codeql/java-all: '*'
138+
codeql/java-queries: '*'
139+
""")
140+
131141
with open(qlFile, "w") as f:
132142
f.write(
133143
"import java\nimport utils.flowtestcasegenerator.GenerateFlowTestCase\n\nclass GenRow extends TargetSummaryModelCsv {\n\n\toverride predicate row(string r) {\n\t\tr = [\n")
@@ -163,9 +173,9 @@ def getTuples(queryName, jsonResult, fname):
163173
with open(generatedJson, "r") as f:
164174
generateOutput = json.load(f)
165175
expectedTables = ("getTestCase", "getASupportMethodModel",
166-
"missingSummaryModelCsv", "getAParseFailure", "noTestCaseGenerated")
176+
"missingSummaryModel", "getAParseFailure", "noTestCaseGenerated")
167177

168-
testCaseRows, supportModelRows, missingSummaryModelCsvRows, parseFailureRows, noTestCaseGeneratedRows = \
178+
testCaseRows, supportModelRows, missingSummaryModelRows, parseFailureRows, noTestCaseGeneratedRows = \
169179
tuple([getTuples(k, generateOutput, generatedJson)
170180
for k in expectedTables])
171181

@@ -182,9 +192,9 @@ def getTuples(queryName, jsonResult, fname):
182192
print("Expected exactly one column in noTestCaseGenerated relation (got: %s)" %
183193
json.dumps(noTestCaseGeneratedRows), file=sys.stderr)
184194

185-
if len(missingSummaryModelCsvRows) != 0:
186-
print("Tests for some CSV rows were requested that were not in scope (SummaryModelCsv.row does not hold):\n" +
187-
"\n".join(r[0] for r in missingSummaryModelCsvRows))
195+
if len(missingSummaryModelRows) != 0:
196+
print("Tests for some CSV rows were requested that were not in scope (a summary doesn't already exist):\n" +
197+
"\n".join(r[0] for r in missingSummaryModelRows))
188198
sys.exit(1)
189199
if len(parseFailureRows) != 0:
190200
print("The following rows failed to generate any test case. Check package, class and method name spelling, and argument and result specifications:\n%s" %
@@ -207,11 +217,32 @@ def copyfile(fromName, toFileHandle):
207217

208218
with open(resultQl, "w") as f:
209219
copyfile("testHeader.qlfrag", f)
210-
if len(supportModelRows) != 0:
211-
copyfile("testModelsHeader.qlfrag", f)
212-
f.write(", ".join('"%s"' %
213-
modelSpecRow[0].strip() for modelSpecRow in supportModelRows))
214-
copyfile("testModelsFooter.qlfrag", f)
220+
221+
if len(supportModelRows) != 0:
222+
# Make a test extension file
223+
with open(resultYml, "w") as f:
224+
models = "\n".join(' - [%s]' %
225+
modelSpecRow[0].strip() for modelSpecRow in supportModelRows)
226+
dataextensions = f"""extensions:
227+
- addsTo:
228+
pack: codeql/java-tests
229+
extensible: extSummaryModel
230+
data:
231+
{models}
232+
"""
233+
f.write(dataextensions)
234+
# Make a qlpack file such that the extension will be picked up
235+
with open(resultPack, "w") as f:
236+
f.write("""name: example-test-pack
237+
version: 0.0.0
238+
extractor: java
239+
dependencies:
240+
codeql/java-all: '*'
241+
codeql/java-queries: '*'
242+
codeql/java-tests: '*'
243+
dataExtensions:
244+
- test.model.yml
245+
""")
215246

216247
# Make an empty .expected file, since this is an inline-exectations test
217248
with open(os.path.join(sys.argv[3], "test.expected"), "w"):

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ private import FlowTestCaseSupportMethods
1313
private import FlowTestCaseUtils
1414

1515
/**
16-
* Gets a CSV row for which a test has been requested, and `SummaryModelCsv.row` does hold, but
16+
* Gets a CSV row for which a test has been requested, and where there exists a summary, but
1717
* nonetheless we can't generate a test case for it, indicating we cannot resolve either the callable
1818
* spec or an input or output spec.
1919
*/
2020
query string getAParseFailure(string reason) {
2121
any(TargetSummaryModelCsv target).row(result) and
22-
any(SummaryModelCsv model).row(result) and
22+
summaryModelRow(_, _, _, _, _, _, _, _, _, _, result) and
2323
(
2424
not summaryModelRow(_, _, _, _, _, _, _, _, _, _, result) and
2525
reason = "row could not be parsed"
@@ -52,7 +52,7 @@ query string getAParseFailure(string reason) {
5252
*/
5353
query string noTestCaseGenerated() {
5454
any(TargetSummaryModelCsv target).row(result) and
55-
any(SummaryModelCsv model).row(result) and
55+
summaryModelRow(_, _, _, _, _, _, _, _, _, _, result) and
5656
not exists(getAParseFailure(_)) and
5757
not exists(any(TestCase tc).getATestSnippetForRow(result))
5858
}
@@ -85,12 +85,12 @@ SupportMethod getASupportMethod() {
8585
}
8686

8787
/**
88-
* Returns a CSV specification of the taint-/value-propagation behavior of a test support method (`get` or `newWith` method).
88+
* Returns a data extension specification of the taint-/value-propagation behavior of a test support method (`get` or `newWith` method).
8989
*/
90-
query string getASupportMethodModel() { result = getASupportMethod().getCsvModel() }
90+
query string getASupportMethodModel() { result = getASupportMethod().getDataExtensionModel() }
9191

9292
/**
93-
* Gets a Java file body testing all requested CSV rows against whatever classes and methods they resolve against.
93+
* Gets a Java file body testing all requested Models as Data rows against whatever classes and methods they resolve against.
9494
*/
9595
query string getTestCase() {
9696
result =

java/ql/src/utils/flowtestcasegenerator/testModelsFooter.qlfrag

Lines changed: 0 additions & 3 deletions
This file was deleted.

java/ql/src/utils/flowtestcasegenerator/testModelsHeader.qlfrag

Lines changed: 0 additions & 5 deletions
This file was deleted.

misc/scripts/models-as-data/generate_flow_model_extensions.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,8 @@ def makeContent(self):
173173
else:
174174
negativeSummaryAddsTo = ""
175175

176-
return f"""
177-
# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
178-
# Definitions of taint steps in the {self.friendlyname} framework.
176+
return f"""# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
177+
# Definitions of models for the {self.friendlyname} framework.
179178
180179
extensions:
181180
{sinkAddsTo}

0 commit comments

Comments
 (0)