winIDEA SDK
cumulative_coverage.py
1# This script is licensed under BSD License, see file LICENSE.txt.
2#
3# (c) TASKING Germany GmbH, 2023
4
5import isystem.connect as ic
6import isystem.itest as it
7
8winidea_id = ''
9
10
11try:
12 import pylab as pl
13 is_pylab_available = True
14except Exception as ex:
15 is_pylab_available = False
16
17
18class Monitor:
19 def __init__(self, no_of_sub_tasks):
20 self.no_of_sub_tasks = no_of_sub_tasks
21 self.test_count = 0
22
23 def isCanceled(self):
24 pass
25
26 def subTask(self, task_desc):
27 print(task_desc)
28
29 def worked(self, count):
30 self.test_count += count
31 print('#' * self.test_count + '-' * (self.no_of_sub_tasks - self.test_count))
32
33
34def init_target() -> ic.ConnectionMgr:
35 cmgr = ic.ConnectionMgr()
36
37 cmgr.connect(ic.CConnectionConfig().instanceId(winidea_id))
38
39 debug_ctrl = ic.CDebugFacade(cmgr)
40 session_ctrl = ic.CSessionCtrl(cmgr)
41
42
43 session_ctrl.begin_reset()
44
45 debug_ctrl.runUntilFunction('main')
46 debug_ctrl.waitUntilStopped()
47
48 return cmgr
49
50
51def run_test(connection_mgr: ic.ConnectionMgr) -> ic.CTestReportContainer:
52 test_case = it.PTestCase(connection_mgr)
53
54 test_case.reset() # just in case there's something left from previous run
55
56 # Test specification contains everything needed for code coverage
57 # measurements. It would also be possible to remove the first test
58 # specification, which configures coverage and replace it with
59 # isystem.connect calls. See 'cumulativeModuleCoverage.py' for example.
60 test_spec = ic.CTestSpecification.parseTestSpec(
61 """
62 # the first test specification configures coverage
63 func: [fibonacci, [0], rv]
64 coverage:
65 runMode: start # coverage should be configured
66 document: fibonacci.trd
67 openMode: w # write mode erases any previous coverage data
68 isDecisionCoverage: true
69 statistics:
70 - func: fibonacci
71 tests:
72 # abstract base specification follows, which defines coverage for all
73 # derived tests.
74 - func: [fibonacci, [0], rv]
75 run: no # this is an abstract base test
76 coverage:
77 runMode: resume # coverage should be accumulated
78 document: fibonacci.trd
79 openMode: u # 'update' mode keeps previous coverage data
80 statistics:
81 - func: fibonacci
82 tests: # derived tests execute the function for several parameters to get full coverage
83 - params: [0]
84 expect: [rv == 0]
85
86 - params: [1]
87 expect: [rv == 1]
88
89 - params: [2]
90 expect: [rv == 1]
91
92 - params: [3]
93 expect: [rv == 2]
94
95 - params: [7]
96 expect: [rv == 13 @@ d]
97 coverage: # the last test should check for coverage values
98 runMode: resume
99 document: fibonacci.trd
100 openMode: u
101 isDecisionCoverage: true
102 statistics:
103 - func: fibonacci
104 code: 100
105 branches: 100
106 taken: 100
107 notTaken: 100
108 both: 100
109 """)
110
111 monitor = Monitor(test_spec.getNoOfTests(True))
112
113 print('Running tests to achieve full coverage...')
114 test_case.runTests(test_spec, None, monitor)
115
116 test_results = test_case.getTestResultsContainer()
117 return test_results
118
119
120def save_test_Results(test_results: ic.CTestReportContainer, resultFileName: str):
121 error_file_stream = ic.CFileStream(resultFileName)
122 err_emitter = ic.EmitterFactory.createCSVEmitter(error_file_stream, ",", True)
123 test_results.resetTestResultIterator()
124
125 while test_results.hasNextTestResult():
126 result = test_results.nextTestResult()
127 # serialize result to file
128 err_emitter.startDocument(False)
129 result.serialize(err_emitter, None)
130 err_emitter.endDocument(False)
131
132 # serialize result to string for printing to stdout
133 err_stream = ic.CStringStream()
134 emitter = ic.EmitterFactory.createYamlEmitter(err_stream)
135
136 emitter.startStream()
137 emitter.startDocument(False)
138 result.serializeErrorsOnly(emitter, None)
139 emitter.endDocument(False)
140 emitter.endStream()
141
142 print(err_stream.getString())
143
144 err_emitter.endStream()
145 error_file_stream.close()
146
147
148def plot_coverage(test_results: ic.CTestReportContainer, is_plot_all=False):
149 """
150 Draws a plot showing the amount of coverage in each test.
151 This function requires 'matplotlib' and 'numpy' Python packages to be
152 installed.
153 """
154 bytes_executed = []
155 lines_executed = []
156 branches_executed = []
157 branches_taken = []
158 branches_not_taken = []
159 branches_both = []
160
161 test_results.resetTestResultIterator()
162 while test_results.hasNextTestResult():
163 result = test_results.nextTestResult()
164 cvrg_result = ic.StrCoverageTestResultsMap()
165 result.getCoverageResults(cvrg_result)
166 for function in cvrg_result:
167 stats = cvrg_result[function].getMeasuredCoverage(True)
168
169 bytes_executed.append(stats.getBytesExecuted())
170 lines_executed.append(stats.getSourceLinesExecuted())
171 branches_executed.append(stats.getBranchExecuted())
172 branches_taken.append(stats.getBranchTaken())
173 branches_not_taken.append(stats.getBranchNotTaken())
174 branches_both.append(stats.getBranchBoth())
175
176 x = range(len(bytes_executed))
177 pl.plot(x, bytes_executed, '.-', label='bytes executed')
178 pl.plot(x, branches_both, '.-', label='branches both')
179 if is_plot_all:
180 pl.plot(x, lines_executed, 'o', label='lines executed')
181 pl.plot(x, branches_taken, '--', label='branches taken')
182 pl.plot(x, branches_not_taken, '--', label='branches not taken')
183 pl.plot(x, branches_executed, '--', label='branches executed')
184
185 pl.axis([0, 5, 0, 110])
186
187 pl.xlabel('test number')
188 pl.ylabel('%')
189 pl.title("Coverage of function under test")
190 pl.legend()
191 pl.grid(True)
192 pl.show()
193
194
195def main():
196 cmgr = init_target()
197 test_results = run_test(cmgr)
198
199 result_file_name = 'errorsReport.csv'
200 print('Tests finished, saving results to file: ', result_file_name)
201 save_test_Results(test_results, result_file_name)
202
203 if is_pylab_available:
204 print('Showing results in graph (matplotlib and numpy packages must be installed!).')
205 plot_coverage(test_results)
206 else:
207 print("Can't plot coverage results, pylab module is not available.")
208
209
210if __name__ == '__main__':
211 main()