1"""Utility code for facilitating collection of code coverage when running tests.""" 2from __future__ import (absolute_import, division, print_function) 3__metaclass__ = type 4 5import contextlib 6import os 7import tempfile 8 9from .config import ( 10 IntegrationConfig, 11 SanityConfig, 12 TestConfig, 13) 14 15from .util import ( 16 COVERAGE_CONFIG_NAME, 17 remove_tree, 18) 19 20from .util_common import ( 21 write_text_file, 22) 23 24from .data import ( 25 data_context, 26) 27 28 29@contextlib.contextmanager 30def coverage_context(args): # type: (TestConfig) -> None 31 """Content to set up and clean up code coverage configuration for tests.""" 32 coverage_setup(args) 33 34 try: 35 yield 36 finally: 37 coverage_cleanup(args) 38 39 40def coverage_setup(args): # type: (TestConfig) -> None 41 """Set up code coverage configuration before running tests.""" 42 if not args.coverage: 43 return 44 45 coverage_config = generate_coverage_config(args) 46 47 if args.explain: 48 args.coverage_config_base_path = '/tmp/coverage-temp-dir' 49 else: 50 args.coverage_config_base_path = tempfile.mkdtemp() 51 52 write_text_file(os.path.join(args.coverage_config_base_path, COVERAGE_CONFIG_NAME), coverage_config) 53 54 55def coverage_cleanup(args): # type: (TestConfig) -> None 56 """Clean up code coverage configuration after tests have finished.""" 57 if args.coverage_config_base_path and not args.explain: 58 remove_tree(args.coverage_config_base_path) 59 args.coverage_config_base_path = None 60 61 62def generate_coverage_config(args): # type: (TestConfig) -> str 63 """Generate code coverage configuration for tests.""" 64 if data_context().content.collection: 65 coverage_config = generate_collection_coverage_config(args) 66 else: 67 coverage_config = generate_ansible_coverage_config() 68 69 return coverage_config 70 71 72def generate_ansible_coverage_config(): # type: () -> str 73 """Generate code coverage configuration for Ansible tests.""" 74 coverage_config = ''' 75[run] 76branch = True 77concurrency = multiprocessing 78parallel = True 79 80omit = 81 */python*/dist-packages/* 82 */python*/site-packages/* 83 */python*/distutils/* 84 */pyshared/* 85 */pytest 86 */AnsiballZ_*.py 87 */test/results/* 88''' 89 90 return coverage_config 91 92 93def generate_collection_coverage_config(args): # type: (TestConfig) -> str 94 """Generate code coverage configuration for Ansible Collection tests.""" 95 coverage_config = ''' 96[run] 97branch = True 98concurrency = multiprocessing 99parallel = True 100disable_warnings = 101 no-data-collected 102''' 103 104 if isinstance(args, IntegrationConfig): 105 coverage_config += ''' 106include = 107 %s/* 108 */%s/* 109''' % (data_context().content.root, data_context().content.collection.directory) 110 elif isinstance(args, SanityConfig): 111 # temporary work-around for import sanity test 112 coverage_config += ''' 113include = 114 %s/* 115 116omit = 117 %s/* 118''' % (data_context().content.root, os.path.join(data_context().content.root, data_context().content.results_path)) 119 else: 120 coverage_config += ''' 121include = 122 %s/* 123''' % data_context().content.root 124 125 return coverage_config 126