diff --git a/sdmetrics/reports/base_report.py b/sdmetrics/reports/base_report.py index 5b02a70e..bb6a6b0a 100644 --- a/sdmetrics/reports/base_report.py +++ b/sdmetrics/reports/base_report.py @@ -159,6 +159,8 @@ def generate(self, real_data, synthetic_data, metadata, verbose=True): verbose (bool): Whether or not to print report summary and progress. """ + real_data = deepcopy(real_data) + synthetic_data = deepcopy(synthetic_data) self._validate(real_data, synthetic_data, metadata) self._skipped_properties = self._get_skipped_properties(metadata) self._original_datetime_columns = self.convert_datetimes( diff --git a/tests/integration/reports/multi_table/test_diagnostic_report.py b/tests/integration/reports/multi_table/test_diagnostic_report.py index f7d76af1..fdfa4bdf 100644 --- a/tests/integration/reports/multi_table/test_diagnostic_report.py +++ b/tests/integration/reports/multi_table/test_diagnostic_report.py @@ -373,3 +373,21 @@ def test_metadata_without_relationship(self): # Run and Assert report.generate(real_data, synthetic_data, metadata) + + +def test_report_keeps_data_unchanged(): + """Test that the diagnostic report does not modify the input data.""" + # Setup + real_data, synthetic_data, metadata = load_demo(modality='multi_table') + real_data_copy = {table_name: table.copy() for table_name, table in real_data.items()} + synthetic_data_copy = {table_name: table.copy() for table_name, table in synthetic_data.items()} + report = DiagnosticReport() + + # Run + report.generate(real_data, synthetic_data, metadata, verbose=True) + + # Assert + for table_name in real_data: + pd.testing.assert_frame_equal(real_data[table_name], real_data_copy[table_name]) + for table_name in synthetic_data: + pd.testing.assert_frame_equal(synthetic_data[table_name], synthetic_data_copy[table_name]) diff --git a/tests/integration/reports/multi_table/test_quality_report.py b/tests/integration/reports/multi_table/test_quality_report.py index fa9cab44..8faf13f4 100644 --- a/tests/integration/reports/multi_table/test_quality_report.py +++ b/tests/integration/reports/multi_table/test_quality_report.py @@ -564,3 +564,21 @@ def test_with_large_dataset(): assert np.isclose(report_2.get_score(), score_1_run_1, atol=0.001) assert np.isclose(cpt_report_1, cpt_report_2, atol=0.001) assert np.isclose(intertable_trends_1, intertable_trends_2, atol=0.001) + + +def test_report_keeps_data_unchanged(): + """Test that the quality report does not modify the input data.""" + # Setup + real_data, synthetic_data, metadata = load_demo(modality='multi_table') + real_data_copy = {table_name: table.copy() for table_name, table in real_data.items()} + synthetic_data_copy = {table_name: table.copy() for table_name, table in synthetic_data.items()} + report = QualityReport() + + # Run + report.generate(real_data, synthetic_data, metadata, verbose=True) + + # Assert + for table_name in real_data: + pd.testing.assert_frame_equal(real_data[table_name], real_data_copy[table_name]) + for table_name in synthetic_data: + pd.testing.assert_frame_equal(synthetic_data[table_name], synthetic_data_copy[table_name]) diff --git a/tests/unit/reports/test_base_report.py b/tests/unit/reports/test_base_report.py index 19f6b2f0..32aa85d9 100644 --- a/tests/unit/reports/test_base_report.py +++ b/tests/unit/reports/test_base_report.py @@ -305,14 +305,19 @@ def test_generate(self, version_mock, time_mock, datetime_mock): base_report.generate(real_data, synthetic_data, metadata, verbose=False) # Assert - mock_validate.assert_called_once_with(real_data, synthetic_data, metadata) + mock_validate.assert_called_once() + copied_real_data, copied_synthetic_data, _ = mock_validate.call_args.args + pd.testing.assert_frame_equal(copied_real_data, real_data) + pd.testing.assert_frame_equal(copied_synthetic_data, synthetic_data) + assert copied_real_data is not real_data + assert copied_synthetic_data is not synthetic_data mock__print_results.assert_called_once_with(False) base_report._properties['Property 1'].get_score.assert_called_with( - real_data, synthetic_data, metadata, progress_bar=None + copied_real_data, copied_synthetic_data, metadata, progress_bar=None ) assert base_report._properties['Property 1'].num_rows_subsample == 1000 base_report._properties['Property 2'].get_score.assert_called_with( - real_data, synthetic_data, metadata, progress_bar=None + copied_real_data, copied_synthetic_data, metadata, progress_bar=None ) assert base_report._properties['Property 2'].num_rows_subsample == 1000 expected_info = { @@ -377,11 +382,20 @@ def test_generate_multi_table_details(self, version_mock, time_mock, datetime_mo base_report.generate(real_data, synthetic_data, metadata, verbose=False) # Assert + copied_real_data, copied_synthetic_data, _ = base_report._validate.call_args.args + for table_name, table in real_data.items(): + pd.testing.assert_frame_equal(copied_real_data[table_name], table) + pd.testing.assert_frame_equal( + copied_synthetic_data[table_name], synthetic_data[table_name] + ) + + assert copied_real_data is not real_data + assert copied_synthetic_data is not synthetic_data base_report._properties['Property 1'].get_score.assert_called_with( - real_data, synthetic_data, metadata, progress_bar=None + copied_real_data, copied_synthetic_data, metadata, progress_bar=None ) base_report._properties['Property 2'].get_score.assert_called_with( - real_data, synthetic_data, metadata, progress_bar=None + copied_real_data, copied_synthetic_data, metadata, progress_bar=None ) expected_info = { 'report_type': 'BaseReport', @@ -483,6 +497,11 @@ def test_generate_verbose_with_skipped_property(self, mock_tqdm, mock_write): base_report.generate(real_data, synthetic_data, metadata, verbose=True) # Assert + copied_real_data, copied_synthetic_data, _ = base_report._validate.call_args.args + pd.testing.assert_frame_equal(copied_real_data, real_data) + pd.testing.assert_frame_equal(copied_synthetic_data, synthetic_data) + assert copied_real_data is not real_data + assert copied_synthetic_data is not synthetic_data base_report._properties['Property 1'].get_score.assert_not_called() base_report._properties['Property 1']._get_num_iterations.assert_not_called() assert base_report._properties['Property 1'].details.empty @@ -490,7 +509,7 @@ def test_generate_verbose_with_skipped_property(self, mock_tqdm, mock_write): assert base_report._skipped_properties == {'Property 1'} base_report._get_skipped_properties.assert_called_once_with(metadata) base_report._properties['Property 2'].get_score.assert_called_once_with( - real_data, synthetic_data, metadata, progress_bar=mock_tqdm.return_value + copied_real_data, copied_synthetic_data, metadata, progress_bar=mock_tqdm.return_value ) mock_write.assert_any_call('Generating report ...\n\n') mock_write.assert_any_call(