Skip to content

Commit f386888

Browse files
committed
Test case generator: rework to use a less-invasive ExternalFlow API
Some predicate/type names and docs are also improved
1 parent 59725d6 commit f386888

File tree

1 file changed

+66
-27
lines changed

1 file changed

+66
-27
lines changed

java/ql/src/utils/GenerateFlowTestCase.qll

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,21 @@ query TargetSummaryModelCsv getAParseFailure(string reason) {
4848
)
4949
}
5050

51+
private class CallableToTest extends Callable {
52+
CallableToTest() {
53+
exists(
54+
string namespace, string type, boolean subtypes, string name, string signature, string ext
55+
|
56+
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _) and
57+
this = interpretElement(namespace, type, subtypes, name, signature, ext)
58+
)
59+
}
60+
}
61+
5162
/**
5263
* Returns type of parameter `i` of `callable`, including the type of `this` for parameter -1.
5364
*/
54-
Type getParameterType(Private::External::SummarizedCallableExternal callable, int i) {
65+
Type getParameterType(CallableToTest callable, int i) {
5566
if i = -1 then result = callable.getDeclaringType() else result = callable.getParameterType(i)
5667
}
5768

@@ -149,37 +160,45 @@ Type getRootSourceDeclaration(Type t) {
149160
* from `input` to `output`). Usually there is one of these per CSV row (`row`), but there may be more if `row` describes more than one
150161
* override or overload of a particular method, or if the input or output specifications cover more than one argument.
151162
*/
152-
newtype TRowTestSnippet =
153-
MkSnippet(
154-
CsvRow row, Private::External::SummarizedCallableExternal callable, SummaryComponentStack input,
155-
SummaryComponentStack output, boolean preservesValue
163+
newtype TTestCase =
164+
MkTestCase(
165+
CallableToTest callable, SummaryComponentStack input, SummaryComponentStack output, string kind,
166+
string row
156167
) {
157-
callable.propagatesFlowForRow(input, output, preservesValue, row)
168+
exists(
169+
string namespace, string type, boolean subtypes, string name, string signature, string ext,
170+
string inputSpec, string outputSpec
171+
|
172+
summaryModel(namespace, type, subtypes, name, signature, ext, inputSpec, outputSpec, kind, row) and
173+
callable = interpretElement(namespace, type, subtypes, name, signature, ext) and
174+
Private::External::interpretSpec(inputSpec, input) and
175+
Private::External::interpretSpec(outputSpec, output)
176+
)
158177
}
159178

160179
/**
161-
* A test snippet (as `TRowTestSnippet`, except `baseInput` and `baseOutput` hold the bottom of the summary stacks
180+
* A test snippet (as `TTestCase`, except `baseInput` and `baseOutput` hold the bottom of the summary stacks
162181
* `input` and `output` respectively (hence, `baseInput` and `baseOutput` are parameters or return values).
163182
*/
164-
class RowTestSnippet extends TRowTestSnippet {
165-
string row;
166-
Private::External::SummarizedCallableExternal callable;
183+
class TestCase extends TTestCase {
184+
CallableToTest callable;
167185
SummaryComponentStack input;
168186
SummaryComponentStack output;
169187
SummaryComponentStack baseInput;
170188
SummaryComponentStack baseOutput;
171-
boolean preservesValue;
189+
string kind;
190+
string row;
172191

173-
RowTestSnippet() {
174-
this = MkSnippet(row, callable, input, output, preservesValue) and
192+
TestCase() {
193+
this = MkTestCase(callable, input, output, kind, row) and
175194
baseInput = input.drop(input.length() - 1) and
176195
baseOutput = output.drop(output.length() - 1)
177196
}
178197

179198
string toString() {
180199
result =
181200
row + " / " + callable + " / " + input + " / " + output + " / " + baseInput + " / " +
182-
baseOutput + " / " + preservesValue
201+
baseOutput + " / " + kind
183202
}
184203

185204
/**
@@ -261,9 +280,9 @@ class RowTestSnippet extends TRowTestSnippet {
261280
* Returns an inline test expectation appropriate to this CSV row.
262281
*/
263282
string getExpectation() {
264-
preservesValue = true and result = "// $hasValueFlow"
283+
kind = "value" and result = "// $hasValueFlow"
265284
or
266-
preservesValue = false and result = "// $hasTaintFlow"
285+
kind = "taint" and result = "// $hasTaintFlow"
267286
}
268287

269288
/**
@@ -371,6 +390,26 @@ class RowTestSnippet extends TRowTestSnippet {
371390
"(Object container) { return null; }"
372391
}
373392

393+
/**
394+
* Gets a string that specifies summary component `c` in a summary specification CSV row.
395+
*/
396+
string getComponentSpec(SummaryComponent c) {
397+
exists(Content content |
398+
c = SummaryComponent::content(content) and
399+
(
400+
content instanceof ArrayContent and result = "ArrayElement"
401+
or
402+
content instanceof MapValueContent and result = "MapValue"
403+
or
404+
content instanceof MapKeyContent and result = "MapKey"
405+
or
406+
content instanceof CollectionContent and result = "Element"
407+
or
408+
result = "Field[" + content.(FieldContent).getField().getQualifiedName() + "]"
409+
)
410+
)
411+
}
412+
374413
/**
375414
* Returns a CSV row describing a support method (`newWith` or `get` method) needed to set up the output for this test.
376415
*
@@ -379,15 +418,15 @@ class RowTestSnippet extends TRowTestSnippet {
379418
*/
380419
string getASupportMethodModel() {
381420
exists(SummaryComponent c, string contentSsvDescription |
382-
c = input.drop(_).head() and c = Private::External::interpretComponent(contentSsvDescription)
421+
c = input.drop(_).head() and contentSsvDescription = getComponentSpec(c)
383422
|
384423
result =
385424
"generatedtest;Test;false;newWith" + contentToken(getContent(c)) + ";;;Argument[0];" +
386425
contentSsvDescription + " of ReturnValue;value"
387426
)
388427
or
389428
exists(SummaryComponent c, string contentSsvDescription |
390-
c = output.drop(_).head() and c = Private::External::interpretComponent(contentSsvDescription)
429+
c = output.drop(_).head() and contentSsvDescription = getComponentSpec(c)
391430
|
392431
result =
393432
"generatedtest;Test;false;get" + contentToken(getContent(c)) + ";;;" + contentSsvDescription
@@ -428,10 +467,10 @@ class RowTestSnippet extends TRowTestSnippet {
428467
* Holds if type `t` does not clash with another type we want to import that has the same base name.
429468
*/
430469
predicate isImportable(Type t) {
431-
t = any(RowTestSnippet r).getADesiredImport() and
470+
t = any(TestCase tc).getADesiredImport() and
432471
t =
433472
unique(Type sharesBaseName |
434-
sharesBaseName = any(RowTestSnippet r).getADesiredImport() and
473+
sharesBaseName = any(TestCase tc).getADesiredImport() and
435474
sharesBaseName.getName() = t.getName()
436475
|
437476
sharesBaseName
@@ -444,7 +483,7 @@ predicate isImportable(Type t) {
444483
* if we cannot import it due to a name clash.
445484
*/
446485
string getShortNameIfPossible(Type t) {
447-
getRootSourceDeclaration(t) = any(RowTestSnippet r).getADesiredImport() and
486+
getRootSourceDeclaration(t) = any(TestCase tc).getADesiredImport() and
448487
if t instanceof RefType
449488
then
450489
exists(RefType replaced, string nestedName |
@@ -463,7 +502,7 @@ string getShortNameIfPossible(Type t) {
463502
*/
464503
string getAnImportStatement() {
465504
exists(RefType t |
466-
t = any(RowTestSnippet r).getADesiredImport() and
505+
t = any(TestCase tc).getADesiredImport() and
467506
isImportable(t) and
468507
t.getPackage().getName() != "java.lang"
469508
|
@@ -477,24 +516,24 @@ string getAnImportStatement() {
477516
string getASupportMethod() {
478517
result = "Object source() { return null; }" or
479518
result = "void sink(Object o) { }" or
480-
result = any(RowTestSnippet r).getASupportMethod()
519+
result = any(TestCase tc).getASupportMethod()
481520
}
482521

483522
/**
484523
* Returns a CSV specification of the taint-/value-propagation behaviour of a test support method (`get` or `newWith` method).
485524
*/
486-
query string getASupportMethodModel() { result = any(RowTestSnippet r).getASupportMethodModel() }
525+
query string getASupportMethodModel() { result = any(TestCase tc).getASupportMethodModel() }
487526

488527
/**
489-
* Gets a Java file body testing all `CsvRow` instances in scope against whatever classes and methods they resolve against.
528+
* Gets a Java file body testing all requested SSV rows against whatever classes and methods they resolve against.
490529
*/
491530
query string getTestCase() {
492531
result =
493532
"package generatedtest;\n\n" + concat(getAnImportStatement() + "\n") +
494-
"\n//Test case generated by GenerateFlowTestCase.ql\npublic class Test {\n\n" +
533+
"\n// Test case generated by GenerateFlowTestCase.ql\npublic class Test {\n\n" +
495534
concat("\t" + getASupportMethod() + "\n") + "\n\tpublic void test() {\n\n" +
496535
concat(string row, string snippet |
497-
snippet = any(RowTestSnippet r).getATestSnippetForRow(row)
536+
snippet = any(TestCase tc).getATestSnippetForRow(row)
498537
|
499538
snippet order by row
500539
) + "\n\t}\n\n}"

0 commit comments

Comments
 (0)