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 parser 24 25MAGIC = 0x67636e6f 26 27 28class 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 130class 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 215if __name__ == "__main__": 216 unittest.main() 217