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