Skip to content

Commit

Permalink
Merge pull request #693 from tomato42/low-mem-usage
Browse files Browse the repository at this point in the history
Lower mem usage
  • Loading branch information
tomato42 committed Aug 25, 2020
2 parents cd8190d + b838068 commit 92cb1c4
Show file tree
Hide file tree
Showing 2 changed files with 286 additions and 35 deletions.
209 changes: 180 additions & 29 deletions tests/test_tlsfuzzer_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
try:
from tlsfuzzer.analysis import Analysis, main, TestPair, help_msg
import pandas as pd
import numpy as np
except ImportError:
failed_import = True

Expand All @@ -42,34 +43,35 @@ def setUp(self):
'B': [0, 0, 2, 6, 7] + [7] * 95,
})
timings = pd.DataFrame(data=data)
self.mock_read_csv = mock.Mock(spec=pd.read_csv)
self.mock_read_csv = mock.Mock()
self.mock_read_csv.return_value = timings

def test_report(self):
with mock.patch("tlsfuzzer.analysis.pd.read_csv", self.mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.load_data", self.mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.ecdf_plot") as mock_ecdf:
with mock.patch("tlsfuzzer.analysis.Analysis.box_plot") as mock_box:
with mock.patch("tlsfuzzer.analysis.Analysis.scatter_plot") as mock_scatter:
with mock.patch("tlsfuzzer.analysis.Analysis.conf_interval_plot") as mock_conf_int:
with mock.patch("__main__.__builtins__.open", mock.mock_open()) as mock_open:
with mock.patch("builtins.print"):
analysis = Analysis("/tmp")
ret = analysis.generate_report()

self.mock_read_csv.assert_called_once()
#mock_ecdf.assert_called_once()
#mock_box.assert_called_once()
#mock_scatter.assert_called_once()
# we're writing to report.csv, legend.csv, and
# report.txt
self.assertEqual(mock_open.call_count, 3)
self.assertEqual(ret, 0)
with mock.patch("tlsfuzzer.analysis.Analysis._convert_to_binary"):
analysis = Analysis("/tmp")
ret = analysis.generate_report()

self.mock_read_csv.assert_called_once()
#mock_ecdf.assert_called_once()
#mock_box.assert_called_once()
#mock_scatter.assert_called_once()
# we're writing to report.csv, legend.csv, and
# report.txt
self.assertEqual(mock_open.call_count, 3)
self.assertEqual(ret, 0)

def test_report_neq(self):
timings = pd.DataFrame(data=self.neq_data)
mock_read_csv = mock.Mock(spec=pd.read_csv)
mock_read_csv = mock.Mock()
mock_read_csv.return_value = timings
with mock.patch("tlsfuzzer.analysis.pd.read_csv", mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.load_data", mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.ecdf_plot") as mock_ecdf:
with mock.patch("tlsfuzzer.analysis.Analysis.box_plot") as mock_box:
with mock.patch("tlsfuzzer.analysis.Analysis.scatter_plot") as mock_scatter:
Expand All @@ -89,7 +91,7 @@ def test_report_neq(self):
self.assertEqual(ret, 1)

def test_wilcoxon_test(self):
with mock.patch("tlsfuzzer.analysis.pd.read_csv", self.mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.load_data", self.mock_read_csv):
analysis = Analysis("/tmp")
self.mock_read_csv.assert_called_once()

Expand All @@ -99,7 +101,7 @@ def test_wilcoxon_test(self):
self.assertGreaterEqual(result, 0.25)

def test_box_test(self):
with mock.patch("tlsfuzzer.analysis.pd.read_csv", self.mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.load_data", self.mock_read_csv):
analysis = Analysis("/tmp")
self.mock_read_csv.assert_called_once()

Expand All @@ -110,9 +112,9 @@ def test_box_test(self):

def test_box_test_neq(self):
timings = pd.DataFrame(data=self.neq_data)
mock_read_csv = mock.Mock(spec=pd.read_csv)
mock_read_csv = mock.Mock()
mock_read_csv.return_value = timings
with mock.patch("tlsfuzzer.analysis.pd.read_csv", mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.load_data", mock_read_csv):
analysis = Analysis("/tmp")

res = analysis.box_test()
Expand All @@ -122,9 +124,9 @@ def test_box_test_neq(self):

def test_box_test_neq_overlap(self):
timings = pd.DataFrame(data=self.neq_data_overlap)
mock_read_csv = mock.Mock(spec=pd.read_csv)
mock_read_csv = mock.Mock()
mock_read_csv.return_value = timings
with mock.patch("tlsfuzzer.analysis.pd.read_csv", mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.load_data", mock_read_csv):
analysis = Analysis("/tmp")
mock_read_csv.assert_called_once()

Expand All @@ -136,9 +138,9 @@ def test_box_test_neq_overlap(self):
def test__mean_of_random_sample(self):
diffs = [1, 2, 3, 4, 5, 6, 7, 8, 9]
timings = pd.DataFrame(data=self.neq_data_overlap)
mock_read_csv = mock.Mock(spec=pd.read_csv)
mock_read_csv = mock.Mock()
mock_read_csv.return_value = timings
with mock.patch("tlsfuzzer.analysis.pd.read_csv", mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.load_data", mock_read_csv):
with mock.patch("tlsfuzzer.analysis._diffs", diffs):
analysis = Analysis("/tmp")
vals = analysis._mean_of_random_sample(10)
Expand All @@ -151,9 +153,9 @@ def test__mean_of_random_sample(self):
def test__mean_of_random_sample_with_no_reps(self):
diffs = [1, 2, 3, 4, 5, 6, 7, 8, 9]
timings = pd.DataFrame(data=self.neq_data_overlap)
mock_read_csv = mock.Mock(spec=pd.read_csv)
mock_read_csv = mock.Mock()
mock_read_csv.return_value = timings
with mock.patch("tlsfuzzer.analysis.pd.read_csv", mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.load_data", mock_read_csv):
with mock.patch("tlsfuzzer.analysis._diffs", diffs):
analysis = Analysis("/tmp")
vals = analysis._mean_of_random_sample(0)
Expand All @@ -173,9 +175,9 @@ def setUp(self):
0.000734843, 0.000754852, 0.000667378, 0.000671230, 0.000790935]
}
timings = pd.DataFrame(data=data)
mock_read_csv = mock.Mock(spec=pd.read_csv)
mock_read_csv = mock.Mock()
mock_read_csv.return_value = timings
with mock.patch("tlsfuzzer.analysis.pd.read_csv", mock_read_csv):
with mock.patch("tlsfuzzer.analysis.Analysis.load_data", mock_read_csv):
self.analysis = Analysis("/tmp")

def test_ecdf_plot(self):
Expand All @@ -193,8 +195,28 @@ def test_scatter_plot(self):
def test_box_plot(self):
with mock.patch("tlsfuzzer.analysis.FigureCanvas.print_figure",
mock.Mock()) as mock_save:
self.analysis.box_plot()
mock_save.assert_called_once()
with mock.patch("tlsfuzzer.analysis.Analysis._calc_percentiles")\
as mock_percentiles:
mock_percentiles.return_value = pd.DataFrame(
data={'A': [0.05, 0.25, 0.5, 0.75, 0.95],
'B': [0.55, 0.75, 1, 1.25, 1.45]})
self.analysis.box_plot()
mock_save.assert_called_once()
mock_percentiles.assert_called_once_with()

@mock.patch("tlsfuzzer.analysis.np.memmap")
@mock.patch("tlsfuzzer.analysis.os.remove")
@mock.patch("tlsfuzzer.analysis.shutil.copyfile")
def test__calc_percentiles(self, mock_copyfile, mock_remove, mock_memmap):
mock_memmap.return_value = self.analysis.data.values

ret = self.analysis._calc_percentiles()

self.assertIsNotNone(ret)
self.assertEqual(ret.values[0, 0], 0.0006691114)
mock_copyfile.assert_called_once_with(
"/tmp/timing.bin", "/tmp/.quantiles.tmp")
mock_remove.assert_called_once_with("/tmp/.quantiles.tmp")

def test_conf_interval_plot(self):
with mock.patch("tlsfuzzer.analysis.FigureCanvas.print_figure",
Expand Down Expand Up @@ -250,3 +272,132 @@ def test_missing_output(self):
args = ["analysis.py"]
with mock.patch("sys.argv", args):
self.assertRaises(ValueError, main)


@unittest.skipIf(failed_import,
"Could not import analysis. Skipping related tests.")
class TestDataLoad(unittest.TestCase):

@classmethod
def setUpClass(cls):
cls.data = {
'A': [0.000758130, 0.000696718, 0.000980080, 0.000988899,
0.000875510, 0.000734843, 0.000754852, 0.000667378,
0.000671230, 0.000790935],
'B': [0.000758130, 0.000696718, 0.000980080, 0.000988899,
0.000875510, 0.000734843, 0.000754852, 0.000667378,
0.000671230, 0.000790935]
}
cls.df = pd.DataFrame(data=cls.data)
cls.legend = {
'ID': [0, 1],
'Name': ['A', 'B']
}

@staticmethod
def file_selector(name, mode="r"):
if name == "/tmp/timing.bin.shape":
return mock.mock_open(read_data="nrow,ncol\n10,2")(name, mode)
if name == "/tmp/legend.csv":
print("called with legend.csv")
return mock.mock_open(read_data="ID,Name\n0,A\n1,B")(name, mode)
return mock.mock_open(name, mode)

@mock.patch("tlsfuzzer.analysis.np.memmap")
@mock.patch("tlsfuzzer.analysis.pd.read_csv")
@mock.patch("builtins.open")
@mock.patch("tlsfuzzer.analysis.Analysis._convert_to_binary")
def test_load_data(self, convert_mock, open_mock, read_csv_mock,
memmap_mock):
open_mock.side_effect = self.file_selector
read_csv_mock.return_value = pd.DataFrame(data=self.legend)
memmap_mock.return_value = self.df.values

a = Analysis("/tmp")

self.assertTrue(a.data.equals(self.df))

convert_mock.assert_called_once_with()
read_csv_mock.assert_called_once_with("/tmp/legend.csv")
memmap_mock.assert_called_once_with(
"/tmp/timing.bin", dtype=np.float64, mode="r", shape=(10, 2),
order="C")

@mock.patch("builtins.open")
@mock.patch("tlsfuzzer.analysis.Analysis._convert_to_binary")
def test_load_data_with_wrong_shape_file(self, convert_mock, open_mock):
open_mock.side_effect = lambda a, b:\
mock.mock_open(read_data="som,wrong,file\n1,2,3")(a, b)

with self.assertRaises(ValueError) as err:
Analysis("/tmp")

self.assertIn("Malformed /tmp/timing.bin.shape ", str(err.exception))
convert_mock.assert_called_once()

@mock.patch("tlsfuzzer.analysis.pd.read_csv")
@mock.patch("builtins.open")
@mock.patch("tlsfuzzer.analysis.Analysis._convert_to_binary")
def test_load_data_with_inconsistent_legend_and_shape(self, convert_mock,
open_mock, read_csv_mock):
open_mock.side_effect = self.file_selector
read_csv_mock.return_value = pd.DataFrame(data=
{"A": [0, 1, 2], "B": [0, 1, 2], "C": [3, 4, 5]})

with self.assertRaises(ValueError) as err:
Analysis("/tmp")

self.assertIn("Inconsistent /tmp/legend.csv and /tmp/timing.bin.shape",
str(err.exception))
convert_mock.assert_called_once()
read_csv_mock.assert_called_once()

@mock.patch("tlsfuzzer.analysis.os.path.getmtime")
@mock.patch("tlsfuzzer.analysis.os.path.isfile")
@mock.patch("tlsfuzzer.analysis.np.memmap")
@mock.patch("tlsfuzzer.analysis.pd.read_csv")
@mock.patch("builtins.open")
def test__convert_to_binary_with_noop(self, open_mock, read_csv_mock,
memmap_mock, isfile_mock, getmtime_mock):
open_mock.side_effect = self.file_selector
read_csv_mock.return_value = pd.DataFrame(data=self.legend)
memmap_mock.return_value = self.df.values
isfile_mock.return_value = True
getmtime_mock.side_effect = lambda f_name: \
1 if f_name == "/tmp/timing.bin" else 0

a = Analysis("/tmp")

self.assertTrue(a.data.equals(self.df))

read_csv_mock.assert_called_once_with("/tmp/legend.csv")
memmap_mock.assert_called_once_with(
"/tmp/timing.bin", dtype=np.float64, mode="r", shape=(10, 2),
order="C")
self.assertEqual(isfile_mock.call_args_list,
[mock.call("/tmp/timing.bin"),
mock.call("/tmp/legend.csv"),
mock.call("/tmp/timing.bin.shape")])

@staticmethod
def mock_memmap(name, dtype, mode, shape, order):
return np.empty(shape, dtype, order)

@mock.patch("tlsfuzzer.analysis.np.memmap")
@mock.patch("builtins.open")
@mock.patch("tlsfuzzer.analysis.pd.read_csv")
@mock.patch("tlsfuzzer.analysis.os.path.getmtime")
@mock.patch("tlsfuzzer.analysis.os.path.isfile")
def test__convert_to_binary_refresh(self, isfile_mock, getmtime_mock,
read_csv_mock, open_mock, memmap_mock):
isfile_mock.return_value = True
getmtime_mock.return_value = 0
read_csv_mock.side_effect = lambda _, chunksize, dtype: \
iter(self.df[i:i+1] for i in range(self.df.shape[0]))
open_mock.side_effect = self.file_selector
memmap_mock.side_effect = self.mock_memmap

a = Analysis.__new__(Analysis)
a.output = "/tmp"

a._convert_to_binary()

0 comments on commit 92cb1c4

Please sign in to comment.