# Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for coverage.py""" import os import json import unittest from unittest import mock import coverage # pylint: disable=protected-access TEST_FILES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_files') PROJECT_NAME = 'curl' REPO_PATH = '/src/curl' FUZZ_TARGET = 'curl_fuzzer' PROJECT_COV_JSON_FILENAME = 'example_curl_cov.json' FUZZ_TARGET_COV_JSON_FILENAME = 'example_curl_fuzzer_cov.json' INVALID_TARGET = 'not-a-fuzz-target' with open(os.path.join(TEST_FILES_PATH, PROJECT_COV_JSON_FILENAME),) as cov_file_handle: PROJECT_COV_INFO = json.loads(cov_file_handle.read()) class GetFuzzerStatsDirUrlTest(unittest.TestCase): """Tests _get_fuzzer_stats_dir_url.""" @mock.patch('coverage.get_json_from_url', return_value={ 'fuzzer_stats_dir': 'gs://oss-fuzz-coverage/systemd/fuzzer_stats/20210303' }) def test_get_valid_project(self, mocked_get_json_from_url): """Tests that a project's coverage report can be downloaded and parsed. NOTE: This test relies on the PROJECT_NAME repo's coverage report. The "example" project was not used because it has no coverage reports. """ result = coverage._get_fuzzer_stats_dir_url(PROJECT_NAME) (url,), _ = mocked_get_json_from_url.call_args self.assertEqual( 'https://storage.googleapis.com/oss-fuzz-coverage/' 'latest_report_info/curl.json', url) expected_result = ( 'https://storage.googleapis.com/oss-fuzz-coverage/systemd/fuzzer_stats/' '20210303') self.assertEqual(result, expected_result) def test_get_invalid_project(self): """Tests that passing a bad project returns None.""" self.assertIsNone(coverage._get_fuzzer_stats_dir_url('not-a-proj')) class GetTargetCoverageReportTest(unittest.TestCase): """Tests get_target_coverage_report.""" def setUp(self): with mock.patch('coverage._get_latest_cov_report_info', return_value=PROJECT_COV_INFO): self.coverage_getter = coverage.OssFuzzCoverageGetter( PROJECT_NAME, REPO_PATH) @mock.patch('coverage.get_json_from_url', return_value={}) def test_valid_target(self, mocked_get_json_from_url): """Tests that a target's coverage report can be downloaded and parsed.""" self.coverage_getter.get_target_coverage_report(FUZZ_TARGET) (url,), _ = mocked_get_json_from_url.call_args self.assertEqual( 'https://storage.googleapis.com/oss-fuzz-coverage/' 'curl/fuzzer_stats/20200226/curl_fuzzer.json', url) def test_invalid_target(self): """Tests that passing an invalid target coverage report returns None.""" self.assertIsNone( self.coverage_getter.get_target_coverage_report(INVALID_TARGET)) @mock.patch('coverage._get_latest_cov_report_info', return_value=None) def test_invalid_project_json(self, _): """Tests an invalid project JSON results in None being returned.""" coverage_getter = coverage.OssFuzzCoverageGetter(PROJECT_NAME, REPO_PATH) self.assertIsNone(coverage_getter.get_target_coverage_report(FUZZ_TARGET)) class GetFilesCoveredByTargetTest(unittest.TestCase): """Tests get_files_covered_by_target.""" def setUp(self): with mock.patch('coverage._get_latest_cov_report_info', return_value=PROJECT_COV_INFO): self.coverage_getter = coverage.OssFuzzCoverageGetter( PROJECT_NAME, REPO_PATH) def test_valid_target(self): """Tests that covered files can be retrieved from a coverage report.""" with open(os.path.join(TEST_FILES_PATH, FUZZ_TARGET_COV_JSON_FILENAME),) as file_handle: fuzzer_cov_info = json.loads(file_handle.read()) with mock.patch('coverage.OssFuzzCoverageGetter.get_target_coverage_report', return_value=fuzzer_cov_info): file_list = self.coverage_getter.get_files_covered_by_target(FUZZ_TARGET) curl_files_list_path = os.path.join(TEST_FILES_PATH, 'example_curl_file_list.json') with open(curl_files_list_path) as file_handle: expected_file_list = json.loads(file_handle.read()) self.assertCountEqual(file_list, expected_file_list) def test_invalid_target(self): """Tests passing invalid fuzz target returns None.""" self.assertIsNone( self.coverage_getter.get_files_covered_by_target(INVALID_TARGET)) class IsFileCoveredTest(unittest.TestCase): """Tests for is_file_covered.""" def test_is_file_covered_covered(self): """Tests that is_file_covered returns True for a covered file.""" file_coverage = { 'filename': '/src/systemd/src/basic/locale-util.c', 'summary': { 'regions': { 'count': 204, 'covered': 200, 'notcovered': 200, 'percent': 98.03 } } } self.assertTrue(coverage.is_file_covered(file_coverage)) def test_is_file_covered_not_covered(self): """Tests that is_file_covered returns False for a not covered file.""" file_coverage = { 'filename': '/src/systemd/src/basic/locale-util.c', 'summary': { 'regions': { 'count': 204, 'covered': 0, 'notcovered': 0, 'percent': 0 } } } self.assertFalse(coverage.is_file_covered(file_coverage)) class GetLatestCovReportInfo(unittest.TestCase): """Tests that _get_latest_cov_report_info works as intended.""" PROJECT = 'project' LATEST_REPORT_INFO_URL = ('https://storage.googleapis.com/oss-fuzz-coverage/' 'latest_report_info/project.json') @mock.patch('logging.error') @mock.patch('coverage.get_json_from_url', return_value={'coverage': 1}) def test_get_latest_cov_report_info(self, mocked_get_json_from_url, mocked_error): """Tests that _get_latest_cov_report_info works as intended.""" result = coverage._get_latest_cov_report_info(self.PROJECT) self.assertEqual(result, {'coverage': 1}) mocked_error.assert_not_called() mocked_get_json_from_url.assert_called_with(self.LATEST_REPORT_INFO_URL) @mock.patch('logging.error') @mock.patch('coverage.get_json_from_url', return_value=None) def test_get_latest_cov_report_info_fail(self, _, mocked_error): """Tests that _get_latest_cov_report_info works as intended when we can't get latest report info.""" result = coverage._get_latest_cov_report_info('project') self.assertIsNone(result) mocked_error.assert_called_with( 'Could not get the coverage report json from url: %s.', self.LATEST_REPORT_INFO_URL) if __name__ == '__main__': unittest.main()