Home | History | Annotate | Download | only in coverage
      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 
     18 import math
     19 import os
     20 import struct
     21 import unittest
     22 
     23 from vts.utils.python.coverage import parser
     24 
     25 MAGIC = 0x67636e6f
     26 
     27 
     28 class MockStream(object):
     29     """MockStream object allows for mocking file reading behavior.
     30 
     31     Allows for adding integers and strings to the file stream in a
     32     specified byte format and then reads them as if from a file.
     33 
     34     Attributes:
     35         content: the byte list representing a file stream
     36         cursor: the index into the content such that everything before it
     37                 has been read already.
     38     """
     39     BYTES_PER_WORD = 4
     40 
     41     def __init__(self, magic=MAGIC, format='<'):
     42         self.format = format
     43         self.magic = magic
     44         version = struct.unpack(format + 'I', '*802')[0]
     45         self.content = struct.pack(format + 'III', magic, version, 0)
     46         self.cursor = 0
     47 
     48     @classmethod
     49     def concat_int(cls, stream, integer):
     50         """Returns the stream with a binary formatted integer concatenated.
     51 
     52         Args:
     53             stream: the stream to which the integer will be concatenated.
     54             integer: the integer to be concatenated to the content stream.
     55             format: the string format decorator to apply to the integer.
     56 
     57         Returns:
     58             The content with the binary-formatted integer concatenated.
     59         """
     60         new_content = stream.content + struct.pack(stream.format + 'I',
     61                                                    integer)
     62         s = MockStream(stream.magic, stream.format)
     63         s.content = new_content
     64         s.cursor = stream.cursor
     65         return s
     66 
     67     @classmethod
     68     def concat_int64(cls, stream, integer):
     69         """Returns the stream with a binary formatted int64 concatenated.
     70 
     71         Args:
     72             stream: the stream to which the integer will be concatenated.
     73             integer: the 8-byte int to be concatenated to the content stream.
     74             format: the string format decorator to apply to the long.
     75 
     76         Returns:
     77             The content with the binary-formatted int64 concatenated.
     78         """
     79         lo = ((1 << 32) - 1) & integer
     80         hi = (integer - lo) >> 32
     81         new_content = stream.content + struct.pack(stream.format + 'II', lo,
     82                                                    hi)
     83         s = MockStream(stream.magic, stream.format)
     84         s.content = new_content
     85         s.cursor = stream.cursor
     86         return s
     87 
     88     @classmethod
     89     def concat_string(cls, stream, string):
     90         """Returns the stream with a binary formatted string concatenated.
     91 
     92         Preceeds the string with an integer representing the number of
     93         words in the string. Pads the string so that it is word-aligned.
     94 
     95         Args:
     96             stream: the stream to which the string will be concatenated.
     97             string: the string to be concatenated to the content stream.
     98             format: the string format decorator to apply to the integer.
     99 
    100         Returns:
    101             The content with the formatted binary string concatenated.
    102         """
    103         byte_count = len(string)
    104         word_count = int(
    105             math.ceil(byte_count * 1.0 / MockStream.BYTES_PER_WORD))
    106         padding = '\x00' * (
    107             MockStream.BYTES_PER_WORD * word_count - byte_count)
    108         new_content = stream.content + struct.pack(
    109             stream.format + 'I', word_count) + bytes(string + padding)
    110         s = MockStream(stream.magic, stream.format)
    111         s.content = new_content
    112         s.cursor = stream.cursor
    113         return s
    114 
    115     def read(self, n_bytes):
    116         """Reads the specified number of bytes from the content stream.
    117 
    118         Args:
    119             n_bytes: integer number of bytes to read.
    120 
    121         Returns:
    122             The string of length n_bytes beginning at the cursor location
    123             in the content stream.
    124         """
    125         content = self.content[self.cursor:self.cursor + n_bytes]
    126         self.cursor += n_bytes
    127         return content
    128 
    129 
    130 class ParserTest(unittest.TestCase):
    131     """Tests for stream parser of vts.utils.python.coverage.
    132 
    133     Ensures error handling, byte order detection, and correct
    134     parsing of integers and strings.
    135     """
    136 
    137     def setUp(self):
    138         """Creates a stream for each test.
    139       """
    140         self.stream = MockStream()
    141 
    142     def testLittleEndiannessInitialization(self):
    143         """Tests parser init  with little-endian byte order.
    144 
    145         Verifies that the byte-order is correctly detected.
    146         """
    147         p = parser.GcovStreamParserUtil(self.stream, MAGIC)
    148         self.assertEqual(p.format, '<')
    149 
    150     def testBigEndiannessInitialization(self):
    151         """Tests parser init with big-endian byte order.
    152 
    153         Verifies that the byte-order is correctly detected.
    154         """
    155         self.stream = MockStream(format='>')
    156         p = parser.GcovStreamParserUtil(self.stream, MAGIC)
    157         self.assertEqual(p.format, '>')
    158 
    159     def testReadIntNormal(self):
    160         """Asserts that integers are correctly read from the stream.
    161 
    162         Tests the normal case--when the value is actually an integer.
    163         """
    164         integer = 2016
    165         self.stream = MockStream.concat_int(self.stream, integer)
    166         p = parser.GcovStreamParserUtil(self.stream, MAGIC)
    167         self.assertEqual(p.ReadInt(), integer)
    168 
    169     def testReadIntEof(self):
    170         """Asserts that an error is thrown when the EOF is reached.
    171         """
    172         p = parser.GcovStreamParserUtil(self.stream, MAGIC)
    173         self.assertRaises(parser.FileFormatError, p.ReadInt)
    174 
    175     def testReadInt64(self):
    176         """Asserts that longs are read correctly.
    177         """
    178         number = 68719476836
    179         self.stream = MockStream.concat_int64(self.stream, number)
    180         p = parser.GcovStreamParserUtil(self.stream, MAGIC)
    181         self.assertEqual(number, p.ReadInt64())
    182 
    183         self.stream = MockStream(format='>')
    184         self.stream = MockStream.concat_int64(self.stream, number)
    185         p = parser.GcovStreamParserUtil(self.stream, MAGIC)
    186         self.assertEqual(number, p.ReadInt64())
    187 
    188     def testReadStringNormal(self):
    189         """Asserts that strings are correctly read from the stream.
    190 
    191         Tests the normal case--when the string is correctly formatted.
    192         """
    193         test_string = "This is a test."
    194         self.stream = MockStream.concat_string(self.stream, test_string)
    195         p = parser.GcovStreamParserUtil(self.stream, MAGIC)
    196         self.assertEqual(p.ReadString(), test_string)
    197 
    198     def testReadStringError(self):
    199         """Asserts that invalid string format raises error.
    200 
    201         Tests when the string length is too short and EOF is reached.
    202         """
    203         test_string = "This is a test."
    204         byte_count = len(test_string)
    205         word_count = int(round(byte_count / 4.0))
    206         padding = '\x00' * (4 * word_count - byte_count)
    207         test_string_padded = test_string + padding
    208         content = struct.pack('<I', word_count + 1)  #  will cause EOF error
    209         content += bytes(test_string_padded)
    210         self.stream.content += content
    211         p = parser.GcovStreamParserUtil(self.stream, MAGIC)
    212         self.assertRaises(parser.FileFormatError, p.ReadString)
    213 
    214 
    215 if __name__ == "__main__":
    216     unittest.main()
    217