12
12
import tempfile
13
13
14
14
if any (s == "--help" for s in sys .argv ):
15
- print ("""Usage:
15
+ print ("""Usage:
16
16
GenerateFlowTestCase.py specsToTest.csv projectPom.xml outdir
17
17
18
18
This generates test cases exercising function model specifications found in specsToTest.csv
27
27
After test generation completes, any lines in specsToTest.csv that didn't produce tests are output.
28
28
If this happens, check the spelling of class and method names, and the syntax of input and output specifications.
29
29
""" )
30
- sys .exit (0 )
30
+ sys .exit (0 )
31
31
32
32
if len (sys .argv ) != 4 :
33
- print ("Usage: GenerateFlowTestCase.py specsToTest.csv projectPom.xml outdir" , file = sys .stderr )
34
- print ("specsToTest.csv should contain CSV rows describing method taint-propagation specifications to test" , file = sys .stderr )
35
- print ("projectPom.xml should import dependencies sufficient to resolve the types used in specsToTest.csv" , file = sys .stderr )
36
- sys .exit (1 )
33
+ print ("Usage: GenerateFlowTestCase.py specsToTest.csv projectPom.xml outdir" , file = sys .stderr )
34
+ print ("specsToTest.csv should contain CSV rows describing method taint-propagation specifications to test" , file = sys .stderr )
35
+ print ("projectPom.xml should import dependencies sufficient to resolve the types used in specsToTest.csv" , file = sys .stderr )
36
+ sys .exit (1 )
37
37
38
38
try :
39
- os .makedirs (sys .argv [3 ])
39
+ os .makedirs (sys .argv [3 ])
40
40
except Exception as e :
41
- if e .errno != errno .EEXIST :
42
- print ("Failed to create output directory %s: %s" % (sys .argv [3 ], e ))
43
- sys .exit (1 )
41
+ if e .errno != errno .EEXIST :
42
+ print ("Failed to create output directory %s: %s" % (sys .argv [3 ], e ))
43
+ sys .exit (1 )
44
44
45
45
resultJava = os .path .join (sys .argv [3 ], "Test.java" )
46
46
resultQl = os .path .join (sys .argv [3 ], "test.ql" )
47
47
48
48
if os .path .exists (resultJava ) or os .path .exists (resultQl ):
49
- print ("Won't overwrite existing files '%s' or '%s'" % (resultJava , resultQl ), file = sys .stderr )
50
- sys .exit (1 )
49
+ print ("Won't overwrite existing files '%s' or '%s'" %
50
+ (resultJava , resultQl ), file = sys .stderr )
51
+ sys .exit (1 )
51
52
52
53
workDir = tempfile .mkdtemp ()
53
54
57
58
os .makedirs (projectDir )
58
59
59
60
try :
60
- shutil .copyfile (sys .argv [2 ], os .path .join (projectDir , "pom.xml" ))
61
+ shutil .copyfile (sys .argv [2 ], os .path .join (projectDir , "pom.xml" ))
61
62
except Exception as e :
62
- print ("Failed to read project POM %s: %s" % (sys .argv [2 ], e ), file = sys .stderr )
63
- sys .exit (1 )
63
+ print ("Failed to read project POM %s: %s" %
64
+ (sys .argv [2 ], e ), file = sys .stderr )
65
+ sys .exit (1 )
64
66
65
67
commentRegex = re .compile ("^\s*(//|#)" )
68
+
69
+
66
70
def isComment (s ):
67
- return commentRegex .match (s ) is not None
71
+ return commentRegex .match (s ) is not None
72
+
68
73
69
74
try :
70
- with open (sys .argv [1 ], "r" ) as f :
71
- specs = [l for l in f if not isComment (l )]
75
+ with open (sys .argv [1 ], "r" ) as f :
76
+ specs = [l for l in f if not isComment (l )]
72
77
except Exception as e :
73
- print ("Failed to open %s: %s\n " % (sys .argv [1 ], e ))
74
- sys .exit (1 )
78
+ print ("Failed to open %s: %s\n " % (sys .argv [1 ], e ))
79
+ sys .exit (1 )
75
80
76
81
projectTestPkgDir = os .path .join (projectDir , "src" , "main" , "java" , "test" )
77
82
projectTestFile = os .path .join (projectTestPkgDir , "Test.java" )
78
83
79
84
os .makedirs (projectTestPkgDir )
80
85
86
+
81
87
def qualifiedOuterNameFromCsvRow (row ):
82
- cells = row .split (";" )
83
- if len (cells ) < 2 :
84
- return None
85
- return cells [0 ] + "." + cells [1 ].replace ("$" , "." )
88
+ cells = row .split (";" )
89
+ if len (cells ) < 2 :
90
+ return None
91
+ return cells [0 ] + "." + cells [1 ].replace ("$" , "." )
92
+
86
93
87
94
with open (projectTestFile , "w" ) as testJava :
88
- testJava .write ("package test;\n \n public class Test {\n \n " )
95
+ testJava .write ("package test;\n \n public class Test {\n \n " )
89
96
90
- for i , spec in enumerate (specs ):
91
- outerName = qualifiedOuterNameFromCsvRow (spec )
92
- if outerName is None :
93
- print ("A taint specification has the wrong format: should be 'package;classname;methodname....'" , file = sys .stderr )
94
- print ("Mis-formatted row: " + spec , file = sys .stderr )
95
- sys .exit (1 )
96
- testJava .write ("\t %s obj%d = null;\n " % (outerName , i ))
97
+ for i , spec in enumerate (specs ):
98
+ outerName = qualifiedOuterNameFromCsvRow (spec )
99
+ if outerName is None :
100
+ print ("A taint specification has the wrong format: should be 'package;classname;methodname....'" , file = sys .stderr )
101
+ print ("Mis-formatted row: " + spec , file = sys .stderr )
102
+ sys .exit (1 )
103
+ testJava .write ("\t %s obj%d = null;\n " % (outerName , i ))
97
104
98
- testJava .write ("}" )
105
+ testJava .write ("}" )
99
106
100
107
print ("Creating project database" )
101
108
cmd = ["codeql" , "database" , "create" , "--language=java" , "db" ]
102
- ret = subprocess .call (cmd , cwd = projectDir )
109
+ ret = subprocess .call (cmd , cwd = projectDir )
103
110
if ret != 0 :
104
- print ("Failed to create project database. Check that '%s' is a valid POM that pulls in all necessary dependencies, and '%s' specifies valid classes and methods." % (sys .argv [2 ], sys .argv [1 ]), file = sys .stderr )
105
- print ("Failed command was: %s (cwd: %s)" % (shlex .join (cmd ), projectDir ), file = sys .stderr )
106
- sys .exit (1 )
111
+ print ("Failed to create project database. Check that '%s' is a valid POM that pulls in all necessary dependencies, and '%s' specifies valid classes and methods." % (
112
+ sys .argv [2 ], sys .argv [1 ]), file = sys .stderr )
113
+ print ("Failed command was: %s (cwd: %s)" %
114
+ (shlex .join (cmd ), projectDir ), file = sys .stderr )
115
+ sys .exit (1 )
107
116
108
117
print ("Creating test-generation query" )
109
118
queryDir = os .path .join (workDir , "query" )
110
119
os .makedirs (queryDir )
111
120
qlFile = os .path .join (queryDir , "gen.ql" )
112
121
with open (os .path .join (queryDir , "qlpack.yml" ), "w" ) as f :
113
- f .write ("name: test-generation-query\n version: 0.0.0\n libraryPathDependencies: codeql-java" )
122
+ f .write ("name: test-generation-query\n version: 0.0.0\n libraryPathDependencies: codeql-java" )
114
123
with open (qlFile , "w" ) as f :
115
- f .write ("import java\n import utils.GenerateFlowTestCase\n \n class GenRow extends TargetSummaryModelCsv {\n \n \t override predicate row(string r) {\n \t \t r = [\n " )
116
- f .write (",\n " .join ('\t \t \t "%s"' % spec .strip () for spec in specs ))
117
- f .write ("\n \t \t ]\n \t }\n }\n " )
124
+ f .write (
125
+ "import java\n import utils.GenerateFlowTestCase\n \n class GenRow extends TargetSummaryModelCsv {\n \n \t override predicate row(string r) {\n \t \t r = [\n " )
126
+ f .write (",\n " .join ('\t \t \t "%s"' % spec .strip () for spec in specs ))
127
+ f .write ("\n \t \t ]\n \t }\n }\n " )
118
128
119
129
print ("Generating tests" )
120
130
generatedBqrs = os .path .join (queryDir , "out.bqrs" )
121
- cmd = ['codeql' , 'query' , 'run' , qlFile , '--database' , os .path .join (projectDir , "db" ), '--output' , generatedBqrs ]
131
+ cmd = ['codeql' , 'query' , 'run' , qlFile , '--database' ,
132
+ os .path .join (projectDir , "db" ), '--output' , generatedBqrs ]
122
133
ret = subprocess .call (cmd )
123
134
if ret != 0 :
124
- print ("Failed to generate tests. Failed command was: " + shlex .join (cmd ))
125
- sys .exit (1 )
135
+ print ("Failed to generate tests. Failed command was: " + shlex .join (cmd ))
136
+ sys .exit (1 )
126
137
127
138
generatedJson = os .path .join (queryDir , "out.json" )
128
- cmd = ['codeql' , 'bqrs' , 'decode' , generatedBqrs , '--format=json' , '--output' , generatedJson ]
139
+ cmd = ['codeql' , 'bqrs' , 'decode' , generatedBqrs ,
140
+ '--format=json' , '--output' , generatedJson ]
129
141
ret = subprocess .call (cmd )
130
142
if ret != 0 :
131
- print ("Failed to decode BQRS. Failed command was: " + shlex .join (cmd ))
132
- sys .exit (1 )
133
-
134
- def getTuples (queryName , jsonResult , fname ):
135
- if queryName not in jsonResult or "tuples" not in jsonResult [queryName ]:
136
- print ("Failed to read generated tests: expected key '%s' with a 'tuples' subkey in file '%s'" % (queryName , fname ), file = sys .stderr )
143
+ print ("Failed to decode BQRS. Failed command was: " + shlex .join (cmd ))
137
144
sys .exit (1 )
138
- return jsonResult [queryName ]["tuples" ]
139
145
140
- with open (generatedJson , "r" ) as f :
141
- generateOutput = json .load (f )
142
- expectedTables = ("getTestCase" , "getASupportMethodModel" , "missingSummaryModelCsv" , "getAParseFailure" )
143
146
144
- testCaseRows , supportModelRows , missingSummaryModelCsvRows , parseFailureRows = \
145
- tuple ([getTuples (k , generateOutput , generatedJson ) for k in expectedTables ])
147
+ def getTuples (queryName , jsonResult , fname ):
148
+ if queryName not in jsonResult or "tuples" not in jsonResult [queryName ]:
149
+ print ("Failed to read generated tests: expected key '%s' with a 'tuples' subkey in file '%s'" % (
150
+ queryName , fname ), file = sys .stderr )
151
+ sys .exit (1 )
152
+ return jsonResult [queryName ]["tuples" ]
146
153
147
- if len (testCaseRows ) != 1 or len (testCaseRows [0 ]) != 1 :
148
- print ("Expected exactly one getTestCase result with one column (got: %s)" % json .dumps (testCaseRows ), file = sys .stderr )
149
- if any (len (row ) != 1 for row in supportModelRows ):
150
- print ("Expected exactly one column in getASupportMethodModel relation (got: %s)" % json .dumps (supportModelRows ), 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
154
- if len (missingSummaryModelCsvRows ) != 0 :
155
- 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 ))
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 )
155
+ with open (generatedJson , "r" ) as f :
156
+ generateOutput = json .load (f )
157
+ expectedTables = ("getTestCase" , "getASupportMethodModel" ,
158
+ "missingSummaryModelCsv" , "getAParseFailure" )
159
+
160
+ testCaseRows , supportModelRows , missingSummaryModelCsvRows , parseFailureRows = \
161
+ tuple ([getTuples (k , generateOutput , generatedJson )
162
+ for k in expectedTables ])
163
+
164
+ if len (testCaseRows ) != 1 or len (testCaseRows [0 ]) != 1 :
165
+ print ("Expected exactly one getTestCase result with one column (got: %s)" %
166
+ json .dumps (testCaseRows ), file = sys .stderr )
167
+ if any (len (row ) != 1 for row in supportModelRows ):
168
+ print ("Expected exactly one column in getASupportMethodModel relation (got: %s)" %
169
+ json .dumps (supportModelRows ), file = sys .stderr )
170
+ if any (len (row ) != 2 for row in parseFailureRows ):
171
+ print ("Expected exactly two columns in parseFailureRows relation (got: %s)" %
172
+ json .dumps (parseFailureRows ), file = sys .stderr )
173
+
174
+ if len (missingSummaryModelCsvRows ) != 0 :
175
+ print ("Tests for some CSV rows were requested that were not in scope (SummaryModelCsv.row does not hold):\n " +
176
+ "\n " .join (r [0 ] for r in missingSummaryModelCsvRows ))
177
+ sys .exit (1 )
178
+ if len (parseFailureRows ) != 0 :
179
+ print ("The following rows failed to generate any test case. Check package, class and method name spelling, and argument and result specifications:\n %s" %
180
+ "\n " .join (r [0 ] + ": " + r [1 ] for r in parseFailureRows ), file = sys .stderr )
181
+ sys .exit (1 )
160
182
161
183
with open (resultJava , "w" ) as f :
162
- f .write (generateOutput ["getTestCase" ]["tuples" ][0 ][0 ])
184
+ f .write (generateOutput ["getTestCase" ]["tuples" ][0 ][0 ])
163
185
164
186
scriptPath = os .path .dirname (sys .argv [0 ])
165
187
188
+
166
189
def copyfile (fromName , toFileHandle ):
167
- with open (os .path .join (scriptPath , fromName ), "r" ) as fromFileHandle :
168
- shutil .copyfileobj (fromFileHandle , toFileHandle )
190
+ with open (os .path .join (scriptPath , fromName ), "r" ) as fromFileHandle :
191
+ shutil .copyfileobj (fromFileHandle , toFileHandle )
192
+
169
193
170
194
with open (resultQl , "w" ) as f :
171
- copyfile ("testHeader.qlfrag" , f )
172
- if len (supportModelRows ) != 0 :
173
- copyfile ("testModelsHeader.qlfrag" , f )
174
- f .write (", " .join ('"%s"' % modelSpecRow [0 ].strip () for modelSpecRow in supportModelRows ))
175
- copyfile ("testModelsFooter.qlfrag" , f )
176
- copyfile ("testFooter.qlfrag" , f )
195
+ copyfile ("testHeader.qlfrag" , f )
196
+ if len (supportModelRows ) != 0 :
197
+ copyfile ("testModelsHeader.qlfrag" , f )
198
+ f .write (", " .join ('"%s"' %
199
+ modelSpecRow [0 ].strip () for modelSpecRow in supportModelRows ))
200
+ copyfile ("testModelsFooter.qlfrag" , f )
201
+ copyfile ("testFooter.qlfrag" , f )
177
202
178
203
# Make an empty .expected file, since this is an inline-exectations test
179
204
with open (os .path .join (sys .argv [3 ], "test.expected" ), "w" ):
180
- pass
205
+ pass
181
206
182
207
cmd = ['codeql' , 'query' , 'format' , '-qq' , '-i' , resultQl ]
183
208
subprocess .call (cmd )
184
209
185
- shutil .rmtree (workDir )
210
+ shutil .rmtree (workDir )
0 commit comments