# Copyright 2020 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. """Contains convenient helpers for writing tests.""" import contextlib import os import sys import shutil import tempfile from unittest import mock import config_utils import docker import workspace_utils INFRA_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # pylint: disable=wrong-import-position,import-error sys.path.append(INFRA_DIR) import helper @mock.patch('config_utils._is_dry_run', return_value=True) @mock.patch('config_utils.GenericCiEnvironment.project_src_path', return_value=None) @mock.patch('os.path.basename', return_value=None) def _create_config(config_cls, _, __, ___, **kwargs): """Creates a config object from |config_cls| and then sets every attribute that is a key in |kwargs| to the corresponding value. Asserts that each key in |kwargs| is an attribute of config.""" with mock.patch('config_utils.BaseConfig.validate', return_value=True): config = config_cls() for key, value in kwargs.items(): assert hasattr(config, key), 'Config doesn\'t have attribute: ' + key setattr(config, key, value) return config def create_build_config(**kwargs): """Wrapper around _create_config for build configs.""" return _create_config(config_utils.BuildFuzzersConfig, **kwargs) def create_run_config(**kwargs): """Wrapper around _create_config for run configs.""" return _create_config(config_utils.RunFuzzersConfig, **kwargs) def create_workspace(workspace_path='/workspace'): """Returns a workspace located at |workspace_path| ('/workspace' by default).""" config = create_run_config(workspace=workspace_path) return workspace_utils.Workspace(config) def patch_environ(testcase_obj, env=None, empty=False, runner=False): """Patch environment. |testcase_obj| is the unittest.TestCase that contains tests. |env|, if specified, is a dictionary of environment variables to start from. If |empty| is True then the new patched environment will be empty. If |runner| is True then the necessary environment variables will be set to run the scripts from base-runner.""" if env is None: env = {} patcher = mock.patch.dict(os.environ, env) testcase_obj.addCleanup(patcher.stop) patcher.start() if empty: for key in os.environ.copy(): del os.environ[key] if runner: # Add the scripts for base-runner to the path since the wont be in # /usr/local/bin on host machines during testing. base_runner_dir = os.path.join(INFRA_DIR, 'base-images', 'base-runner') os.environ['PATH'] = (os.environ.get('PATH', '') + os.pathsep + base_runner_dir) if 'GOPATH' not in os.environ: # A GOPATH must be set or else the coverage script fails, even for getting # the coverage of non-Go programs. os.environ['GOPATH'] = '/root/go' @contextlib.contextmanager def temp_dir_copy(directory): """Context manager that yields a temporary copy of |directory|.""" with tempfile.TemporaryDirectory() as temp_dir: temp_copy_path = os.path.join(temp_dir, os.path.basename(directory)) shutil.copytree(directory, temp_copy_path) yield temp_copy_path @contextlib.contextmanager def docker_temp_dir(): """Returns a temporary a directory that is useful for use with docker. On cleanup this contextmanager uses docker to delete the directory's contents so that if anything is owned by root it can be deleted (which tempfile.TemporaryDirectory() cannot do) by non-root users.""" with tempfile.TemporaryDirectory() as temp_dir: yield temp_dir helper.docker_run([ '-v', f'{temp_dir}:/temp_dir', '-t', docker.BASE_BUILDER_TAG, '/bin/bash', '-c', 'rm -rf /temp_dir/*' ])