blob: 0916f9a8a1ec4dd3b818dabf709c3a18c1d7cc98 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import math
import os
import struct
import unittest
from vts.utils.python.coverage import parser
MAGIC = 0x67636e6f
class MockStream(object):
"""MockStream object allows for mocking file reading behavior.
Allows for adding integers and strings to the file stream in a
specified byte format and then reads them as if from a file.
Attributes:
content: the byte list representing a file stream
cursor: the index into the content such that everything before it
has been read already.
"""
BYTES_PER_WORD = 4
def __init__(self, magic=MAGIC, format='<'):
self.format = format
self.magic = magic
version = struct.unpack(format + 'I', '*802')[0]
self.content = struct.pack(format + 'III', magic, version, 0)
self.cursor = 0
@classmethod
def concat_int(cls, stream, integer):
"""Returns the stream with a binary formatted integer concatenated.
Args:
stream: the stream to which the integer will be concatenated.
integer: the integer to be concatenated to the content stream.
format: the string format decorator to apply to the integer.
Returns:
The content with the binary-formatted integer concatenated.
"""
new_content = stream.content + struct.pack(stream.format + 'I',
integer)
s = MockStream(stream.magic, stream.format)
s.content = new_content
s.cursor = stream.cursor
return s
@classmethod
def concat_int64(cls, stream, integer):
"""Returns the stream with a binary formatted int64 concatenated.
Args:
stream: the stream to which the integer will be concatenated.
integer: the 8-byte int to be concatenated to the content stream.
format: the string format decorator to apply to the long.
Returns:
The content with the binary-formatted int64 concatenated.
"""
lo = ((1 << 32) - 1) & integer
hi = (integer - lo) >> 32
new_content = stream.content + struct.pack(stream.format + 'II', lo,
hi)
s = MockStream(stream.magic, stream.format)
s.content = new_content
s.cursor = stream.cursor
return s
@classmethod
def concat_string(cls, stream, string):
"""Returns the stream with a binary formatted string concatenated.
Preceeds the string with an integer representing the number of
words in the string. Pads the string so that it is word-aligned.
Args:
stream: the stream to which the string will be concatenated.
string: the string to be concatenated to the content stream.
format: the string format decorator to apply to the integer.
Returns:
The content with the formatted binary string concatenated.
"""
byte_count = len(string)
word_count = int(
math.ceil(byte_count * 1.0 / MockStream.BYTES_PER_WORD))
padding = '\x00' * (
MockStream.BYTES_PER_WORD * word_count - byte_count)
new_content = stream.content + struct.pack(
stream.format + 'I', word_count) + bytes(string + padding)
s = MockStream(stream.magic, stream.format)
s.content = new_content
s.cursor = stream.cursor
return s
def read(self, n_bytes):
"""Reads the specified number of bytes from the content stream.
Args:
n_bytes: integer number of bytes to read.
Returns:
The string of length n_bytes beginning at the cursor location
in the content stream.
"""
content = self.content[self.cursor:self.cursor + n_bytes]
self.cursor += n_bytes
return content
class ParserTest(unittest.TestCase):
"""Tests for stream parser of vts.utils.python.coverage.
Ensures error handling, byte order detection, and correct
parsing of integers and strings.
"""
def setUp(self):
"""Creates a stream for each test.
"""
self.stream = MockStream()
def testLittleEndiannessInitialization(self):
"""Tests parser init with little-endian byte order.
Verifies that the byte-order is correctly detected.
"""
p = parser.GcovStreamParserUtil(self.stream, MAGIC)
self.assertEqual(p.format, '<')
def testBigEndiannessInitialization(self):
"""Tests parser init with big-endian byte order.
Verifies that the byte-order is correctly detected.
"""
self.stream = MockStream(format='>')
p = parser.GcovStreamParserUtil(self.stream, MAGIC)
self.assertEqual(p.format, '>')
def testReadIntNormal(self):
"""Asserts that integers are correctly read from the stream.
Tests the normal case--when the value is actually an integer.
"""
integer = 2016
self.stream = MockStream.concat_int(self.stream, integer)
p = parser.GcovStreamParserUtil(self.stream, MAGIC)
self.assertEqual(p.ReadInt(), integer)
def testReadIntEof(self):
"""Asserts that an error is thrown when the EOF is reached.
"""
p = parser.GcovStreamParserUtil(self.stream, MAGIC)
self.assertRaises(parser.FileFormatError, p.ReadInt)
def testReadInt64(self):
"""Asserts that longs are read correctly.
"""
number = 68719476836
self.stream = MockStream.concat_int64(self.stream, number)
p = parser.GcovStreamParserUtil(self.stream, MAGIC)
self.assertEqual(number, p.ReadInt64())
self.stream = MockStream(format='>')
self.stream = MockStream.concat_int64(self.stream, number)
p = parser.GcovStreamParserUtil(self.stream, MAGIC)
self.assertEqual(number, p.ReadInt64())
def testReadStringNormal(self):
"""Asserts that strings are correctly read from the stream.
Tests the normal case--when the string is correctly formatted.
"""
test_string = "This is a test."
self.stream = MockStream.concat_string(self.stream, test_string)
p = parser.GcovStreamParserUtil(self.stream, MAGIC)
self.assertEqual(p.ReadString(), test_string)
def testReadStringError(self):
"""Asserts that invalid string format raises error.
Tests when the string length is too short and EOF is reached.
"""
test_string = "This is a test."
byte_count = len(test_string)
word_count = int(round(byte_count / 4.0))
padding = '\x00' * (4 * word_count - byte_count)
test_string_padded = test_string + padding
content = struct.pack('<I', word_count + 1) # will cause EOF error
content += bytes(test_string_padded)
self.stream.content += content
p = parser.GcovStreamParserUtil(self.stream, MAGIC)
self.assertRaises(parser.FileFormatError, p.ReadString)
if __name__ == "__main__":
unittest.main()