mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Merge pull request #11455 from michaelnebel/java/flowtestcaseextensions
Java: Update the flow test case generator to produce data extensions.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
|
||||
# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
|
||||
# Definitions of taint steps in the org.apache.commons.io framework.
|
||||
# Definitions of models for the org.apache.commons.io framework.
|
||||
|
||||
extensions:
|
||||
- addsTo:
|
||||
|
||||
@@ -12,7 +12,7 @@ private import FlowTestCaseSupportMethods
|
||||
|
||||
/**
|
||||
* A CSV row to generate tests for. Users should extend this to define which
|
||||
* tests to generate. Rows specified here should also satisfy `SummaryModelCsv.row`.
|
||||
* tests to generate. There should already exist summaries for the rows specified here.
|
||||
*/
|
||||
class TargetSummaryModelCsv extends Unit {
|
||||
/**
|
||||
@@ -42,11 +42,11 @@ predicate summaryModelRow(
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a CSV row for which a test has been requested, but `SummaryModelCsv.row` does not hold of it.
|
||||
* Gets a CSV row for which a test has been requested, but where a summary has not already been defined.
|
||||
*/
|
||||
query string missingSummaryModelCsv() {
|
||||
query string missingSummaryModel() {
|
||||
any(TargetSummaryModelCsv target).row(result) and
|
||||
not any(SummaryModelCsv model).row(result)
|
||||
not summaryModelRow(_, _, _, _, _, _, _, _, _, _, result)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -97,12 +97,12 @@ abstract class SupportMethod extends string {
|
||||
int getPriority() { result = 50 }
|
||||
|
||||
/**
|
||||
* Gets the CSV row describing this support method if it is needed to set up the output for this test.
|
||||
* Gets the data extension row describing this support method if it is needed to set up the output for this test.
|
||||
*
|
||||
* For example, `newWithMapValue` will propagate a value from `Argument[0]` to `MapValue of ReturnValue`, and `getMapValue`
|
||||
* For example, `newWithMapValue` will propagate a value from `Argument[0]` to `ReturnValue.MapValue`, and `getMapValue`
|
||||
* will do the opposite.
|
||||
*/
|
||||
string getCsvModel() { none() }
|
||||
string getDataExtensionModel() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,10 +162,11 @@ private class DefaultGetMethod extends GetMethod {
|
||||
result = "Object get" + contentToken(c) + "Default(Object container) { return null; }"
|
||||
}
|
||||
|
||||
override string getCsvModel() {
|
||||
override string getDataExtensionModel() {
|
||||
result =
|
||||
"generatedtest;Test;false;" + this.getName() + ";(Object);;Argument[0]." +
|
||||
getComponentSpec(SummaryComponent::content(c)) + ";ReturnValue;value;manual"
|
||||
"\"generatedtest\", \"Test\", False, \"" + this.getName() +
|
||||
"\", \"(Object)\", \"\", \"Argument[0]." + getComponentSpec(SummaryComponent::content(c)) +
|
||||
"\", \"ReturnValue\", \"value\", \"manual\""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,10 +359,11 @@ private class DefaultGenMethod extends GenMethod {
|
||||
result = "Object newWith" + contentToken(c) + "Default(Object element) { return null; }"
|
||||
}
|
||||
|
||||
override string getCsvModel() {
|
||||
override string getDataExtensionModel() {
|
||||
result =
|
||||
"generatedtest;Test;false;" + this.getName() + ";(Object);;Argument[0];ReturnValue." +
|
||||
getComponentSpec(SummaryComponent::content(c)) + ";value;manual"
|
||||
"\"generatedtest\", \"Test\", False, \"" + this.getName() +
|
||||
"\", \"(Object)\", \"\", \"Argument[0]\", \"ReturnValue." +
|
||||
getComponentSpec(SummaryComponent::content(c)) + "\", \"value\", \"manual\""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ contain the needed classes.
|
||||
|
||||
If --force is present, existing files may be overwritten.
|
||||
|
||||
Requirements: `mvn` and `codeql` should both appear on your path.
|
||||
Requirements:
|
||||
- `mvn` and `codeql` should both appear on your path.
|
||||
- `--additional-packs /path/to/semmle-code/ql` should be added to your `.config/codeql/config` file.
|
||||
|
||||
After test generation completes, any lines in specsToTest.csv that didn't produce tests are output.
|
||||
If this happens, check the spelling of class and method names, and the syntax of input and output specifications.
|
||||
@@ -52,10 +54,12 @@ except Exception as e:
|
||||
|
||||
resultJava = os.path.join(sys.argv[3], "Test.java")
|
||||
resultQl = os.path.join(sys.argv[3], "test.ql")
|
||||
resultYml = os.path.join(sys.argv[3], "test.model.yml")
|
||||
resultPack = os.path.join(sys.argv[3], "qlpack.yml")
|
||||
|
||||
if not force and (os.path.exists(resultJava) or os.path.exists(resultQl)):
|
||||
print("Won't overwrite existing files '%s' or '%s'" %
|
||||
(resultJava, resultQl), file=sys.stderr)
|
||||
if not force and (os.path.exists(resultJava) or os.path.exists(resultQl) or os.path.exists(resultYml) or os.path.exists(resultPack)):
|
||||
print("Won't overwrite existing files '%s', '%s', '%s' or '%s'." %
|
||||
(resultJava, resultQl, resultYml, resultPack), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
workDir = tempfile.mkdtemp()
|
||||
@@ -127,7 +131,13 @@ queryDir = os.path.join(workDir, "query")
|
||||
os.makedirs(queryDir)
|
||||
qlFile = os.path.join(queryDir, "gen.ql")
|
||||
with open(os.path.join(queryDir, "qlpack.yml"), "w") as f:
|
||||
f.write("name: test-generation-query\nversion: 0.0.0\nlibraryPathDependencies: codeql/java-queries")
|
||||
f.write("""name: test-generation-query
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
codeql/java-all: '*'
|
||||
codeql/java-queries: '*'
|
||||
""")
|
||||
|
||||
with open(qlFile, "w") as f:
|
||||
f.write(
|
||||
"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):
|
||||
with open(generatedJson, "r") as f:
|
||||
generateOutput = json.load(f)
|
||||
expectedTables = ("getTestCase", "getASupportMethodModel",
|
||||
"missingSummaryModelCsv", "getAParseFailure", "noTestCaseGenerated")
|
||||
"missingSummaryModel", "getAParseFailure", "noTestCaseGenerated")
|
||||
|
||||
testCaseRows, supportModelRows, missingSummaryModelCsvRows, parseFailureRows, noTestCaseGeneratedRows = \
|
||||
testCaseRows, supportModelRows, missingSummaryModelRows, parseFailureRows, noTestCaseGeneratedRows = \
|
||||
tuple([getTuples(k, generateOutput, generatedJson)
|
||||
for k in expectedTables])
|
||||
|
||||
@@ -182,9 +192,9 @@ with open(generatedJson, "r") as f:
|
||||
print("Expected exactly one column in noTestCaseGenerated relation (got: %s)" %
|
||||
json.dumps(noTestCaseGeneratedRows), file=sys.stderr)
|
||||
|
||||
if len(missingSummaryModelCsvRows) != 0:
|
||||
print("Tests for some CSV rows were requested that were not in scope (SummaryModelCsv.row does not hold):\n" +
|
||||
"\n".join(r[0] for r in missingSummaryModelCsvRows))
|
||||
if len(missingSummaryModelRows) != 0:
|
||||
print("Tests for some CSV rows were requested that were not in scope (a summary doesn't already exist):\n" +
|
||||
"\n".join(r[0] for r in missingSummaryModelRows))
|
||||
sys.exit(1)
|
||||
if len(parseFailureRows) != 0:
|
||||
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):
|
||||
|
||||
with open(resultQl, "w") as f:
|
||||
copyfile("testHeader.qlfrag", f)
|
||||
if len(supportModelRows) != 0:
|
||||
copyfile("testModelsHeader.qlfrag", f)
|
||||
f.write(", ".join('"%s"' %
|
||||
modelSpecRow[0].strip() for modelSpecRow in supportModelRows))
|
||||
copyfile("testModelsFooter.qlfrag", f)
|
||||
|
||||
if len(supportModelRows) != 0:
|
||||
# Make a test extension file
|
||||
with open(resultYml, "w") as f:
|
||||
models = "\n".join(' - [%s]' %
|
||||
modelSpecRow[0].strip() for modelSpecRow in supportModelRows)
|
||||
dataextensions = f"""extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-tests
|
||||
extensible: extSummaryModel
|
||||
data:
|
||||
{models}
|
||||
"""
|
||||
f.write(dataextensions)
|
||||
# Make a qlpack file such that the extension will be picked up
|
||||
with open(resultPack, "w") as f:
|
||||
f.write("""name: example-test-pack
|
||||
version: 0.0.0
|
||||
extractor: java
|
||||
dependencies:
|
||||
codeql/java-all: '*'
|
||||
codeql/java-queries: '*'
|
||||
codeql/java-tests: '*'
|
||||
dataExtensions:
|
||||
- test.model.yml
|
||||
""")
|
||||
|
||||
# Make an empty .expected file, since this is an inline-exectations test
|
||||
with open(os.path.join(sys.argv[3], "test.expected"), "w"):
|
||||
|
||||
@@ -13,13 +13,13 @@ private import FlowTestCaseSupportMethods
|
||||
private import FlowTestCaseUtils
|
||||
|
||||
/**
|
||||
* Gets a CSV row for which a test has been requested, and `SummaryModelCsv.row` does hold, but
|
||||
* Gets a CSV row for which a test has been requested, and where there exists a summary, but
|
||||
* nonetheless we can't generate a test case for it, indicating we cannot resolve either the callable
|
||||
* spec or an input or output spec.
|
||||
*/
|
||||
query string getAParseFailure(string reason) {
|
||||
any(TargetSummaryModelCsv target).row(result) and
|
||||
any(SummaryModelCsv model).row(result) and
|
||||
summaryModelRow(_, _, _, _, _, _, _, _, _, _, result) and
|
||||
(
|
||||
not summaryModelRow(_, _, _, _, _, _, _, _, _, _, result) and
|
||||
reason = "row could not be parsed"
|
||||
@@ -52,7 +52,7 @@ query string getAParseFailure(string reason) {
|
||||
*/
|
||||
query string noTestCaseGenerated() {
|
||||
any(TargetSummaryModelCsv target).row(result) and
|
||||
any(SummaryModelCsv model).row(result) and
|
||||
summaryModelRow(_, _, _, _, _, _, _, _, _, _, result) and
|
||||
not exists(getAParseFailure(_)) and
|
||||
not exists(any(TestCase tc).getATestSnippetForRow(result))
|
||||
}
|
||||
@@ -85,12 +85,12 @@ SupportMethod getASupportMethod() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CSV specification of the taint-/value-propagation behavior of a test support method (`get` or `newWith` method).
|
||||
* Returns a data extension specification of the taint-/value-propagation behavior of a test support method (`get` or `newWith` method).
|
||||
*/
|
||||
query string getASupportMethodModel() { result = getASupportMethod().getCsvModel() }
|
||||
query string getASupportMethodModel() { result = getASupportMethod().getDataExtensionModel() }
|
||||
|
||||
/**
|
||||
* Gets a Java file body testing all requested CSV rows against whatever classes and methods they resolve against.
|
||||
* Gets a Java file body testing all requested Models as Data rows against whatever classes and methods they resolve against.
|
||||
*/
|
||||
query string getTestCase() {
|
||||
result =
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
class SummaryModelTest extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
//"package;type;overrides;name;signature;ext;inputspec;outputspec;kind;provenance"
|
||||
Reference in New Issue
Block a user