1#!/usr/bin/env python
2#
3# Copyright (C) 2016 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import math
19import os
20import struct
21import unittest
22
23from vts.utils.python.coverage import arc_summary
24from vts.utils.python.coverage import block_summary
25from vts.utils.python.coverage import function_summary
26from vts.utils.python.coverage import gcno_parser
27from vts.utils.python.coverage.parser_test import MockStream
28
29
30class GCNOParserTest(unittest.TestCase):
31    """Tests for GCNO parser of vts.utils.python.coverage.
32
33    Ensures error handling, byte order detection, and correct
34    parsing of functions, blocks, arcs, and lines.
35    """
36
37    def setUp(self):
38        """Creates a stream for each test.
39        """
40        self.stream = MockStream()
41
42    def testReadFunction(self):
43        """Asserts that the function is read correctly.
44
45        Verifies that ident, name, source file name,
46        and first line number are all read correctly.
47        """
48        ident = 102010
49        self.stream = MockStream.concat_int(self.stream, ident)
50        self.stream = MockStream.concat_int(self.stream, 0)
51        self.stream = MockStream.concat_int(self.stream, 0)
52        name = "TestFunction"
53        src_file_name = "TestSouceFile.c"
54        first_line_number = 102
55        self.stream = MockStream.concat_string(self.stream, name)
56        self.stream = MockStream.concat_string(self.stream, src_file_name)
57        self.stream = MockStream.concat_int(self.stream, first_line_number)
58        parser = gcno_parser.GCNOParser(self.stream)
59        summary = parser.ReadFunction()
60        self.assertEqual(name, summary.name)
61        self.assertEqual(ident, summary.ident)
62        self.assertEqual(src_file_name, summary.src_file_name)
63        self.assertEqual(first_line_number, summary.first_line_number)
64
65    def testReadBlocks(self):
66        """Asserts that blocks are correctly read from the stream.
67
68        Tests correct values for flag and index.
69        """
70        n_blocks = 10
71        func = function_summary.FunctionSummary(0, "func", "src.c", 1)
72        for i in range(n_blocks):
73            self.stream = MockStream.concat_int(self.stream, 3 * i)
74        parser = gcno_parser.GCNOParser(self.stream)
75        parser.ReadBlocks(n_blocks, func)
76        self.assertEqual(len(func.blocks), n_blocks)
77        for i in range(n_blocks):
78            self.assertEqual(func.blocks[i].flag, 3 * i)
79            self.assertEqual(func.blocks[i].index, i)
80
81    def testReadArcsNormal(self):
82        """Asserts that arcs are correctly read from the stream.
83
84        Does not test the use of flags. Validates that arcs are
85        created in both blocks and the source/destination are
86        correct for each.
87        """
88        n_blocks = 50
89        func = function_summary.FunctionSummary(0, "func", "src.c", 1)
90        func.blocks = [block_summary.BlockSummary(i, 3 * i)
91                       for i in range(n_blocks)]
92        src_block_index = 0
93        skip = 2
94        self.stream = MockStream.concat_int(self.stream, src_block_index)
95        for i in range(src_block_index + 1, n_blocks, skip):
96            self.stream = MockStream.concat_int(self.stream, i)
97            self.stream = MockStream.concat_int(
98                self.stream, 0)  #  no flag applied to the arc
99        parser = gcno_parser.GCNOParser(self.stream)
100        n_arcs = len(range(src_block_index + 1, n_blocks, skip))
101        parser.ReadArcs(n_arcs * 2 + 1, func)
102        j = 0
103        for i in range(src_block_index + 1, n_blocks, skip):
104            self.assertEqual(
105                func.blocks[src_block_index].exit_arcs[j].src_block.index,
106                src_block_index)
107            self.assertEqual(
108                func.blocks[src_block_index].exit_arcs[j].dst_block.index, i)
109            self.assertEqual(func.blocks[i].entry_arcs[0].src_block.index,
110                             src_block_index)
111            self.assertEqual(func.blocks[i].entry_arcs[0].dst_block.index, i)
112            j += 1
113
114    def testReadArcFlags(self):
115        """Asserts that arc flags are correctly interpreted.
116        """
117        n_blocks = 5
118        func = function_summary.FunctionSummary(0, "func", "src.c", 1)
119        func.blocks = [block_summary.BlockSummary(i, 3 * i)
120                       for i in range(n_blocks)]
121        self.stream = MockStream.concat_int(self.stream,
122                                            0)  #  source block index
123
124        self.stream = MockStream.concat_int(self.stream, 1)  #  normal arc
125        self.stream = MockStream.concat_int(self.stream, 0)
126
127        self.stream = MockStream.concat_int(self.stream, 2)  #  on-tree arc
128        self.stream = MockStream.concat_int(
129            self.stream, arc_summary.ArcSummary.GCOV_ARC_ON_TREE)
130
131        self.stream = MockStream.concat_int(self.stream, 3)  #  fake arc
132        self.stream = MockStream.concat_int(
133            self.stream, arc_summary.ArcSummary.GCOV_ARC_FAKE)
134
135        self.stream = MockStream.concat_int(self.stream, 4)  #  fallthrough arc
136        self.stream = MockStream.concat_int(
137            self.stream, arc_summary.ArcSummary.GCOV_ARC_FALLTHROUGH)
138
139        parser = gcno_parser.GCNOParser(self.stream)
140        parser.ReadArcs(4 * 2 + 1, func)
141
142        self.assertFalse(func.blocks[0].exit_arcs[0].on_tree)
143        self.assertFalse(func.blocks[0].exit_arcs[0].fake)
144        self.assertFalse(func.blocks[0].exit_arcs[0].fallthrough)
145
146        self.assertTrue(func.blocks[0].exit_arcs[1].on_tree)
147        self.assertFalse(func.blocks[0].exit_arcs[1].fake)
148        self.assertFalse(func.blocks[0].exit_arcs[1].fallthrough)
149
150        self.assertFalse(func.blocks[0].exit_arcs[2].on_tree)
151        self.assertTrue(func.blocks[0].exit_arcs[2].fake)
152        self.assertFalse(func.blocks[0].exit_arcs[2].fallthrough)
153
154        self.assertFalse(func.blocks[0].exit_arcs[3].on_tree)
155        self.assertFalse(func.blocks[0].exit_arcs[3].fake)
156        self.assertTrue(func.blocks[0].exit_arcs[3].fallthrough)
157
158    def testReadLines(self):
159        """Asserts that lines are read correctly.
160
161        Blocks must have correct references to the lines contained
162        in the block.
163        """
164        self.stream = MockStream.concat_int(self.stream, 2)  #  block number
165        self.stream = MockStream.concat_int(self.stream, 0)  #  dummy
166        name = "src.c"
167        name_length = int(
168            math.ceil(1.0 * len(name) / MockStream.BYTES_PER_WORD)) + 1
169        self.stream = MockStream.concat_string(self.stream, name)
170        n_arcs = 5
171        for i in range(1, n_arcs + 1):
172            self.stream = MockStream.concat_int(self.stream, i)
173
174        n_blocks = 5
175        func = function_summary.FunctionSummary(0, "func", name, 1)
176        func.blocks = [block_summary.BlockSummary(i, 3 * i)
177                       for i in range(n_blocks)]
178        parser = gcno_parser.GCNOParser(self.stream)
179        parser.ReadLines(n_arcs + name_length + 3, func)
180        self.assertEqual(len(func.blocks[2].lines), 5)
181        self.assertEqual(func.blocks[2].lines, range(1, 6))
182
183    def testSampleFile(self):
184        """Asserts correct parsing of sample GCNO file.
185
186        Verifies the blocks and lines for each function in
187        the file.
188        """
189        path = os.path.join(
190            os.getenv('ANDROID_BUILD_TOP'),
191            'test/vts/utils/python/coverage/testdata/sample.gcno')
192        summary = gcno_parser.ParseGcnoFile(path)
193        self.assertEqual(len(summary.functions), 2)
194
195        # Check function: testFunctionName
196        func = summary.functions[4]
197        self.assertEqual(func.name, 'testFunctionName')
198        self.assertEqual(func.src_file_name, 'sample.c')
199        self.assertEqual(func.first_line_number, 35)
200        self.assertEqual(len(func.blocks), 5)
201        expected_list = [[], [], [35, 40, 41], [42], []]
202        for index, expected in zip(range(5), expected_list):
203            self.assertEqual(func.blocks[index].lines, expected)
204
205        # Check function: main
206        func = summary.functions[3]
207        self.assertEqual(func.name, 'main')
208        self.assertEqual(func.first_line_number, 5)
209        self.assertEqual(len(func.blocks), 12)
210        self.assertEqual(func.blocks[0].lines, [])
211        expected_list = [[], [], [5, 11, 12, 13], [15], [17], [18], [20],
212                         [23, 24, 25], [26, 25], [], [29], [31]]
213        for index, expected in zip(range(12), expected_list):
214            self.assertEqual(func.blocks[index].lines, expected)
215
216
217if __name__ == "__main__":
218    unittest.main()
219