Skip to content

Commit 7cdc8e9

Browse files
author
Owen Jones
committed
Add CSVSA tests folder
The test mainly checks that security scanner doesn't crash. I've tried to structure it so that we could reuse some of the code to use pytest to check test.desc files written for test.pl. More work would be needed to implement this.
1 parent d67621f commit 7cdc8e9

File tree

9 files changed

+122
-4
lines changed

9 files changed

+122
-4
lines changed
551 Bytes
Binary file not shown.

regression/CSVSA/TestBasic/Test.class

513 Bytes
Binary file not shown.

regression/CSVSA/TestBasic/Test.java

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package CSVSA.TestBasic;
2+
3+
public class Test {
4+
class A {
5+
public void f() {
6+
}
7+
8+
public void g() {
9+
}
10+
}
11+
12+
public void tolerate_unreachable_functions() {
13+
A a = null;
14+
a.f();
15+
a.g();
16+
}
17+
}

regression/CSVSA/TestBasic/__init__.py

Whitespace-only changes.
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from regression.CSVSA.csvsa_driver import CsvsaDriver
2+
3+
4+
folder_name = 'TestBasic'
5+
6+
7+
def test_tolerate_unreachable_functions():
8+
csvsa_driver = CsvsaDriver(folder_name).with_test_function('tolerate_unreachable_functions')
9+
csvsa_expectation = csvsa_driver.run()
10+
11+
csvsa_expectation.check_successful_run()
12+
csvsa_expectation.check_does_not_match("a \. f\(\)")
13+
csvsa_expectation.check_does_not_match("a \. g\(\)")
14+
csvsa_expectation.check_does_not_match("a \. f\(\)")
15+
csvsa_expectation.check_does_not_match("a \. g\(\)")

regression/CSVSA/__init__.py

Whitespace-only changes.

regression/CSVSA/csvsa_driver.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import os
2+
3+
from regression.executable_runner import ExecutableRunner
4+
from regression.regex_expectation import RegexExpectation
5+
6+
7+
class CsvsaExpectation(RegexExpectation):
8+
"""Encapsulate the output of CsvsaDriver"""
9+
10+
def __init__(self, goto_functions, return_code):
11+
"""Get the state at the end of test_function"""
12+
RegexExpectation.__init__(self, goto_functions, return_code, compatibility_mode=False, check_verification=False)
13+
14+
15+
class CsvsaDriver:
16+
"""Run security scanner with CSVSA"""
17+
18+
def __init__(self, folder_name):
19+
"""Set the class path and temporary directory"""
20+
self.folder_name = folder_name
21+
self.with_test_class_name('Test')
22+
self.with_test_function('f')
23+
24+
def with_test_class_name(self, test_class_name):
25+
self.class_name = test_class_name
26+
self.class_filename = test_class_name + '.class'
27+
return self
28+
29+
def with_test_function(self, test_function_name):
30+
self.function_name = test_function_name
31+
return self
32+
33+
def run(self):
34+
"""Run CSVSA and parse the output before returning it"""
35+
fq_class_name = '.'.join(['CSVSA', self.folder_name, self.class_name])
36+
fq_function_name = fq_class_name + '.' + self.function_name
37+
executable_path = os.path.join(os.environ['SECURITY_SCANNER_HOME'], 'bin', 'security-analyzer')
38+
class_path = os.path.join('CSVSA', self.folder_name, self.class_filename)
39+
cmd = [executable_path, '--function', fq_function_name, '--lazy-methods-context-sensitive',
40+
'--show-goto-functions', class_path]
41+
42+
executable_runner = ExecutableRunner(cmd)
43+
(stdout, _, return_code) = executable_runner.run(pipe_stderr_to_stdout=True)
44+
45+
return CsvsaExpectation(stdout, return_code)

regression/executable_runner.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def __init__(self, cmd, timeout=600, encoding='utf-8'):
1313
self.timeout = timeout
1414
self.encoding = encoding
1515

16-
def run(self):
16+
def run(self, pipe_stderr_to_stdout=False):
1717
"""Run the executable and capture the output
1818
1919
If the process exceeds the timeout then we assert false so that the test fails.
@@ -22,13 +22,14 @@ def run(self):
2222
"""
2323
# We limit ourselves to the API available in python 3.3 to make life easier on Travis. There are API
2424
# improvements in python 3.5 and 3.6 which would make this code simpler.
25-
proc = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
25+
stderr = subprocess.STDOUT if pipe_stderr_to_stdout else subprocess.PIPE
26+
proc = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=stderr)
2627
try:
2728
(stdout_data, stderr_data) = proc.communicate(timeout=self.timeout)
2829
except subprocess.TimeoutExpired:
2930
proc.kill()
3031
print('The executable ran for longer than {} seconds'.format(self.timeout))
3132
assert False
32-
return (stdout_data.decode(self.encoding),
33-
stderr_data.decode(self.encoding),
33+
return (stdout_data.decode(self.encoding) if stdout_data else None,
34+
stderr_data.decode(self.encoding) if stderr_data else None,
3435
proc.returncode)

regression/regex_expectation.py

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import re
2+
3+
4+
class RegexExpectation:
5+
"""Encapsulate the output of CsvsaDriver"""
6+
7+
def __init__(self, text, return_code, compatibility_mode=True, check_verification=False):
8+
"""Get the state at the end of test_function"""
9+
if return_code < 0:
10+
self.exit_code = 0
11+
self.signal = -return_code
12+
else:
13+
self.exit_code = return_code
14+
self.signal = 0
15+
if compatibility_mode:
16+
# This makes is easier to use this code with test.desc files written for test.pl
17+
text += "\nEXIT={}\nSIGNAL={}\n".format(self.exit_code, self.signal)
18+
self.text = text
19+
self.split_text = text.splitlines()
20+
# check_verification indicates whether a successful run should include "VERIFICATION SUCCESSFUL" in it
21+
self.check_verification = check_verification
22+
23+
def check_successful_run(self):
24+
self.check_does_not_match_regex("^warning: ignoring")
25+
if self.check_verification:
26+
self.check_matches_regex("^VERIFICATION SUCCESSFUL$")
27+
assert self.exit_code == 0
28+
assert self.signal == 0
29+
30+
def check_matches(self, x):
31+
assert any(line.find(x) != -1 for line in self.split_text)
32+
33+
def check_does_not_match(self, x):
34+
assert all(line.find(x) == -1 for line in self.split_text)
35+
36+
def check_matches_regex(self, x):
37+
assert any(re.search(x, line) for line in self.split_text)
38+
39+
def check_does_not_match_regex(self, x):
40+
assert all(not re.search(x, line) for line in self.split_text)

0 commit comments

Comments
 (0)