#!/usr/bin/env python3
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import ast
import itertools
import os
import unittest
import bcanalyzer
import parallel
import test_util
def _MakeBytes(bits, toks):
"""Creates a multi-byte string from ASCII strings and/or ASCII codes.
Args:
bits: Number of bits per character, must be in {8, 16, 32}.
toks: A list of tokens, each of which is a ASCII strings or an integer
representing a character's ASCII value (e.g., 0 for terminating null).
Returns: A flattened string of the |bits|-bit string formed by |bit|-extending
the result of concatanating tokens.
"""
cfi = itertools.chain.from_iterable
chars = cfi(map(ord, t) if isinstance(t, str) else (t, ) for t in toks)
return bytes(cfi((c >> (8 * i) for i in range(bits // 8)) for c in chars))
class BcAnalyzerTest(unittest.TestCase):
def testParseTag(self):
# Valid cases.
self.assertEqual((bcanalyzer.OPENING_TAG, 'FOO', 4),
bcanalyzer.ParseTag('<FOO> trailing'))
self.assertEqual((bcanalyzer.OPENING_TAG, 'BAR', 4),
bcanalyzer.ParseTag('<BAR op0=3 op1=4>'))
self.assertEqual((bcanalyzer.CLOSING_TAG, 'FOO', 5),
bcanalyzer.ParseTag('</FOO>'))
self.assertEqual((bcanalyzer.SELF_CLOSING_TAG, 'FOO', 4),
bcanalyzer.ParseTag('<FOO/>'))
self.assertEqual((bcanalyzer.SELF_CLOSING_TAG, 'TOMATO2', 8),
bcanalyzer.ParseTag('<TOMATO2 />'))
# Not self-closing: For simplicity we requires '/>' with space.
self.assertEqual((bcanalyzer.OPENING_TAG, 'TOMATO2', 8),
bcanalyzer.ParseTag('<TOMATO2 / >'))
self.assertEqual((bcanalyzer.SELF_CLOSING_TAG, 'BAR', 4),
bcanalyzer.ParseTag('<BAR op0=3 op1=4/>'))
self.assertEqual((bcanalyzer.OPENING_TAG, 'FOO', 4),
bcanalyzer.ParseTag('<FOO> / trailing'))
self.assertEqual((bcanalyzer.SELF_CLOSING_TAG, 'STRUCT_NAME', 12),
bcanalyzer.ParseTag('<STRUCT_NAME abbrevid=7 op0=0/>'))
self.assertEqual((bcanalyzer.SELF_CLOSING_TAG, 'UnkownCode41', 13),
bcanalyzer.ParseTag('<UnkownCode41 op0=0 op1=4 op2=5/>'))
self.assertEqual((bcanalyzer.CLOSING_TAG, 'FOO_BAR', 9),
bcanalyzer.ParseTag('</FOO_BAR> \'/>trailing\''))
self.assertEqual((bcanalyzer.OPENING_TAG, 'A', 2),
bcanalyzer.ParseTag('<A>'))
self.assertEqual((bcanalyzer.OPENING_TAG, 'lower', 6),
bcanalyzer.ParseTag('<lower >'))
# An invalid tag (all numbers), but we allow for simplicity.
self.assertEqual((bcanalyzer.OPENING_TAG, '123', 4),
bcanalyzer.ParseTag('<123>'))
# Abominations that are allowed for simplicity.
self.assertEqual((bcanalyzer.SELF_CLOSING_TAG, 'FOO', 5),
bcanalyzer.ParseTag('</FOO/>'))
self.assertEqual((bcanalyzer.SELF_CLOSING_TAG, 'FOO', 4),
bcanalyzer.ParseTag('<FOO///////>'))
self.assertEqual((bcanalyzer.OPENING_TAG, 'FOO', 4),
bcanalyzer.ParseTag('<FOO>>>>>'))
# Invalid cases.
None3 = (None, None, None)
self.assertEqual(None3, bcanalyzer.ParseTag(''))
self.assertEqual(None3, bcanalyzer.ParseTag(' '))
self.assertEqual(None3, bcanalyzer.ParseTag('<>'))
self.assertEqual(None3, bcanalyzer.ParseTag('<> '))
self.assertEqual(None3, bcanalyzer.ParseTag('< >'))
self.assertEqual(None3, bcanalyzer.ParseTag('<<FOO>'))
self.assertEqual(None3, bcanalyzer.ParseTag('< FOO>'))
self.assertEqual(None3, bcanalyzer.ParseTag('<//FOO>'))
self.assertEqual(None3, bcanalyzer.ParseTag('</ FOO>'))
self.assertEqual(None3, bcanalyzer.ParseTag('< FOO />'))
self.assertEqual(None3, bcanalyzer.ParseTag('<FOO<>'))
self.assertEqual(None3, bcanalyzer.ParseTag('<NOEND'))
self.assertEqual(None3, bcanalyzer.ParseTag('some text'))
self.assertEqual(None3, bcanalyzer.ParseTag(' <UNTRIMMED>'))
self.assertEqual(None3, bcanalyzer.ParseTag('<AAA>'))
def testAnalyzer(self):
# Save global param in bcanalyzer.
saved_char_width_limit = bcanalyzer._CHAR_WIDTH_LIMIT
for width_limit, include_4byte_strings in [(2, False), (4, True)]:
# Tweak global param in bcanalyzer.
bcanalyzer._CHAR_WIDTH_LIMIT = width_limit
with test_util.AddMocksToPath():
encoded_results = bcanalyzer.RunBcAnalyzerOnIntermediates(
['test.o'], test_util.TEST_OUTPUT_DIR)
results = parallel.DecodeDictOfLists(
encoded_results, value_transform=ast.literal_eval)
self.assertEqual(['test.o'], list(results.keys()))
str_list = results['test.o']
# See mock_bcanalyzer.py for details on the C++ test file.
expected = []
expected.append(_MakeBytes(8, ['Test1a', 0]))
expected.append(_MakeBytes(8, ['Test1b', 0]))
expected.append(_MakeBytes(8, ['Test2a', 0]))
expected.append(_MakeBytes(8, ['Test2b', 0]))
expected.append(_MakeBytes(16, ['Test3a', 0]))
expected.append(_MakeBytes(16, ['Test3b', 0]))
if include_4byte_strings:
expected.append(_MakeBytes(32, ['Test4a', 0]))
expected.append(_MakeBytes(32, ['Test4b', 0]))
expected.append(_MakeBytes(8, [1, 0, 0, 1, 1, 0]))
expected.append(_MakeBytes(8, [1, 0, 0, 1, 1, 1]))
expected.append(_MakeBytes(8, ['Test5a', 0]))
expected.append(_MakeBytes(8, ['Test5b', 1]))
expected.append(_MakeBytes(16, ['Test6a', 0]))
expected.append(_MakeBytes(16, ['Test6b', 1]))
if include_4byte_strings:
expected.append(_MakeBytes(32, ['Test7a', 0]))
expected.append(_MakeBytes(32, ['Test7b', 1]))
expected.append(_MakeBytes(8, ['Test8a', 0]))
expected.append(_MakeBytes(8, ['Test8b', 0]))
# Exclude |{u8a, u8b, u16a, u16b, u32a, u32b, u64a, u64b}|.
# Exclude |{s8empty, s16empty, s32empty}|.
expected.append(_MakeBytes(8, ['1a', 0]))
# Exclude |zeros|, which should be in .bss section.
self.assertEqual(expected, str_list)
# Restore globa param in bcanalyzer.
bcanalyzer._CHAR_WIDTH_LIMIT = saved_char_width_limit
if __name__ == '__main__':
unittest.main()