1*61c4878aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*61c4878aSAndroid Build Coastguard Worker# Copyright 2020 The Pigweed Authors 3*61c4878aSAndroid Build Coastguard Worker# 4*61c4878aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); you may not 5*61c4878aSAndroid Build Coastguard Worker# use this file except in compliance with the License. You may obtain a copy of 6*61c4878aSAndroid Build Coastguard Worker# the License at 7*61c4878aSAndroid Build Coastguard Worker# 8*61c4878aSAndroid Build Coastguard Worker# https://www.apache.org/licenses/LICENSE-2.0 9*61c4878aSAndroid Build Coastguard Worker# 10*61c4878aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*61c4878aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12*61c4878aSAndroid Build Coastguard Worker# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13*61c4878aSAndroid Build Coastguard Worker# License for the specific language governing permissions and limitations under 14*61c4878aSAndroid Build Coastguard Worker# the License. 15*61c4878aSAndroid Build Coastguard Worker"""Tests for detokenize.""" 16*61c4878aSAndroid Build Coastguard Worker 17*61c4878aSAndroid Build Coastguard Workerimport base64 18*61c4878aSAndroid Build Coastguard Workerimport concurrent 19*61c4878aSAndroid Build Coastguard Workerimport datetime as dt 20*61c4878aSAndroid Build Coastguard Workerimport functools 21*61c4878aSAndroid Build Coastguard Workerimport io 22*61c4878aSAndroid Build Coastguard Workerimport os 23*61c4878aSAndroid Build Coastguard Workerfrom pathlib import Path 24*61c4878aSAndroid Build Coastguard Workerimport struct 25*61c4878aSAndroid Build Coastguard Workerimport tempfile 26*61c4878aSAndroid Build Coastguard Workerfrom typing import Any, Callable, NamedTuple 27*61c4878aSAndroid Build Coastguard Workerimport unittest 28*61c4878aSAndroid Build Coastguard Workerfrom unittest import mock 29*61c4878aSAndroid Build Coastguard Worker 30*61c4878aSAndroid Build Coastguard Workerfrom pw_tokenizer import database 31*61c4878aSAndroid Build Coastguard Workerfrom pw_tokenizer import detokenize 32*61c4878aSAndroid Build Coastguard Workerfrom pw_tokenizer import elf_reader 33*61c4878aSAndroid Build Coastguard Workerfrom pw_tokenizer import tokens 34*61c4878aSAndroid Build Coastguard Worker 35*61c4878aSAndroid Build Coastguard Worker 36*61c4878aSAndroid Build Coastguard Worker# This function is not part of this test. It was used to generate the binary 37*61c4878aSAndroid Build Coastguard Worker# strings for EMPTY_ELF and ELF_WITH_TOKENIZER_SECTIONS. It takes a path and 38*61c4878aSAndroid Build Coastguard Worker# returns a Python byte string suitable for copying into Python source code. 39*61c4878aSAndroid Build Coastguard Workerdef path_to_byte_string(path: str) -> str: 40*61c4878aSAndroid Build Coastguard Worker with open(path, 'rb') as fd: 41*61c4878aSAndroid Build Coastguard Worker data = fd.read() 42*61c4878aSAndroid Build Coastguard Worker 43*61c4878aSAndroid Build Coastguard Worker output: list[str] = [] 44*61c4878aSAndroid Build Coastguard Worker indices = iter(range(len(data))) 45*61c4878aSAndroid Build Coastguard Worker 46*61c4878aSAndroid Build Coastguard Worker while True: 47*61c4878aSAndroid Build Coastguard Worker line = '' 48*61c4878aSAndroid Build Coastguard Worker 49*61c4878aSAndroid Build Coastguard Worker while len(line) < 70: 50*61c4878aSAndroid Build Coastguard Worker try: 51*61c4878aSAndroid Build Coastguard Worker i = next(indices) 52*61c4878aSAndroid Build Coastguard Worker except StopIteration: 53*61c4878aSAndroid Build Coastguard Worker break 54*61c4878aSAndroid Build Coastguard Worker 55*61c4878aSAndroid Build Coastguard Worker line += repr(data[i : i + 1])[2:-1].replace("'", r'\'') 56*61c4878aSAndroid Build Coastguard Worker 57*61c4878aSAndroid Build Coastguard Worker if not line: 58*61c4878aSAndroid Build Coastguard Worker return ''.join(output) 59*61c4878aSAndroid Build Coastguard Worker 60*61c4878aSAndroid Build Coastguard Worker output.append(" b'{}'\n".format(''.join(line))) 61*61c4878aSAndroid Build Coastguard Worker 62*61c4878aSAndroid Build Coastguard Worker 63*61c4878aSAndroid Build Coastguard Worker# This is an empty ELF file. It was created from the ELF file for 64*61c4878aSAndroid Build Coastguard Worker# tokenize_test.cc with the command: 65*61c4878aSAndroid Build Coastguard Worker# 66*61c4878aSAndroid Build Coastguard Worker# arm-none-eabi-objcopy -S --only-section NO_SECTIONS_PLEASE <ELF> <OUTPUT> 67*61c4878aSAndroid Build Coastguard Worker# 68*61c4878aSAndroid Build Coastguard Worker# The resulting ELF was converted to a Python binary string using 69*61c4878aSAndroid Build Coastguard Worker# path_to_byte_string function above. 70*61c4878aSAndroid Build Coastguard WorkerEMPTY_ELF = ( 71*61c4878aSAndroid Build Coastguard Worker b'\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00(\x00\x01' 72*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00\xd1\x83\x00\x084\x00\x00\x00\xe0\x00\x00\x00\x00\x04\x00\x05' 73*61c4878aSAndroid Build Coastguard Worker b'4\x00 \x00\x05\x00(\x00\x02\x00\x01\x00\x01\x00\x00\x00\xd4\x00\x00\x00' 74*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00' 75*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\xd4\x00\x00\x00\x00\x00\x00\x00' 76*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00' 77*61c4878aSAndroid Build Coastguard Worker b'\x01\x00\x01\x00\x00\x00\xd4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 78*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x01\x00\x01\x00' 79*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\xd4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 80*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\xd4\x00' 81*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 82*61c4878aSAndroid Build Coastguard Worker b'\x06\x00\x00\x00\x00\x00\x01\x00\x00.shstrtab\x00\x00\x00\x00\x00\x00\x00' 83*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 84*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' 85*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd4\x00\x00' 86*61c4878aSAndroid Build Coastguard Worker b'\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00' 87*61c4878aSAndroid Build Coastguard Worker b'\x00\x00\x00' 88*61c4878aSAndroid Build Coastguard Worker) 89*61c4878aSAndroid Build Coastguard Worker 90*61c4878aSAndroid Build Coastguard Worker# This is an ELF file with only the pw_tokenizer sections. It was created 91*61c4878aSAndroid Build Coastguard Worker# from a tokenize_test binary built for the STM32F429i Discovery board. The 92*61c4878aSAndroid Build Coastguard Worker# pw_tokenizer sections were extracted with this command: 93*61c4878aSAndroid Build Coastguard Worker# 94*61c4878aSAndroid Build Coastguard Worker# arm-none-eabi-objcopy -S --only-section ".pw_tokenizer*" <ELF> <OUTPUT> 95*61c4878aSAndroid Build Coastguard Worker# 96*61c4878aSAndroid Build Coastguard WorkerELF_WITH_TOKENIZER_SECTIONS_PATH = Path(__file__).parent.joinpath( 97*61c4878aSAndroid Build Coastguard Worker 'example_binary_with_tokenized_strings.elf' 98*61c4878aSAndroid Build Coastguard Worker) 99*61c4878aSAndroid Build Coastguard WorkerELF_WITH_TOKENIZER_SECTIONS = ELF_WITH_TOKENIZER_SECTIONS_PATH.read_bytes() 100*61c4878aSAndroid Build Coastguard Worker 101*61c4878aSAndroid Build Coastguard WorkerDEFAULT_DOMAIN_TOKENS = 22 102*61c4878aSAndroid Build Coastguard Worker 103*61c4878aSAndroid Build Coastguard Worker# 27 total since token 881436a0="The answer is: %s" is in two domains. 104*61c4878aSAndroid Build Coastguard WorkerALL_DOMAIN_TOKENS = 26 + 1 105*61c4878aSAndroid Build Coastguard Worker 106*61c4878aSAndroid Build Coastguard Worker# 0x2e668cd6 is 'Jello, world!' (which is also used in database_test.py). 107*61c4878aSAndroid Build Coastguard WorkerJELLO_WORLD_TOKEN = b'\xd6\x8c\x66\x2e' 108*61c4878aSAndroid Build Coastguard Worker 109*61c4878aSAndroid Build Coastguard Worker 110*61c4878aSAndroid Build Coastguard Workerclass DetokenizeTest(unittest.TestCase): 111*61c4878aSAndroid Build Coastguard Worker """Tests the detokenize.Detokenizer.""" 112*61c4878aSAndroid Build Coastguard Worker 113*61c4878aSAndroid Build Coastguard Worker def test_simple(self) -> None: 114*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 115*61c4878aSAndroid Build Coastguard Worker tokens.Database( 116*61c4878aSAndroid Build Coastguard Worker [ 117*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 118*61c4878aSAndroid Build Coastguard Worker 0xCDAB, '%02d %s %c%%', date_removed=dt.datetime.now() 119*61c4878aSAndroid Build Coastguard Worker ) 120*61c4878aSAndroid Build Coastguard Worker ] 121*61c4878aSAndroid Build Coastguard Worker ) 122*61c4878aSAndroid Build Coastguard Worker ) 123*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 124*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\xab\xcd\0\0\x02\x03Two\x66')), '01 Two 3%' 125*61c4878aSAndroid Build Coastguard Worker ) 126*61c4878aSAndroid Build Coastguard Worker 127*61c4878aSAndroid Build Coastguard Worker def test_detokenize_extra_data_is_unsuccessful(self) -> None: 128*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 129*61c4878aSAndroid Build Coastguard Worker tokens.Database( 130*61c4878aSAndroid Build Coastguard Worker [ 131*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 132*61c4878aSAndroid Build Coastguard Worker 1, 'no args', date_removed=dt.datetime(1, 1, 1) 133*61c4878aSAndroid Build Coastguard Worker ) 134*61c4878aSAndroid Build Coastguard Worker ] 135*61c4878aSAndroid Build Coastguard Worker ) 136*61c4878aSAndroid Build Coastguard Worker ) 137*61c4878aSAndroid Build Coastguard Worker 138*61c4878aSAndroid Build Coastguard Worker result = detok.detokenize(b'\x01\0\0\0\x04args') 139*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(result.failures), 1) 140*61c4878aSAndroid Build Coastguard Worker string, args, remaining = result.failures[0] 141*61c4878aSAndroid Build Coastguard Worker self.assertEqual('no args', string) 142*61c4878aSAndroid Build Coastguard Worker self.assertFalse(args) 143*61c4878aSAndroid Build Coastguard Worker self.assertEqual(b'\x04args', remaining) 144*61c4878aSAndroid Build Coastguard Worker self.assertEqual('no args', string) 145*61c4878aSAndroid Build Coastguard Worker self.assertEqual('no args', str(result)) 146*61c4878aSAndroid Build Coastguard Worker 147*61c4878aSAndroid Build Coastguard Worker def test_detokenize_zero_extend_short_token_with_no_args(self) -> None: 148*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 149*61c4878aSAndroid Build Coastguard Worker tokens.Database( 150*61c4878aSAndroid Build Coastguard Worker [tokens.TokenizedStringEntry(0xCDAB, 'This token is 16 bits')] 151*61c4878aSAndroid Build Coastguard Worker ) 152*61c4878aSAndroid Build Coastguard Worker ) 153*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 154*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\xab\xcd')), 'This token is 16 bits' 155*61c4878aSAndroid Build Coastguard Worker ) 156*61c4878aSAndroid Build Coastguard Worker 157*61c4878aSAndroid Build Coastguard Worker def test_detokenize_missing_data_is_unsuccessful(self) -> None: 158*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 159*61c4878aSAndroid Build Coastguard Worker tokens.Database( 160*61c4878aSAndroid Build Coastguard Worker [ 161*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 162*61c4878aSAndroid Build Coastguard Worker 2, '%s', date_removed=dt.datetime(1, 1, 1) 163*61c4878aSAndroid Build Coastguard Worker ) 164*61c4878aSAndroid Build Coastguard Worker ] 165*61c4878aSAndroid Build Coastguard Worker ) 166*61c4878aSAndroid Build Coastguard Worker ) 167*61c4878aSAndroid Build Coastguard Worker 168*61c4878aSAndroid Build Coastguard Worker result = detok.detokenize(b'\x02\0\0\0') 169*61c4878aSAndroid Build Coastguard Worker string, args, remaining = result.failures[0] 170*61c4878aSAndroid Build Coastguard Worker self.assertEqual('%s', string) 171*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(args), 1) 172*61c4878aSAndroid Build Coastguard Worker self.assertEqual(b'', remaining) 173*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(result.failures), 1) 174*61c4878aSAndroid Build Coastguard Worker self.assertEqual('%s', str(result)) 175*61c4878aSAndroid Build Coastguard Worker 176*61c4878aSAndroid Build Coastguard Worker def test_detokenize_missing_data_with_errors_is_unsuccessful(self) -> None: 177*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 178*61c4878aSAndroid Build Coastguard Worker tokens.Database( 179*61c4878aSAndroid Build Coastguard Worker [ 180*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 181*61c4878aSAndroid Build Coastguard Worker 2, '%s', date_removed=dt.datetime(1, 1, 1) 182*61c4878aSAndroid Build Coastguard Worker ) 183*61c4878aSAndroid Build Coastguard Worker ] 184*61c4878aSAndroid Build Coastguard Worker ), 185*61c4878aSAndroid Build Coastguard Worker show_errors=True, 186*61c4878aSAndroid Build Coastguard Worker ) 187*61c4878aSAndroid Build Coastguard Worker 188*61c4878aSAndroid Build Coastguard Worker result = detok.detokenize(b'\x02\0\0\0') 189*61c4878aSAndroid Build Coastguard Worker string, args, remaining = result.failures[0] 190*61c4878aSAndroid Build Coastguard Worker self.assertIn('%s MISSING', string) 191*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(args), 1) 192*61c4878aSAndroid Build Coastguard Worker self.assertEqual(b'', remaining) 193*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(result.failures), 1) 194*61c4878aSAndroid Build Coastguard Worker self.assertIn('%s MISSING', str(result)) 195*61c4878aSAndroid Build Coastguard Worker 196*61c4878aSAndroid Build Coastguard Worker def test_unparsed_data(self) -> None: 197*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 198*61c4878aSAndroid Build Coastguard Worker tokens.Database( 199*61c4878aSAndroid Build Coastguard Worker [ 200*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 201*61c4878aSAndroid Build Coastguard Worker 1, 'no args', date_removed=dt.datetime(100, 1, 1) 202*61c4878aSAndroid Build Coastguard Worker ), 203*61c4878aSAndroid Build Coastguard Worker ] 204*61c4878aSAndroid Build Coastguard Worker ) 205*61c4878aSAndroid Build Coastguard Worker ) 206*61c4878aSAndroid Build Coastguard Worker result = detok.detokenize(b'\x01\0\0\0o_o') 207*61c4878aSAndroid Build Coastguard Worker self.assertFalse(result.ok()) 208*61c4878aSAndroid Build Coastguard Worker self.assertEqual('no args', str(result)) 209*61c4878aSAndroid Build Coastguard Worker self.assertIn('o_o', repr(result)) 210*61c4878aSAndroid Build Coastguard Worker self.assertIn('decoding failed', result.error_message()) 211*61c4878aSAndroid Build Coastguard Worker 212*61c4878aSAndroid Build Coastguard Worker def test_empty_db(self) -> None: 213*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(io.BytesIO(EMPTY_ELF)) 214*61c4878aSAndroid Build Coastguard Worker self.assertFalse(detok.detokenize(b'\x12\x34\0\0').ok()) 215*61c4878aSAndroid Build Coastguard Worker self.assertIn( 216*61c4878aSAndroid Build Coastguard Worker 'unknown token', detok.detokenize(b'1234').error_message() 217*61c4878aSAndroid Build Coastguard Worker ) 218*61c4878aSAndroid Build Coastguard Worker self.assertIn('unknown token', repr(detok.detokenize(b'1234'))) 219*61c4878aSAndroid Build Coastguard Worker 220*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 221*61c4878aSAndroid Build Coastguard Worker '$' + base64.b64encode(b'1234').decode(), 222*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'1234')), 223*61c4878aSAndroid Build Coastguard Worker ) 224*61c4878aSAndroid Build Coastguard Worker 225*61c4878aSAndroid Build Coastguard Worker self.assertIsNone(detok.detokenize(b'').token) 226*61c4878aSAndroid Build Coastguard Worker 227*61c4878aSAndroid Build Coastguard Worker def test_empty_db_show_errors(self) -> None: 228*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(io.BytesIO(EMPTY_ELF), show_errors=True) 229*61c4878aSAndroid Build Coastguard Worker self.assertFalse(detok.detokenize(b'\x12\x34\0\0').ok()) 230*61c4878aSAndroid Build Coastguard Worker self.assertIn( 231*61c4878aSAndroid Build Coastguard Worker 'unknown token', detok.detokenize(b'1234').error_message() 232*61c4878aSAndroid Build Coastguard Worker ) 233*61c4878aSAndroid Build Coastguard Worker self.assertIn('unknown token', repr(detok.detokenize(b'1234'))) 234*61c4878aSAndroid Build Coastguard Worker self.assertIn('unknown token', str(detok.detokenize(b'1234'))) 235*61c4878aSAndroid Build Coastguard Worker 236*61c4878aSAndroid Build Coastguard Worker self.assertIsNone(detok.detokenize(b'').token) 237*61c4878aSAndroid Build Coastguard Worker 238*61c4878aSAndroid Build Coastguard Worker def test_missing_token_show_errors(self) -> None: 239*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(io.BytesIO(EMPTY_ELF), show_errors=True) 240*61c4878aSAndroid Build Coastguard Worker self.assertIn('missing token', detok.detokenize(b'').error_message()) 241*61c4878aSAndroid Build Coastguard Worker self.assertIn('missing token', str(detok.detokenize(b''))) 242*61c4878aSAndroid Build Coastguard Worker 243*61c4878aSAndroid Build Coastguard Worker def test_missing_token(self) -> None: 244*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(io.BytesIO(EMPTY_ELF)) 245*61c4878aSAndroid Build Coastguard Worker self.assertIn('missing token', detok.detokenize(b'').error_message()) 246*61c4878aSAndroid Build Coastguard Worker self.assertEqual('$', str(detok.detokenize(b''))) 247*61c4878aSAndroid Build Coastguard Worker 248*61c4878aSAndroid Build Coastguard Worker def test_unknown_shorter_token_show_error(self) -> None: 249*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(io.BytesIO(EMPTY_ELF), show_errors=True) 250*61c4878aSAndroid Build Coastguard Worker 251*61c4878aSAndroid Build Coastguard Worker self.assertIn('unknown token', detok.detokenize(b'1').error_message()) 252*61c4878aSAndroid Build Coastguard Worker self.assertIn('unknown token', str(detok.detokenize(b'1'))) 253*61c4878aSAndroid Build Coastguard Worker self.assertIn('unknown token', repr(detok.detokenize(b'1'))) 254*61c4878aSAndroid Build Coastguard Worker 255*61c4878aSAndroid Build Coastguard Worker self.assertIn('unknown token', detok.detokenize(b'123').error_message()) 256*61c4878aSAndroid Build Coastguard Worker self.assertIn('unknown token', str(detok.detokenize(b'123'))) 257*61c4878aSAndroid Build Coastguard Worker self.assertIn('unknown token', repr(detok.detokenize(b'123'))) 258*61c4878aSAndroid Build Coastguard Worker 259*61c4878aSAndroid Build Coastguard Worker def test_unknown_shorter_token(self) -> None: 260*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(io.BytesIO(EMPTY_ELF)) 261*61c4878aSAndroid Build Coastguard Worker 262*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 263*61c4878aSAndroid Build Coastguard Worker 'unknown token 00000001', detok.detokenize(b'\1').error_message() 264*61c4878aSAndroid Build Coastguard Worker ) 265*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 266*61c4878aSAndroid Build Coastguard Worker '$' + base64.b64encode(b'\1\0\0\0').decode(), 267*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\1')), 268*61c4878aSAndroid Build Coastguard Worker ) 269*61c4878aSAndroid Build Coastguard Worker self.assertIn('unknown token 00000001', repr(detok.detokenize(b'\1'))) 270*61c4878aSAndroid Build Coastguard Worker 271*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 272*61c4878aSAndroid Build Coastguard Worker 'unknown token 00030201', 273*61c4878aSAndroid Build Coastguard Worker detok.detokenize(b'\1\2\3').error_message(), 274*61c4878aSAndroid Build Coastguard Worker ) 275*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 276*61c4878aSAndroid Build Coastguard Worker '$' + base64.b64encode(b'\1\2\3\0').decode(), 277*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\1\2\3')), 278*61c4878aSAndroid Build Coastguard Worker ) 279*61c4878aSAndroid Build Coastguard Worker self.assertIn( 280*61c4878aSAndroid Build Coastguard Worker 'unknown token 00030201', repr(detok.detokenize(b'\1\2\3')) 281*61c4878aSAndroid Build Coastguard Worker ) 282*61c4878aSAndroid Build Coastguard Worker 283*61c4878aSAndroid Build Coastguard Worker def test_decode_from_elf_data(self) -> None: 284*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(io.BytesIO(ELF_WITH_TOKENIZER_SECTIONS)) 285*61c4878aSAndroid Build Coastguard Worker 286*61c4878aSAndroid Build Coastguard Worker self.assertTrue(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 287*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 288*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(JELLO_WORLD_TOKEN)), 'Jello, world!' 289*61c4878aSAndroid Build Coastguard Worker ) 290*61c4878aSAndroid Build Coastguard Worker 291*61c4878aSAndroid Build Coastguard Worker undecoded_args = detok.detokenize(JELLO_WORLD_TOKEN + b'some junk') 292*61c4878aSAndroid Build Coastguard Worker self.assertFalse(undecoded_args.ok()) 293*61c4878aSAndroid Build Coastguard Worker self.assertEqual(str(undecoded_args), 'Jello, world!') 294*61c4878aSAndroid Build Coastguard Worker 295*61c4878aSAndroid Build Coastguard Worker self.assertTrue(detok.detokenize(b'\0\0\0\0').ok()) 296*61c4878aSAndroid Build Coastguard Worker self.assertEqual(str(detok.detokenize(b'\0\0\0\0')), '') 297*61c4878aSAndroid Build Coastguard Worker 298*61c4878aSAndroid Build Coastguard Worker def test_decode_from_elf_file(self) -> None: 299*61c4878aSAndroid Build Coastguard Worker """Test decoding from an elf file.""" 300*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(io.BytesIO(ELF_WITH_TOKENIZER_SECTIONS)) 301*61c4878aSAndroid Build Coastguard Worker expected_tokens = frozenset(detok.database.token_to_entries.keys()) 302*61c4878aSAndroid Build Coastguard Worker 303*61c4878aSAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile('wb', delete=False) as elf: 304*61c4878aSAndroid Build Coastguard Worker try: 305*61c4878aSAndroid Build Coastguard Worker elf.write(ELF_WITH_TOKENIZER_SECTIONS) 306*61c4878aSAndroid Build Coastguard Worker elf.close() 307*61c4878aSAndroid Build Coastguard Worker 308*61c4878aSAndroid Build Coastguard Worker # Open ELF by file object 309*61c4878aSAndroid Build Coastguard Worker with open(elf.name, 'rb') as fd: 310*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(fd) 311*61c4878aSAndroid Build Coastguard Worker 312*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 313*61c4878aSAndroid Build Coastguard Worker expected_tokens, 314*61c4878aSAndroid Build Coastguard Worker frozenset(detok.database.token_to_entries.keys()), 315*61c4878aSAndroid Build Coastguard Worker ) 316*61c4878aSAndroid Build Coastguard Worker 317*61c4878aSAndroid Build Coastguard Worker # Open ELF by path 318*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(elf.name) 319*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 320*61c4878aSAndroid Build Coastguard Worker expected_tokens, 321*61c4878aSAndroid Build Coastguard Worker frozenset(detok.database.token_to_entries.keys()), 322*61c4878aSAndroid Build Coastguard Worker ) 323*61c4878aSAndroid Build Coastguard Worker 324*61c4878aSAndroid Build Coastguard Worker # Open ELF by elf_reader.Elf 325*61c4878aSAndroid Build Coastguard Worker with open(elf.name, 'rb') as fd: 326*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(elf_reader.Elf(fd)) 327*61c4878aSAndroid Build Coastguard Worker 328*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 329*61c4878aSAndroid Build Coastguard Worker expected_tokens, 330*61c4878aSAndroid Build Coastguard Worker frozenset(detok.database.token_to_entries.keys()), 331*61c4878aSAndroid Build Coastguard Worker ) 332*61c4878aSAndroid Build Coastguard Worker finally: 333*61c4878aSAndroid Build Coastguard Worker os.unlink(elf.name) 334*61c4878aSAndroid Build Coastguard Worker 335*61c4878aSAndroid Build Coastguard Worker def test_decode_from_csv_file(self) -> None: 336*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(io.BytesIO(ELF_WITH_TOKENIZER_SECTIONS)) 337*61c4878aSAndroid Build Coastguard Worker expected_tokens = frozenset(detok.database.token_to_entries.keys()) 338*61c4878aSAndroid Build Coastguard Worker 339*61c4878aSAndroid Build Coastguard Worker csv_database = str(detok.database) 340*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(csv_database.splitlines()), ALL_DOMAIN_TOKENS) 341*61c4878aSAndroid Build Coastguard Worker 342*61c4878aSAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile('w', delete=False) as csv_file: 343*61c4878aSAndroid Build Coastguard Worker try: 344*61c4878aSAndroid Build Coastguard Worker csv_file.write(csv_database) 345*61c4878aSAndroid Build Coastguard Worker csv_file.close() 346*61c4878aSAndroid Build Coastguard Worker 347*61c4878aSAndroid Build Coastguard Worker # Open CSV by path 348*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(csv_file.name) 349*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 350*61c4878aSAndroid Build Coastguard Worker expected_tokens, 351*61c4878aSAndroid Build Coastguard Worker frozenset(detok.database.token_to_entries.keys()), 352*61c4878aSAndroid Build Coastguard Worker ) 353*61c4878aSAndroid Build Coastguard Worker 354*61c4878aSAndroid Build Coastguard Worker # Open CSV by file object 355*61c4878aSAndroid Build Coastguard Worker with open(csv_file.name) as fd: 356*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(fd) 357*61c4878aSAndroid Build Coastguard Worker 358*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 359*61c4878aSAndroid Build Coastguard Worker expected_tokens, 360*61c4878aSAndroid Build Coastguard Worker frozenset(detok.database.token_to_entries.keys()), 361*61c4878aSAndroid Build Coastguard Worker ) 362*61c4878aSAndroid Build Coastguard Worker finally: 363*61c4878aSAndroid Build Coastguard Worker os.unlink(csv_file.name) 364*61c4878aSAndroid Build Coastguard Worker 365*61c4878aSAndroid Build Coastguard Worker def test_create_detokenizer_with_token_database(self) -> None: 366*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(io.BytesIO(ELF_WITH_TOKENIZER_SECTIONS)) 367*61c4878aSAndroid Build Coastguard Worker expected_tokens = frozenset(detok.database.token_to_entries.keys()) 368*61c4878aSAndroid Build Coastguard Worker 369*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer(detok.database) 370*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 371*61c4878aSAndroid Build Coastguard Worker expected_tokens, frozenset(detok.database.token_to_entries.keys()) 372*61c4878aSAndroid Build Coastguard Worker ) 373*61c4878aSAndroid Build Coastguard Worker 374*61c4878aSAndroid Build Coastguard Worker 375*61c4878aSAndroid Build Coastguard Workerclass DetokenizeWithCollisions(unittest.TestCase): 376*61c4878aSAndroid Build Coastguard Worker """Tests collision resolution.""" 377*61c4878aSAndroid Build Coastguard Worker 378*61c4878aSAndroid Build Coastguard Worker def setUp(self) -> None: 379*61c4878aSAndroid Build Coastguard Worker super().setUp() 380*61c4878aSAndroid Build Coastguard Worker token = 0xBAAD 381*61c4878aSAndroid Build Coastguard Worker 382*61c4878aSAndroid Build Coastguard Worker # Database with several conflicting tokens. 383*61c4878aSAndroid Build Coastguard Worker self.detok = detokenize.Detokenizer( 384*61c4878aSAndroid Build Coastguard Worker tokens.Database( 385*61c4878aSAndroid Build Coastguard Worker [ 386*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 387*61c4878aSAndroid Build Coastguard Worker token, 'REMOVED', date_removed=dt.datetime(9, 1, 1) 388*61c4878aSAndroid Build Coastguard Worker ), 389*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(token, 'newer'), 390*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 391*61c4878aSAndroid Build Coastguard Worker token, 'A: %d', date_removed=dt.datetime(30, 5, 9) 392*61c4878aSAndroid Build Coastguard Worker ), 393*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 394*61c4878aSAndroid Build Coastguard Worker token, 'B: %c', date_removed=dt.datetime(30, 5, 10) 395*61c4878aSAndroid Build Coastguard Worker ), 396*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(token, 'C: %s'), 397*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(token, '%d%u'), 398*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(token, '%s%u %d'), 399*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(1, '%s'), 400*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(1, '%d'), 401*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(2, 'Three %s %s %s'), 402*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(2, 'Five %d %d %d %d %s'), 403*61c4878aSAndroid Build Coastguard Worker ] 404*61c4878aSAndroid Build Coastguard Worker ) 405*61c4878aSAndroid Build Coastguard Worker ) 406*61c4878aSAndroid Build Coastguard Worker 407*61c4878aSAndroid Build Coastguard Worker def test_collision_no_args_favors_most_recently_present(self) -> None: 408*61c4878aSAndroid Build Coastguard Worker no_args = self.detok.detokenize(b'\xad\xba\0\0') 409*61c4878aSAndroid Build Coastguard Worker self.assertFalse(no_args.ok()) 410*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(no_args.successes), 2) 411*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(no_args.failures), 5) 412*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(no_args.matches()), 7) 413*61c4878aSAndroid Build Coastguard Worker self.assertEqual(str(no_args), 'newer') 414*61c4878aSAndroid Build Coastguard Worker best_result = no_args.best_result() 415*61c4878aSAndroid Build Coastguard Worker assert best_result is not None 416*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(best_result.args), 0) 417*61c4878aSAndroid Build Coastguard Worker self.assertEqual(best_result[0], 'newer') 418*61c4878aSAndroid Build Coastguard Worker 419*61c4878aSAndroid Build Coastguard Worker def test_collision_one_integer_arg_favors_most_recently_present( 420*61c4878aSAndroid Build Coastguard Worker self, 421*61c4878aSAndroid Build Coastguard Worker ) -> None: 422*61c4878aSAndroid Build Coastguard Worker multiple_correct = self.detok.detokenize(b'\xad\xba\0\0\x7a') 423*61c4878aSAndroid Build Coastguard Worker self.assertFalse(multiple_correct.ok()) 424*61c4878aSAndroid Build Coastguard Worker self.assertIn('ERROR', repr(multiple_correct)) 425*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(multiple_correct.successes), 2) 426*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(multiple_correct.failures), 5) 427*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(multiple_correct.matches()), 7) 428*61c4878aSAndroid Build Coastguard Worker self.assertEqual(str(multiple_correct), 'B: =') 429*61c4878aSAndroid Build Coastguard Worker 430*61c4878aSAndroid Build Coastguard Worker def test_collision_one_integer_arg_favor_successful_decode(self) -> None: 431*61c4878aSAndroid Build Coastguard Worker # One string decodes successfully, since the arg is out of range for %c. 432*61c4878aSAndroid Build Coastguard Worker int_arg = self.detok.detokenize(b'\xad\xba\0\0\xfe\xff\xff\xff\x0f') 433*61c4878aSAndroid Build Coastguard Worker self.assertTrue(int_arg.ok()) 434*61c4878aSAndroid Build Coastguard Worker self.assertEqual(str(int_arg), 'A: 2147483647') 435*61c4878aSAndroid Build Coastguard Worker 436*61c4878aSAndroid Build Coastguard Worker def test_collision_one_string_arg_favors_successful_decode(self) -> None: 437*61c4878aSAndroid Build Coastguard Worker # One string decodes successfully, since decoding the argument as an 438*61c4878aSAndroid Build Coastguard Worker # integer does not decode all the data. 439*61c4878aSAndroid Build Coastguard Worker string_arg = self.detok.detokenize(b'\xad\xba\0\0\x02Hi') 440*61c4878aSAndroid Build Coastguard Worker self.assertTrue(string_arg.ok()) 441*61c4878aSAndroid Build Coastguard Worker self.assertEqual(str(string_arg), 'C: Hi') 442*61c4878aSAndroid Build Coastguard Worker 443*61c4878aSAndroid Build Coastguard Worker def test_collision_one_string_arg_favors_decoding_all_data(self) -> None: 444*61c4878aSAndroid Build Coastguard Worker result = self.detok.detokenize(b'\1\0\0\0\x83hi') 445*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(result.failures), 2) 446*61c4878aSAndroid Build Coastguard Worker # Should resolve to the string since %d would leave one byte behind. 447*61c4878aSAndroid Build Coastguard Worker self.assertEqual(str(result), '%s') 448*61c4878aSAndroid Build Coastguard Worker 449*61c4878aSAndroid Build Coastguard Worker def test_collision_multiple_args_favors_decoding_more_arguments( 450*61c4878aSAndroid Build Coastguard Worker self, 451*61c4878aSAndroid Build Coastguard Worker ) -> None: 452*61c4878aSAndroid Build Coastguard Worker result = self.detok.detokenize(b'\2\0\0\0\1\2\1\4\5') 453*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(result.matches()), 2) 454*61c4878aSAndroid Build Coastguard Worker self.assertEqual(result.matches()[0][0], 'Five -1 1 -1 2 %s') 455*61c4878aSAndroid Build Coastguard Worker self.assertEqual(result.matches()[1][0], 'Three \2 \4 %s') 456*61c4878aSAndroid Build Coastguard Worker 457*61c4878aSAndroid Build Coastguard Worker def test_collision_multiple_args_favors_decoding_all_arguments( 458*61c4878aSAndroid Build Coastguard Worker self, 459*61c4878aSAndroid Build Coastguard Worker ) -> None: 460*61c4878aSAndroid Build Coastguard Worker unambiguous = self.detok.detokenize(b'\xad\xba\0\0\x01#\x00\x01') 461*61c4878aSAndroid Build Coastguard Worker self.assertTrue(unambiguous.ok()) 462*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(unambiguous.matches()), 7) 463*61c4878aSAndroid Build Coastguard Worker self.assertEqual('#0 -1', str(unambiguous)) 464*61c4878aSAndroid Build Coastguard Worker self.assertIn('#0 -1', repr(unambiguous)) 465*61c4878aSAndroid Build Coastguard Worker 466*61c4878aSAndroid Build Coastguard Worker 467*61c4878aSAndroid Build Coastguard Workerclass ManualPoolExecutor(concurrent.futures.Executor): 468*61c4878aSAndroid Build Coastguard Worker """A stubbed pool executor that captures the most recent work request 469*61c4878aSAndroid Build Coastguard Worker and holds it until the public process method is manually called.""" 470*61c4878aSAndroid Build Coastguard Worker 471*61c4878aSAndroid Build Coastguard Worker def __init__(self) -> None: 472*61c4878aSAndroid Build Coastguard Worker super().__init__() 473*61c4878aSAndroid Build Coastguard Worker self._func = None 474*61c4878aSAndroid Build Coastguard Worker 475*61c4878aSAndroid Build Coastguard Worker # pylint: disable=arguments-differ 476*61c4878aSAndroid Build Coastguard Worker def submit(self, func, *args, **kwargs): 477*61c4878aSAndroid Build Coastguard Worker """Submits work to the pool, stashing the partial for later use.""" 478*61c4878aSAndroid Build Coastguard Worker self._func = functools.partial(func, *args, **kwargs) 479*61c4878aSAndroid Build Coastguard Worker 480*61c4878aSAndroid Build Coastguard Worker def process(self) -> None: 481*61c4878aSAndroid Build Coastguard Worker """Processes the latest func submitted to the pool.""" 482*61c4878aSAndroid Build Coastguard Worker if self._func is not None: 483*61c4878aSAndroid Build Coastguard Worker self._func() 484*61c4878aSAndroid Build Coastguard Worker self._func = None 485*61c4878aSAndroid Build Coastguard Worker 486*61c4878aSAndroid Build Coastguard Worker 487*61c4878aSAndroid Build Coastguard Workerclass InlinePoolExecutor(concurrent.futures.Executor): 488*61c4878aSAndroid Build Coastguard Worker """A stubbed pool executor that runs work immediately, inline.""" 489*61c4878aSAndroid Build Coastguard Worker 490*61c4878aSAndroid Build Coastguard Worker # pylint: disable=arguments-differ 491*61c4878aSAndroid Build Coastguard Worker def submit(self, func, *args, **kwargs): 492*61c4878aSAndroid Build Coastguard Worker """Submits work to the pool, stashing the partial for later use.""" 493*61c4878aSAndroid Build Coastguard Worker func(*args, **kwargs) 494*61c4878aSAndroid Build Coastguard Worker 495*61c4878aSAndroid Build Coastguard Worker 496*61c4878aSAndroid Build Coastguard Worker@mock.patch('os.path.getmtime') 497*61c4878aSAndroid Build Coastguard Workerclass AutoUpdatingDetokenizerTest(unittest.TestCase): 498*61c4878aSAndroid Build Coastguard Worker """Tests the AutoUpdatingDetokenizer class.""" 499*61c4878aSAndroid Build Coastguard Worker 500*61c4878aSAndroid Build Coastguard Worker def test_update(self, mock_getmtime) -> None: 501*61c4878aSAndroid Build Coastguard Worker """Tests the update command.""" 502*61c4878aSAndroid Build Coastguard Worker 503*61c4878aSAndroid Build Coastguard Worker db = database.load_token_database( 504*61c4878aSAndroid Build Coastguard Worker io.BytesIO(ELF_WITH_TOKENIZER_SECTIONS) 505*61c4878aSAndroid Build Coastguard Worker ) 506*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(db), ALL_DOMAIN_TOKENS) 507*61c4878aSAndroid Build Coastguard Worker 508*61c4878aSAndroid Build Coastguard Worker the_time = [100] 509*61c4878aSAndroid Build Coastguard Worker 510*61c4878aSAndroid Build Coastguard Worker def move_back_time_if_file_exists(path) -> int: 511*61c4878aSAndroid Build Coastguard Worker if os.path.exists(path): 512*61c4878aSAndroid Build Coastguard Worker the_time[0] -= 1 513*61c4878aSAndroid Build Coastguard Worker return the_time[0] 514*61c4878aSAndroid Build Coastguard Worker 515*61c4878aSAndroid Build Coastguard Worker raise FileNotFoundError 516*61c4878aSAndroid Build Coastguard Worker 517*61c4878aSAndroid Build Coastguard Worker mock_getmtime.side_effect = move_back_time_if_file_exists 518*61c4878aSAndroid Build Coastguard Worker 519*61c4878aSAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile('wb', delete=False) as file: 520*61c4878aSAndroid Build Coastguard Worker try: 521*61c4878aSAndroid Build Coastguard Worker file.close() 522*61c4878aSAndroid Build Coastguard Worker 523*61c4878aSAndroid Build Coastguard Worker pool = ManualPoolExecutor() 524*61c4878aSAndroid Build Coastguard Worker detok = detokenize.AutoUpdatingDetokenizer( 525*61c4878aSAndroid Build Coastguard Worker file.name, min_poll_period_s=0, pool=pool 526*61c4878aSAndroid Build Coastguard Worker ) 527*61c4878aSAndroid Build Coastguard Worker self.assertFalse(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 528*61c4878aSAndroid Build Coastguard Worker 529*61c4878aSAndroid Build Coastguard Worker with open(file.name, 'wb') as fd: 530*61c4878aSAndroid Build Coastguard Worker tokens.write_binary(db, fd) 531*61c4878aSAndroid Build Coastguard Worker 532*61c4878aSAndroid Build Coastguard Worker # After the change but before the pool runs in another thread, 533*61c4878aSAndroid Build Coastguard Worker # the token should not exist. 534*61c4878aSAndroid Build Coastguard Worker self.assertFalse(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 535*61c4878aSAndroid Build Coastguard Worker 536*61c4878aSAndroid Build Coastguard Worker # After the pool is allowed to process, it should. 537*61c4878aSAndroid Build Coastguard Worker pool.process() 538*61c4878aSAndroid Build Coastguard Worker self.assertTrue(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 539*61c4878aSAndroid Build Coastguard Worker finally: 540*61c4878aSAndroid Build Coastguard Worker os.unlink(file.name) 541*61c4878aSAndroid Build Coastguard Worker 542*61c4878aSAndroid Build Coastguard Worker def test_update_with_directory(self, mock_getmtime) -> None: 543*61c4878aSAndroid Build Coastguard Worker """Tests the update command with a directory format database.""" 544*61c4878aSAndroid Build Coastguard Worker db = database.load_token_database( 545*61c4878aSAndroid Build Coastguard Worker io.BytesIO(ELF_WITH_TOKENIZER_SECTIONS) 546*61c4878aSAndroid Build Coastguard Worker ) 547*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(db), ALL_DOMAIN_TOKENS) 548*61c4878aSAndroid Build Coastguard Worker 549*61c4878aSAndroid Build Coastguard Worker the_time = [100] 550*61c4878aSAndroid Build Coastguard Worker 551*61c4878aSAndroid Build Coastguard Worker def move_back_time_if_file_exists(path: str) -> int: 552*61c4878aSAndroid Build Coastguard Worker if os.path.exists(path): 553*61c4878aSAndroid Build Coastguard Worker the_time[0] -= 1 554*61c4878aSAndroid Build Coastguard Worker return the_time[0] 555*61c4878aSAndroid Build Coastguard Worker 556*61c4878aSAndroid Build Coastguard Worker raise FileNotFoundError 557*61c4878aSAndroid Build Coastguard Worker 558*61c4878aSAndroid Build Coastguard Worker mock_getmtime.side_effect = move_back_time_if_file_exists 559*61c4878aSAndroid Build Coastguard Worker 560*61c4878aSAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as dbdir: 561*61c4878aSAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile( 562*61c4878aSAndroid Build Coastguard Worker 'wb', delete=False, suffix='.pw_tokenizer.csv', dir=dbdir 563*61c4878aSAndroid Build Coastguard Worker ) as matching_suffix_file, tempfile.NamedTemporaryFile( 564*61c4878aSAndroid Build Coastguard Worker 'wb', delete=False, suffix='.not.right', dir=dbdir 565*61c4878aSAndroid Build Coastguard Worker ) as mismatched_suffix_file: 566*61c4878aSAndroid Build Coastguard Worker try: 567*61c4878aSAndroid Build Coastguard Worker matching_suffix_file.close() 568*61c4878aSAndroid Build Coastguard Worker mismatched_suffix_file.close() 569*61c4878aSAndroid Build Coastguard Worker 570*61c4878aSAndroid Build Coastguard Worker pool = ManualPoolExecutor() 571*61c4878aSAndroid Build Coastguard Worker detok = detokenize.AutoUpdatingDetokenizer( 572*61c4878aSAndroid Build Coastguard Worker dbdir, min_poll_period_s=0, pool=pool 573*61c4878aSAndroid Build Coastguard Worker ) 574*61c4878aSAndroid Build Coastguard Worker self.assertFalse(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 575*61c4878aSAndroid Build Coastguard Worker 576*61c4878aSAndroid Build Coastguard Worker with open(mismatched_suffix_file.name, 'wb') as fd: 577*61c4878aSAndroid Build Coastguard Worker tokens.write_csv(db, fd) 578*61c4878aSAndroid Build Coastguard Worker pool.process() 579*61c4878aSAndroid Build Coastguard Worker self.assertFalse(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 580*61c4878aSAndroid Build Coastguard Worker 581*61c4878aSAndroid Build Coastguard Worker with open(matching_suffix_file.name, 'wb') as fd: 582*61c4878aSAndroid Build Coastguard Worker tokens.write_csv(db, fd) 583*61c4878aSAndroid Build Coastguard Worker 584*61c4878aSAndroid Build Coastguard Worker # After the change but before the pool runs in another 585*61c4878aSAndroid Build Coastguard Worker # thread, the token should not exist. 586*61c4878aSAndroid Build Coastguard Worker self.assertFalse(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 587*61c4878aSAndroid Build Coastguard Worker pool.process() 588*61c4878aSAndroid Build Coastguard Worker 589*61c4878aSAndroid Build Coastguard Worker # After the pool is allowed to process, it should. 590*61c4878aSAndroid Build Coastguard Worker self.assertTrue(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 591*61c4878aSAndroid Build Coastguard Worker finally: 592*61c4878aSAndroid Build Coastguard Worker os.unlink(mismatched_suffix_file.name) 593*61c4878aSAndroid Build Coastguard Worker os.unlink(matching_suffix_file.name) 594*61c4878aSAndroid Build Coastguard Worker os.rmdir(dbdir) 595*61c4878aSAndroid Build Coastguard Worker 596*61c4878aSAndroid Build Coastguard Worker # The database stays around if the file is deleted. 597*61c4878aSAndroid Build Coastguard Worker self.assertTrue(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 598*61c4878aSAndroid Build Coastguard Worker 599*61c4878aSAndroid Build Coastguard Worker def test_no_update_if_time_is_same(self, mock_getmtime) -> None: 600*61c4878aSAndroid Build Coastguard Worker mock_getmtime.return_value = 100 601*61c4878aSAndroid Build Coastguard Worker 602*61c4878aSAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile('wb', delete=False) as file: 603*61c4878aSAndroid Build Coastguard Worker try: 604*61c4878aSAndroid Build Coastguard Worker tokens.write_csv( 605*61c4878aSAndroid Build Coastguard Worker database.load_token_database( 606*61c4878aSAndroid Build Coastguard Worker io.BytesIO(ELF_WITH_TOKENIZER_SECTIONS) 607*61c4878aSAndroid Build Coastguard Worker ), 608*61c4878aSAndroid Build Coastguard Worker file, 609*61c4878aSAndroid Build Coastguard Worker ) 610*61c4878aSAndroid Build Coastguard Worker file.close() 611*61c4878aSAndroid Build Coastguard Worker 612*61c4878aSAndroid Build Coastguard Worker detok = detokenize.AutoUpdatingDetokenizer( 613*61c4878aSAndroid Build Coastguard Worker file.name, min_poll_period_s=0, pool=InlinePoolExecutor() 614*61c4878aSAndroid Build Coastguard Worker ) 615*61c4878aSAndroid Build Coastguard Worker self.assertTrue(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 616*61c4878aSAndroid Build Coastguard Worker 617*61c4878aSAndroid Build Coastguard Worker # Empty the database, but keep the mock modified time the same. 618*61c4878aSAndroid Build Coastguard Worker with open(file.name, 'wb'): 619*61c4878aSAndroid Build Coastguard Worker pass 620*61c4878aSAndroid Build Coastguard Worker 621*61c4878aSAndroid Build Coastguard Worker self.assertTrue(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 622*61c4878aSAndroid Build Coastguard Worker self.assertTrue(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 623*61c4878aSAndroid Build Coastguard Worker 624*61c4878aSAndroid Build Coastguard Worker # Move back time so the now-empty file is reloaded. 625*61c4878aSAndroid Build Coastguard Worker mock_getmtime.return_value = 50 626*61c4878aSAndroid Build Coastguard Worker self.assertFalse(detok.detokenize(JELLO_WORLD_TOKEN).ok()) 627*61c4878aSAndroid Build Coastguard Worker finally: 628*61c4878aSAndroid Build Coastguard Worker os.unlink(file.name) 629*61c4878aSAndroid Build Coastguard Worker 630*61c4878aSAndroid Build Coastguard Worker def test_token_domain_in_str(self, _) -> None: 631*61c4878aSAndroid Build Coastguard Worker """Tests a str containing a domain.""" 632*61c4878aSAndroid Build Coastguard Worker detok = detokenize.AutoUpdatingDetokenizer( 633*61c4878aSAndroid Build Coastguard Worker f'{ELF_WITH_TOKENIZER_SECTIONS_PATH}#', # Default domain 634*61c4878aSAndroid Build Coastguard Worker min_poll_period_s=0, 635*61c4878aSAndroid Build Coastguard Worker pool=InlinePoolExecutor(), 636*61c4878aSAndroid Build Coastguard Worker ) 637*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(detok.database), DEFAULT_DOMAIN_TOKENS) 638*61c4878aSAndroid Build Coastguard Worker 639*61c4878aSAndroid Build Coastguard Worker def test_token_domain_in_path(self, _) -> None: 640*61c4878aSAndroid Build Coastguard Worker """Tests a Path() containing a domain.""" 641*61c4878aSAndroid Build Coastguard Worker detok = detokenize.AutoUpdatingDetokenizer( 642*61c4878aSAndroid Build Coastguard Worker Path(f'{ELF_WITH_TOKENIZER_SECTIONS_PATH}#'), 643*61c4878aSAndroid Build Coastguard Worker min_poll_period_s=0, 644*61c4878aSAndroid Build Coastguard Worker pool=InlinePoolExecutor(), 645*61c4878aSAndroid Build Coastguard Worker ) 646*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(detok.database), DEFAULT_DOMAIN_TOKENS) 647*61c4878aSAndroid Build Coastguard Worker 648*61c4878aSAndroid Build Coastguard Worker def test_token_no_domain_in_str(self, _) -> None: 649*61c4878aSAndroid Build Coastguard Worker """Tests a str without a domain, which loads all domains.""" 650*61c4878aSAndroid Build Coastguard Worker detok = detokenize.AutoUpdatingDetokenizer( 651*61c4878aSAndroid Build Coastguard Worker str(ELF_WITH_TOKENIZER_SECTIONS_PATH), 652*61c4878aSAndroid Build Coastguard Worker min_poll_period_s=0, 653*61c4878aSAndroid Build Coastguard Worker pool=InlinePoolExecutor(), 654*61c4878aSAndroid Build Coastguard Worker ) 655*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(detok.database), ALL_DOMAIN_TOKENS) 656*61c4878aSAndroid Build Coastguard Worker 657*61c4878aSAndroid Build Coastguard Worker def test_token_no_domain_in_path(self, _) -> None: 658*61c4878aSAndroid Build Coastguard Worker """Tests a Path() without a domain, which loads all domains.""" 659*61c4878aSAndroid Build Coastguard Worker detok = detokenize.AutoUpdatingDetokenizer( 660*61c4878aSAndroid Build Coastguard Worker ELF_WITH_TOKENIZER_SECTIONS_PATH, 661*61c4878aSAndroid Build Coastguard Worker min_poll_period_s=0, 662*61c4878aSAndroid Build Coastguard Worker pool=InlinePoolExecutor(), 663*61c4878aSAndroid Build Coastguard Worker ) 664*61c4878aSAndroid Build Coastguard Worker self.assertEqual(len(detok.database), ALL_DOMAIN_TOKENS) 665*61c4878aSAndroid Build Coastguard Worker 666*61c4878aSAndroid Build Coastguard Worker def test_invalid_domain_specification(self, _) -> None: 667*61c4878aSAndroid Build Coastguard Worker with self.assertRaises(ValueError, msg='Too many # delimiters'): 668*61c4878aSAndroid Build Coastguard Worker detokenize.AutoUpdatingDetokenizer( 669*61c4878aSAndroid Build Coastguard Worker f'{ELF_WITH_TOKENIZER_SECTIONS_PATH}##', 670*61c4878aSAndroid Build Coastguard Worker min_poll_period_s=0, 671*61c4878aSAndroid Build Coastguard Worker pool=InlinePoolExecutor(), 672*61c4878aSAndroid Build Coastguard Worker ) 673*61c4878aSAndroid Build Coastguard Worker 674*61c4878aSAndroid Build Coastguard Worker 675*61c4878aSAndroid Build Coastguard Workerdef _next_char(message: bytes) -> bytes: 676*61c4878aSAndroid Build Coastguard Worker return bytes(b + 1 for b in message) 677*61c4878aSAndroid Build Coastguard Worker 678*61c4878aSAndroid Build Coastguard Worker 679*61c4878aSAndroid Build Coastguard Workerclass NestedMessageParserTest(unittest.TestCase): 680*61c4878aSAndroid Build Coastguard Worker """Tests parsing prefixed messages.""" 681*61c4878aSAndroid Build Coastguard Worker 682*61c4878aSAndroid Build Coastguard Worker class _Case(NamedTuple): 683*61c4878aSAndroid Build Coastguard Worker data: bytes 684*61c4878aSAndroid Build Coastguard Worker expected: bytes 685*61c4878aSAndroid Build Coastguard Worker title: str 686*61c4878aSAndroid Build Coastguard Worker transform: Callable[[bytes], bytes] = _next_char 687*61c4878aSAndroid Build Coastguard Worker 688*61c4878aSAndroid Build Coastguard Worker TRANSFORM_TEST_CASES = ( 689*61c4878aSAndroid Build Coastguard Worker _Case(b'$abcd', b'%bcde', 'single message'), 690*61c4878aSAndroid Build Coastguard Worker _Case( 691*61c4878aSAndroid Build Coastguard Worker b'$$WHAT?$abc$WHY? is this $ok $', 692*61c4878aSAndroid Build Coastguard Worker b'%%WHAT?%bcd%WHY? is this %ok %', 693*61c4878aSAndroid Build Coastguard Worker 'message and non-message', 694*61c4878aSAndroid Build Coastguard Worker ), 695*61c4878aSAndroid Build Coastguard Worker _Case(b'$1$', b'%1%', 'empty message'), 696*61c4878aSAndroid Build Coastguard Worker _Case(b'$abc$defgh', b'%bcd%efghh', 'sequential message'), 697*61c4878aSAndroid Build Coastguard Worker _Case( 698*61c4878aSAndroid Build Coastguard Worker b'w$abcx$defygh$$abz', 699*61c4878aSAndroid Build Coastguard Worker b'w$ABCx$DEFygh$$ABz', 700*61c4878aSAndroid Build Coastguard Worker 'interspersed start/end non-message', 701*61c4878aSAndroid Build Coastguard Worker bytes.upper, 702*61c4878aSAndroid Build Coastguard Worker ), 703*61c4878aSAndroid Build Coastguard Worker _Case( 704*61c4878aSAndroid Build Coastguard Worker b'$abcx$defygh$$ab', 705*61c4878aSAndroid Build Coastguard Worker b'$ABCx$DEFygh$$AB', 706*61c4878aSAndroid Build Coastguard Worker 'interspersed start/end message ', 707*61c4878aSAndroid Build Coastguard Worker bytes.upper, 708*61c4878aSAndroid Build Coastguard Worker ), 709*61c4878aSAndroid Build Coastguard Worker ) 710*61c4878aSAndroid Build Coastguard Worker 711*61c4878aSAndroid Build Coastguard Worker def setUp(self) -> None: 712*61c4878aSAndroid Build Coastguard Worker self.decoder = detokenize.NestedMessageParser('$', 'abcdefg') 713*61c4878aSAndroid Build Coastguard Worker 714*61c4878aSAndroid Build Coastguard Worker def test_transform_io(self) -> None: 715*61c4878aSAndroid Build Coastguard Worker for data, expected, title, transform in self.TRANSFORM_TEST_CASES: 716*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 717*61c4878aSAndroid Build Coastguard Worker expected, 718*61c4878aSAndroid Build Coastguard Worker b''.join( 719*61c4878aSAndroid Build Coastguard Worker self.decoder.transform_io(io.BytesIO(data), transform) 720*61c4878aSAndroid Build Coastguard Worker ), 721*61c4878aSAndroid Build Coastguard Worker f'{title}: {data!r}', 722*61c4878aSAndroid Build Coastguard Worker ) 723*61c4878aSAndroid Build Coastguard Worker 724*61c4878aSAndroid Build Coastguard Worker def test_transform_bytes_with_flush(self) -> None: 725*61c4878aSAndroid Build Coastguard Worker for data, expected, title, transform in self.TRANSFORM_TEST_CASES: 726*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 727*61c4878aSAndroid Build Coastguard Worker expected, 728*61c4878aSAndroid Build Coastguard Worker self.decoder.transform(data, transform, flush=True), 729*61c4878aSAndroid Build Coastguard Worker f'{title}: {data!r}', 730*61c4878aSAndroid Build Coastguard Worker ) 731*61c4878aSAndroid Build Coastguard Worker 732*61c4878aSAndroid Build Coastguard Worker def test_transform_bytes_sequential(self) -> None: 733*61c4878aSAndroid Build Coastguard Worker def transform(message): 734*61c4878aSAndroid Build Coastguard Worker return message.upper().replace(b'$', b'*') 735*61c4878aSAndroid Build Coastguard Worker 736*61c4878aSAndroid Build Coastguard Worker self.assertEqual(self.decoder.transform(b'abc$abcd', transform), b'abc') 737*61c4878aSAndroid Build Coastguard Worker self.assertEqual(self.decoder.transform(b'$', transform), b'*ABCD') 738*61c4878aSAndroid Build Coastguard Worker self.assertEqual(self.decoder.transform(b'$b', transform), b'*') 739*61c4878aSAndroid Build Coastguard Worker self.assertEqual(self.decoder.transform(b'', transform), b'') 740*61c4878aSAndroid Build Coastguard Worker self.assertEqual(self.decoder.transform(b' ', transform), b'*B ') 741*61c4878aSAndroid Build Coastguard Worker self.assertEqual(self.decoder.transform(b'hello', transform), b'hello') 742*61c4878aSAndroid Build Coastguard Worker self.assertEqual(self.decoder.transform(b'?? $ab', transform), b'?? ') 743*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 744*61c4878aSAndroid Build Coastguard Worker self.decoder.transform(b'123$ab4$56$a', transform), b'*AB123*AB4*56' 745*61c4878aSAndroid Build Coastguard Worker ) 746*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 747*61c4878aSAndroid Build Coastguard Worker self.decoder.transform(b'bc', transform, flush=True), b'*ABC' 748*61c4878aSAndroid Build Coastguard Worker ) 749*61c4878aSAndroid Build Coastguard Worker 750*61c4878aSAndroid Build Coastguard Worker MESSAGES_TEST: Any = ( 751*61c4878aSAndroid Build Coastguard Worker (b'123$abc456$a', (False, b'123'), (True, b'$abc'), (False, b'456')), 752*61c4878aSAndroid Build Coastguard Worker (b'7$abcd', (True, b'$a'), (False, b'7')), 753*61c4878aSAndroid Build Coastguard Worker (b'e',), 754*61c4878aSAndroid Build Coastguard Worker (b'',), 755*61c4878aSAndroid Build Coastguard Worker (b'$', (True, b'$abcde')), 756*61c4878aSAndroid Build Coastguard Worker (b'$', (True, b'$')), 757*61c4878aSAndroid Build Coastguard Worker (b'$a$b$c', (True, b'$'), (True, b'$a'), (True, b'$b')), 758*61c4878aSAndroid Build Coastguard Worker (b'1', (True, b'$c'), (False, b'1')), 759*61c4878aSAndroid Build Coastguard Worker (b'',), 760*61c4878aSAndroid Build Coastguard Worker (b'?', (False, b'?')), 761*61c4878aSAndroid Build Coastguard Worker (b'!@', (False, b'!@')), 762*61c4878aSAndroid Build Coastguard Worker (b'%^&', (False, b'%^&')), 763*61c4878aSAndroid Build Coastguard Worker ) 764*61c4878aSAndroid Build Coastguard Worker 765*61c4878aSAndroid Build Coastguard Worker def test_read_messages(self) -> None: 766*61c4878aSAndroid Build Coastguard Worker for step in self.MESSAGES_TEST: 767*61c4878aSAndroid Build Coastguard Worker data: bytes = step[0] 768*61c4878aSAndroid Build Coastguard Worker pieces: tuple[tuple[bool, bytes], ...] = step[1:] 769*61c4878aSAndroid Build Coastguard Worker self.assertEqual(tuple(self.decoder.read_messages(data)), pieces) 770*61c4878aSAndroid Build Coastguard Worker 771*61c4878aSAndroid Build Coastguard Worker def test_read_messages_flush(self) -> None: 772*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 773*61c4878aSAndroid Build Coastguard Worker list(self.decoder.read_messages(b'123$a')), [(False, b'123')] 774*61c4878aSAndroid Build Coastguard Worker ) 775*61c4878aSAndroid Build Coastguard Worker self.assertEqual(list(self.decoder.read_messages(b'b')), []) 776*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 777*61c4878aSAndroid Build Coastguard Worker list(self.decoder.read_messages(b'', flush=True)), [(True, b'$ab')] 778*61c4878aSAndroid Build Coastguard Worker ) 779*61c4878aSAndroid Build Coastguard Worker 780*61c4878aSAndroid Build Coastguard Worker def test_read_messages_io(self) -> None: 781*61c4878aSAndroid Build Coastguard Worker # Rework the read_messages test data for stream input. 782*61c4878aSAndroid Build Coastguard Worker data = io.BytesIO(b''.join(step[0] for step in self.MESSAGES_TEST)) 783*61c4878aSAndroid Build Coastguard Worker expected_pieces = sum((step[1:] for step in self.MESSAGES_TEST), ()) 784*61c4878aSAndroid Build Coastguard Worker 785*61c4878aSAndroid Build Coastguard Worker result = self.decoder.read_messages_io(data) 786*61c4878aSAndroid Build Coastguard Worker for expected_is_message, expected_data in expected_pieces: 787*61c4878aSAndroid Build Coastguard Worker if expected_is_message: 788*61c4878aSAndroid Build Coastguard Worker is_message, piece = next(result) 789*61c4878aSAndroid Build Coastguard Worker self.assertTrue(is_message) 790*61c4878aSAndroid Build Coastguard Worker self.assertEqual(expected_data, piece) 791*61c4878aSAndroid Build Coastguard Worker else: # the IO version yields non-messages byte by byte 792*61c4878aSAndroid Build Coastguard Worker for byte in expected_data: 793*61c4878aSAndroid Build Coastguard Worker is_message, piece = next(result) 794*61c4878aSAndroid Build Coastguard Worker self.assertFalse(is_message) 795*61c4878aSAndroid Build Coastguard Worker self.assertEqual(bytes([byte]), piece) 796*61c4878aSAndroid Build Coastguard Worker 797*61c4878aSAndroid Build Coastguard Worker 798*61c4878aSAndroid Build Coastguard Workerclass DetokenizeNested(unittest.TestCase): 799*61c4878aSAndroid Build Coastguard Worker """Tests detokenizing nested tokens""" 800*61c4878aSAndroid Build Coastguard Worker 801*61c4878aSAndroid Build Coastguard Worker def test_nested_hashed_arg(self) -> None: 802*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 803*61c4878aSAndroid Build Coastguard Worker tokens.Database( 804*61c4878aSAndroid Build Coastguard Worker [ 805*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(0xA, 'tokenized argument'), 806*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 807*61c4878aSAndroid Build Coastguard Worker 2, 808*61c4878aSAndroid Build Coastguard Worker 'This is a ' + '$#%08x', 809*61c4878aSAndroid Build Coastguard Worker ), 810*61c4878aSAndroid Build Coastguard Worker ] 811*61c4878aSAndroid Build Coastguard Worker ) 812*61c4878aSAndroid Build Coastguard Worker ) 813*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 814*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\x02\0\0\0\x14')), 815*61c4878aSAndroid Build Coastguard Worker 'This is a tokenized argument', 816*61c4878aSAndroid Build Coastguard Worker ) 817*61c4878aSAndroid Build Coastguard Worker 818*61c4878aSAndroid Build Coastguard Worker def test_nested_base64_arg(self) -> None: 819*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 820*61c4878aSAndroid Build Coastguard Worker tokens.Database( 821*61c4878aSAndroid Build Coastguard Worker [ 822*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(1, 'base64 argument'), 823*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(2, 'This is a %s'), 824*61c4878aSAndroid Build Coastguard Worker ] 825*61c4878aSAndroid Build Coastguard Worker ) 826*61c4878aSAndroid Build Coastguard Worker ) 827*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 828*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\x02\0\0\0\x09$AQAAAA==')), # token for 1 829*61c4878aSAndroid Build Coastguard Worker 'This is a base64 argument', 830*61c4878aSAndroid Build Coastguard Worker ) 831*61c4878aSAndroid Build Coastguard Worker 832*61c4878aSAndroid Build Coastguard Worker def test_deeply_nested_arg(self) -> None: 833*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 834*61c4878aSAndroid Build Coastguard Worker tokens.Database( 835*61c4878aSAndroid Build Coastguard Worker [ 836*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(1, '$10#0000000005'), 837*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(2, 'This is a $#%08x'), 838*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(3, 'deeply nested argument'), 839*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(4, '$AQAAAA=='), 840*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(5, '$AwAAAA=='), 841*61c4878aSAndroid Build Coastguard Worker ] 842*61c4878aSAndroid Build Coastguard Worker ) 843*61c4878aSAndroid Build Coastguard Worker ) 844*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 845*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\x02\0\0\0\x08')), # token for 4 846*61c4878aSAndroid Build Coastguard Worker 'This is a deeply nested argument', 847*61c4878aSAndroid Build Coastguard Worker ) 848*61c4878aSAndroid Build Coastguard Worker 849*61c4878aSAndroid Build Coastguard Worker 850*61c4878aSAndroid Build Coastguard Workerclass DetokenizeBase64(unittest.TestCase): 851*61c4878aSAndroid Build Coastguard Worker """Tests detokenizing Base64 messages.""" 852*61c4878aSAndroid Build Coastguard Worker 853*61c4878aSAndroid Build Coastguard Worker JELLO = b'$' + base64.b64encode(JELLO_WORLD_TOKEN) 854*61c4878aSAndroid Build Coastguard Worker 855*61c4878aSAndroid Build Coastguard Worker RECURSION_STRING = f'The secret message is "{JELLO.decode()}"' 856*61c4878aSAndroid Build Coastguard Worker RECURSION = b'$' + base64.b64encode( 857*61c4878aSAndroid Build Coastguard Worker struct.pack('I', tokens.c_hash(RECURSION_STRING)) 858*61c4878aSAndroid Build Coastguard Worker ) 859*61c4878aSAndroid Build Coastguard Worker 860*61c4878aSAndroid Build Coastguard Worker RECURSION_STRING_2 = f"'{RECURSION.decode()}', said the spy." 861*61c4878aSAndroid Build Coastguard Worker RECURSION_2 = b'$' + base64.b64encode( 862*61c4878aSAndroid Build Coastguard Worker struct.pack('I', tokens.c_hash(RECURSION_STRING_2)) 863*61c4878aSAndroid Build Coastguard Worker ) 864*61c4878aSAndroid Build Coastguard Worker 865*61c4878aSAndroid Build Coastguard Worker TEST_CASES = ( 866*61c4878aSAndroid Build Coastguard Worker (b'', b''), 867*61c4878aSAndroid Build Coastguard Worker (b'nothing here', b'nothing here'), 868*61c4878aSAndroid Build Coastguard Worker (JELLO, b'Jello, world!'), 869*61c4878aSAndroid Build Coastguard Worker (JELLO + b'a', b'Jello, world!a'), 870*61c4878aSAndroid Build Coastguard Worker (JELLO + b'abc', b'Jello, world!abc'), 871*61c4878aSAndroid Build Coastguard Worker (JELLO + b'abc=', b'Jello, world!abc='), 872*61c4878aSAndroid Build Coastguard Worker (b'$a' + JELLO + b'a', b'$aJello, world!a'), 873*61c4878aSAndroid Build Coastguard Worker (b'Hello ' + JELLO + b'?', b'Hello Jello, world!?'), 874*61c4878aSAndroid Build Coastguard Worker (b'$' + JELLO, b'$Jello, world!'), 875*61c4878aSAndroid Build Coastguard Worker (JELLO + JELLO, b'Jello, world!Jello, world!'), 876*61c4878aSAndroid Build Coastguard Worker (JELLO + b'$' + JELLO, b'Jello, world!$Jello, world!'), 877*61c4878aSAndroid Build Coastguard Worker (JELLO + b'$a' + JELLO + b'bcd', b'Jello, world!$aJello, world!bcd'), 878*61c4878aSAndroid Build Coastguard Worker (b'$3141', b'$3141'), 879*61c4878aSAndroid Build Coastguard Worker (JELLO + b'$3141', b'Jello, world!$3141'), 880*61c4878aSAndroid Build Coastguard Worker ( 881*61c4878aSAndroid Build Coastguard Worker JELLO + b'$a' + JELLO + b'b' + JELLO + b'c', 882*61c4878aSAndroid Build Coastguard Worker b'Jello, world!$aJello, world!bJello, world!c', 883*61c4878aSAndroid Build Coastguard Worker ), 884*61c4878aSAndroid Build Coastguard Worker (RECURSION, b'The secret message is "Jello, world!"'), 885*61c4878aSAndroid Build Coastguard Worker ( 886*61c4878aSAndroid Build Coastguard Worker RECURSION_2, 887*61c4878aSAndroid Build Coastguard Worker b'\'The secret message is "Jello, world!"\', said the spy.', 888*61c4878aSAndroid Build Coastguard Worker ), 889*61c4878aSAndroid Build Coastguard Worker ) 890*61c4878aSAndroid Build Coastguard Worker 891*61c4878aSAndroid Build Coastguard Worker def setUp(self) -> None: 892*61c4878aSAndroid Build Coastguard Worker super().setUp() 893*61c4878aSAndroid Build Coastguard Worker db = database.load_token_database( 894*61c4878aSAndroid Build Coastguard Worker io.BytesIO(ELF_WITH_TOKENIZER_SECTIONS) 895*61c4878aSAndroid Build Coastguard Worker ) 896*61c4878aSAndroid Build Coastguard Worker db.add( 897*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(tokens.c_hash(s), s) 898*61c4878aSAndroid Build Coastguard Worker for s in [self.RECURSION_STRING, self.RECURSION_STRING_2] 899*61c4878aSAndroid Build Coastguard Worker ) 900*61c4878aSAndroid Build Coastguard Worker self.detok = detokenize.Detokenizer(db) 901*61c4878aSAndroid Build Coastguard Worker 902*61c4878aSAndroid Build Coastguard Worker def test_detokenize_base64_live(self) -> None: 903*61c4878aSAndroid Build Coastguard Worker for data, expected in self.TEST_CASES: 904*61c4878aSAndroid Build Coastguard Worker output = io.BytesIO() 905*61c4878aSAndroid Build Coastguard Worker self.detok.detokenize_base64_live(io.BytesIO(data), output) 906*61c4878aSAndroid Build Coastguard Worker 907*61c4878aSAndroid Build Coastguard Worker self.assertEqual(expected, output.getvalue(), f'Input: {data!r}') 908*61c4878aSAndroid Build Coastguard Worker 909*61c4878aSAndroid Build Coastguard Worker def test_detokenize_base64_to_file(self) -> None: 910*61c4878aSAndroid Build Coastguard Worker for data, expected in self.TEST_CASES: 911*61c4878aSAndroid Build Coastguard Worker output = io.BytesIO() 912*61c4878aSAndroid Build Coastguard Worker self.detok.detokenize_base64_to_file(data, output) 913*61c4878aSAndroid Build Coastguard Worker 914*61c4878aSAndroid Build Coastguard Worker self.assertEqual(expected, output.getvalue()) 915*61c4878aSAndroid Build Coastguard Worker 916*61c4878aSAndroid Build Coastguard Worker def test_detokenize_base64(self) -> None: 917*61c4878aSAndroid Build Coastguard Worker for data, expected in self.TEST_CASES: 918*61c4878aSAndroid Build Coastguard Worker self.assertEqual(expected, self.detok.detokenize_base64(data)) 919*61c4878aSAndroid Build Coastguard Worker 920*61c4878aSAndroid Build Coastguard Worker def test_detokenize_base64_str(self) -> None: 921*61c4878aSAndroid Build Coastguard Worker for data, expected in self.TEST_CASES: 922*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 923*61c4878aSAndroid Build Coastguard Worker expected.decode(), self.detok.detokenize_base64(data.decode()) 924*61c4878aSAndroid Build Coastguard Worker ) 925*61c4878aSAndroid Build Coastguard Worker 926*61c4878aSAndroid Build Coastguard Worker 927*61c4878aSAndroid Build Coastguard Workerclass DetokenizeInfiniteRecursion(unittest.TestCase): 928*61c4878aSAndroid Build Coastguard Worker """Tests that infinite Base64 token recursion resolves.""" 929*61c4878aSAndroid Build Coastguard Worker 930*61c4878aSAndroid Build Coastguard Worker def setUp(self) -> None: 931*61c4878aSAndroid Build Coastguard Worker super().setUp() 932*61c4878aSAndroid Build Coastguard Worker self.detok = detokenize.Detokenizer( 933*61c4878aSAndroid Build Coastguard Worker tokens.Database( 934*61c4878aSAndroid Build Coastguard Worker [ 935*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(0, '$AAAAAA=='), # token for 0 936*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(1, '$AgAAAA=='), # token for 2 937*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(2, '$#00000003'), # token for 3 938*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(3, '$AgAAAA=='), # token for 2 939*61c4878aSAndroid Build Coastguard Worker ] 940*61c4878aSAndroid Build Coastguard Worker ) 941*61c4878aSAndroid Build Coastguard Worker ) 942*61c4878aSAndroid Build Coastguard Worker 943*61c4878aSAndroid Build Coastguard Worker def test_detokenize_self_recursion(self) -> None: 944*61c4878aSAndroid Build Coastguard Worker for depth in range(5): 945*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 946*61c4878aSAndroid Build Coastguard Worker self.detok.detokenize_text( 947*61c4878aSAndroid Build Coastguard Worker b'This one is deep: $AAAAAA==', recursion=depth 948*61c4878aSAndroid Build Coastguard Worker ), 949*61c4878aSAndroid Build Coastguard Worker b'This one is deep: $AAAAAA==', 950*61c4878aSAndroid Build Coastguard Worker ) 951*61c4878aSAndroid Build Coastguard Worker 952*61c4878aSAndroid Build Coastguard Worker def test_detokenize_self_recursion_default(self) -> None: 953*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 954*61c4878aSAndroid Build Coastguard Worker self.detok.detokenize_text( 955*61c4878aSAndroid Build Coastguard Worker b'This one is deep: $AAAAAA==', 956*61c4878aSAndroid Build Coastguard Worker ), 957*61c4878aSAndroid Build Coastguard Worker b'This one is deep: $AAAAAA==', 958*61c4878aSAndroid Build Coastguard Worker ) 959*61c4878aSAndroid Build Coastguard Worker 960*61c4878aSAndroid Build Coastguard Worker def test_detokenize_cyclic_recursion_even(self) -> None: 961*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 962*61c4878aSAndroid Build Coastguard Worker self.detok.detokenize_text(b'I said "$AQAAAA=="', recursion=6), 963*61c4878aSAndroid Build Coastguard Worker b'I said "$AgAAAA=="', 964*61c4878aSAndroid Build Coastguard Worker ) 965*61c4878aSAndroid Build Coastguard Worker 966*61c4878aSAndroid Build Coastguard Worker def test_detokenize_cyclic_recursion_odd(self) -> None: 967*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 968*61c4878aSAndroid Build Coastguard Worker self.detok.detokenize_text(b'I said "$AQAAAA=="', recursion=7), 969*61c4878aSAndroid Build Coastguard Worker b'I said "$#00000003"', 970*61c4878aSAndroid Build Coastguard Worker ) 971*61c4878aSAndroid Build Coastguard Worker 972*61c4878aSAndroid Build Coastguard Worker 973*61c4878aSAndroid Build Coastguard Workerclass DetokenizeBase64InfiniteRecursion(unittest.TestCase): 974*61c4878aSAndroid Build Coastguard Worker """Tests that infinite Bas64 token recursion resolves.""" 975*61c4878aSAndroid Build Coastguard Worker 976*61c4878aSAndroid Build Coastguard Worker def setUp(self) -> None: 977*61c4878aSAndroid Build Coastguard Worker super().setUp() 978*61c4878aSAndroid Build Coastguard Worker self.detok = detokenize.Detokenizer( 979*61c4878aSAndroid Build Coastguard Worker tokens.Database( 980*61c4878aSAndroid Build Coastguard Worker [ 981*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(0, '$AAAAAA=='), # token for 0 982*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(1, '$AgAAAA=='), # token for 2 983*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(2, '$AwAAAA=='), # token for 3 984*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(3, '$AgAAAA=='), # token for 2 985*61c4878aSAndroid Build Coastguard Worker ] 986*61c4878aSAndroid Build Coastguard Worker ) 987*61c4878aSAndroid Build Coastguard Worker ) 988*61c4878aSAndroid Build Coastguard Worker 989*61c4878aSAndroid Build Coastguard Worker def test_detokenize_self_recursion(self) -> None: 990*61c4878aSAndroid Build Coastguard Worker for depth in range(5): 991*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 992*61c4878aSAndroid Build Coastguard Worker self.detok.detokenize_base64( 993*61c4878aSAndroid Build Coastguard Worker b'This one is deep: $AAAAAA==', recursion=depth 994*61c4878aSAndroid Build Coastguard Worker ), 995*61c4878aSAndroid Build Coastguard Worker b'This one is deep: $AAAAAA==', 996*61c4878aSAndroid Build Coastguard Worker ) 997*61c4878aSAndroid Build Coastguard Worker 998*61c4878aSAndroid Build Coastguard Worker def test_detokenize_self_recursion_default(self) -> None: 999*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 1000*61c4878aSAndroid Build Coastguard Worker self.detok.detokenize_base64(b'This one is deep: $64#AAAAAA=='), 1001*61c4878aSAndroid Build Coastguard Worker b'This one is deep: $AAAAAA==', 1002*61c4878aSAndroid Build Coastguard Worker ) 1003*61c4878aSAndroid Build Coastguard Worker 1004*61c4878aSAndroid Build Coastguard Worker def test_detokenize_cyclic_recursion_even(self) -> None: 1005*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 1006*61c4878aSAndroid Build Coastguard Worker self.detok.detokenize_base64(b'I said "$AQAAAA=="', recursion=2), 1007*61c4878aSAndroid Build Coastguard Worker b'I said "$AgAAAA=="', 1008*61c4878aSAndroid Build Coastguard Worker ) 1009*61c4878aSAndroid Build Coastguard Worker 1010*61c4878aSAndroid Build Coastguard Worker def test_detokenize_cyclic_recursion_odd(self) -> None: 1011*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 1012*61c4878aSAndroid Build Coastguard Worker self.detok.detokenize_base64(b'I said "$AQAAAA=="', recursion=3), 1013*61c4878aSAndroid Build Coastguard Worker b'I said "$AwAAAA=="', 1014*61c4878aSAndroid Build Coastguard Worker ) 1015*61c4878aSAndroid Build Coastguard Worker 1016*61c4878aSAndroid Build Coastguard Worker 1017*61c4878aSAndroid Build Coastguard Workerclass DetokenizeNestedDomains(unittest.TestCase): 1018*61c4878aSAndroid Build Coastguard Worker """Tests detokenizing nested tokens with specified domains""" 1019*61c4878aSAndroid Build Coastguard Worker 1020*61c4878aSAndroid Build Coastguard Worker def test_nested_hashed_arg_with_one_domain_match(self) -> None: 1021*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 1022*61c4878aSAndroid Build Coastguard Worker tokens.Database( 1023*61c4878aSAndroid Build Coastguard Worker [ 1024*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(0xA, 'domain1', 'D1'), 1025*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 1026*61c4878aSAndroid Build Coastguard Worker 2, 'This is all in ' + '${D1}#%08x', 'D1' 1027*61c4878aSAndroid Build Coastguard Worker ), 1028*61c4878aSAndroid Build Coastguard Worker ] 1029*61c4878aSAndroid Build Coastguard Worker ) 1030*61c4878aSAndroid Build Coastguard Worker ) 1031*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 1032*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\x02\0\0\0\x14')), 1033*61c4878aSAndroid Build Coastguard Worker 'This is all in domain1', 1034*61c4878aSAndroid Build Coastguard Worker ) 1035*61c4878aSAndroid Build Coastguard Worker 1036*61c4878aSAndroid Build Coastguard Worker def test_multiple_nested_args_in_one_sentence(self) -> None: 1037*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 1038*61c4878aSAndroid Build Coastguard Worker tokens.Database( 1039*61c4878aSAndroid Build Coastguard Worker [ 1040*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(0xA, 'nested token 1', 'D1'), 1041*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 1042*61c4878aSAndroid Build Coastguard Worker 2, 1043*61c4878aSAndroid Build Coastguard Worker 'This is ' 1044*61c4878aSAndroid Build Coastguard Worker + '${D1}#%08x' 1045*61c4878aSAndroid Build Coastguard Worker + ' and this is ' 1046*61c4878aSAndroid Build Coastguard Worker + '${D1}#00000003', 1047*61c4878aSAndroid Build Coastguard Worker 'D1', 1048*61c4878aSAndroid Build Coastguard Worker ), 1049*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(3, 'nested token 2', 'D1'), 1050*61c4878aSAndroid Build Coastguard Worker ] 1051*61c4878aSAndroid Build Coastguard Worker ) 1052*61c4878aSAndroid Build Coastguard Worker ) 1053*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 1054*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\x02\0\0\0\x14')), 1055*61c4878aSAndroid Build Coastguard Worker 'This is nested token 1 and this is nested token 2', 1056*61c4878aSAndroid Build Coastguard Worker ) 1057*61c4878aSAndroid Build Coastguard Worker 1058*61c4878aSAndroid Build Coastguard Worker def test_nested_hashed_arg_with_two_domain_match(self) -> None: 1059*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 1060*61c4878aSAndroid Build Coastguard Worker tokens.Database( 1061*61c4878aSAndroid Build Coastguard Worker [ 1062*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 1063*61c4878aSAndroid Build Coastguard Worker 0xA, 'and this is domain1', 'D1' 1064*61c4878aSAndroid Build Coastguard Worker ), 1065*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 1066*61c4878aSAndroid Build Coastguard Worker 2, 'This is domain2 ' + '${D1}#%08x', 'D2' 1067*61c4878aSAndroid Build Coastguard Worker ), 1068*61c4878aSAndroid Build Coastguard Worker ] 1069*61c4878aSAndroid Build Coastguard Worker ) 1070*61c4878aSAndroid Build Coastguard Worker ) 1071*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 1072*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\x02\0\0\0\x14')), 1073*61c4878aSAndroid Build Coastguard Worker 'This is domain2 and this is domain1', 1074*61c4878aSAndroid Build Coastguard Worker ) 1075*61c4878aSAndroid Build Coastguard Worker 1076*61c4878aSAndroid Build Coastguard Worker def test_nested_hashed_arg_with_different_domains(self) -> None: 1077*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 1078*61c4878aSAndroid Build Coastguard Worker tokens.Database( 1079*61c4878aSAndroid Build Coastguard Worker [ 1080*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(0xA, 'domain1', 'D1'), 1081*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 1082*61c4878aSAndroid Build Coastguard Worker 2, 'This is all in ' + '${D2}#%08x', 'D1' 1083*61c4878aSAndroid Build Coastguard Worker ), 1084*61c4878aSAndroid Build Coastguard Worker ] 1085*61c4878aSAndroid Build Coastguard Worker ) 1086*61c4878aSAndroid Build Coastguard Worker ) 1087*61c4878aSAndroid Build Coastguard Worker result = detok.detokenize(b'\x02\0\0\0\x14') 1088*61c4878aSAndroid Build Coastguard Worker self.assertFalse(result == 'This is all in domain1') 1089*61c4878aSAndroid Build Coastguard Worker 1090*61c4878aSAndroid Build Coastguard Worker def test_nested_base64_arg_multiple_domains(self) -> None: 1091*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 1092*61c4878aSAndroid Build Coastguard Worker tokens.Database( 1093*61c4878aSAndroid Build Coastguard Worker [ 1094*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(1, '${D5}#00000004', 'D1'), 1095*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(2, 'This is a %s', 'D1'), 1096*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry(3, 'base64 argument', 'D2'), 1097*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 1098*61c4878aSAndroid Build Coastguard Worker 4, 'nested ' + '${D2}#00000003', 'D5' 1099*61c4878aSAndroid Build Coastguard Worker ), 1100*61c4878aSAndroid Build Coastguard Worker ] 1101*61c4878aSAndroid Build Coastguard Worker ) 1102*61c4878aSAndroid Build Coastguard Worker ) 1103*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 1104*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\x02\0\0\0\x09$AQAAAA==')), # token for 1 1105*61c4878aSAndroid Build Coastguard Worker 'This is a nested base64 argument', 1106*61c4878aSAndroid Build Coastguard Worker ) 1107*61c4878aSAndroid Build Coastguard Worker 1108*61c4878aSAndroid Build Coastguard Worker def test_nested_hashed_arg_with_domain_whitespace(self) -> None: 1109*61c4878aSAndroid Build Coastguard Worker detok = detokenize.Detokenizer( 1110*61c4878aSAndroid Build Coastguard Worker tokens.Database( 1111*61c4878aSAndroid Build Coastguard Worker [ 1112*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 1113*61c4878aSAndroid Build Coastguard Worker 0xA, 'and this is domain1', 'Domain1' 1114*61c4878aSAndroid Build Coastguard Worker ), 1115*61c4878aSAndroid Build Coastguard Worker tokens.TokenizedStringEntry( 1116*61c4878aSAndroid Build Coastguard Worker 2, 'This is domain2 ' + '${Domain 1}#%08x', 'D2' 1117*61c4878aSAndroid Build Coastguard Worker ), 1118*61c4878aSAndroid Build Coastguard Worker ] 1119*61c4878aSAndroid Build Coastguard Worker ) 1120*61c4878aSAndroid Build Coastguard Worker ) 1121*61c4878aSAndroid Build Coastguard Worker self.assertEqual( 1122*61c4878aSAndroid Build Coastguard Worker str(detok.detokenize(b'\x02\0\0\0\x14')), 1123*61c4878aSAndroid Build Coastguard Worker 'This is domain2 and this is domain1', 1124*61c4878aSAndroid Build Coastguard Worker ) 1125*61c4878aSAndroid Build Coastguard Worker 1126*61c4878aSAndroid Build Coastguard Worker 1127*61c4878aSAndroid Build Coastguard Workerif __name__ == '__main__': 1128*61c4878aSAndroid Build Coastguard Worker unittest.main() 1129