1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python 2*9e94795aSAndroid Build Coastguard Worker# 3*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project 4*9e94795aSAndroid Build Coastguard Worker# 5*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*9e94795aSAndroid Build Coastguard Worker# 9*9e94795aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*9e94795aSAndroid Build Coastguard Worker# 11*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*9e94795aSAndroid Build Coastguard Worker# limitations under the License. 16*9e94795aSAndroid Build Coastguard Worker# 17*9e94795aSAndroid Build Coastguard Worker 18*9e94795aSAndroid Build Coastguard Worker""" 19*9e94795aSAndroid Build Coastguard WorkerUtils for running unittests. 20*9e94795aSAndroid Build Coastguard Worker""" 21*9e94795aSAndroid Build Coastguard Worker 22*9e94795aSAndroid Build Coastguard Workerimport avbtool 23*9e94795aSAndroid Build Coastguard Workerimport logging 24*9e94795aSAndroid Build Coastguard Workerimport os 25*9e94795aSAndroid Build Coastguard Workerimport os.path 26*9e94795aSAndroid Build Coastguard Workerimport re 27*9e94795aSAndroid Build Coastguard Workerimport struct 28*9e94795aSAndroid Build Coastguard Workerimport sys 29*9e94795aSAndroid Build Coastguard Workerimport unittest 30*9e94795aSAndroid Build Coastguard Workerimport zipfile 31*9e94795aSAndroid Build Coastguard Worker 32*9e94795aSAndroid Build Coastguard Workerimport common 33*9e94795aSAndroid Build Coastguard Worker 34*9e94795aSAndroid Build Coastguard Worker# Some test runner doesn't like outputs from stderr. 35*9e94795aSAndroid Build Coastguard Workerlogging.basicConfig(stream=sys.stdout) 36*9e94795aSAndroid Build Coastguard Worker 37*9e94795aSAndroid Build Coastguard WorkerALLOWED_TEST_SUBDIRS = ('merge',) 38*9e94795aSAndroid Build Coastguard Worker 39*9e94795aSAndroid Build Coastguard Worker# Use ANDROID_BUILD_TOP as an indicator to tell if the needed tools (e.g. 40*9e94795aSAndroid Build Coastguard Worker# avbtool, mke2fs) are available while running the tests, unless 41*9e94795aSAndroid Build Coastguard Worker# FORCE_RUN_RELEASETOOLS is set to '1'. Not having the required vars means we 42*9e94795aSAndroid Build Coastguard Worker# can't run the tests that require external tools. 43*9e94795aSAndroid Build Coastguard WorkerEXTERNAL_TOOLS_UNAVAILABLE = ( 44*9e94795aSAndroid Build Coastguard Worker not os.environ.get('ANDROID_BUILD_TOP') and 45*9e94795aSAndroid Build Coastguard Worker os.environ.get('FORCE_RUN_RELEASETOOLS') != '1') 46*9e94795aSAndroid Build Coastguard Worker 47*9e94795aSAndroid Build Coastguard Worker 48*9e94795aSAndroid Build Coastguard Workerdef SkipIfExternalToolsUnavailable(): 49*9e94795aSAndroid Build Coastguard Worker """Decorator function that allows skipping tests per tools availability.""" 50*9e94795aSAndroid Build Coastguard Worker if EXTERNAL_TOOLS_UNAVAILABLE: 51*9e94795aSAndroid Build Coastguard Worker return unittest.skip('External tools unavailable') 52*9e94795aSAndroid Build Coastguard Worker return lambda func: func 53*9e94795aSAndroid Build Coastguard Worker 54*9e94795aSAndroid Build Coastguard Worker 55*9e94795aSAndroid Build Coastguard Workerdef get_testdata_dir(): 56*9e94795aSAndroid Build Coastguard Worker """Returns the testdata dir, in relative to the script dir.""" 57*9e94795aSAndroid Build Coastguard Worker # The script dir is the one we want, which could be different from pwd. 58*9e94795aSAndroid Build Coastguard Worker current_dir = os.path.dirname(os.path.realpath(__file__)) 59*9e94795aSAndroid Build Coastguard Worker return os.path.join(current_dir, 'testdata') 60*9e94795aSAndroid Build Coastguard Worker 61*9e94795aSAndroid Build Coastguard Worker 62*9e94795aSAndroid Build Coastguard Workerdef get_current_dir(): 63*9e94795aSAndroid Build Coastguard Worker """Returns the current dir, relative to the script dir.""" 64*9e94795aSAndroid Build Coastguard Worker # The script dir is the one we want, which could be different from pwd. 65*9e94795aSAndroid Build Coastguard Worker current_dir = os.path.dirname(os.path.realpath(__file__)) 66*9e94795aSAndroid Build Coastguard Worker return current_dir 67*9e94795aSAndroid Build Coastguard Worker 68*9e94795aSAndroid Build Coastguard Worker 69*9e94795aSAndroid Build Coastguard Workerdef get_search_path(): 70*9e94795aSAndroid Build Coastguard Worker """Returns the search path that has 'framework/signapk.jar' under.""" 71*9e94795aSAndroid Build Coastguard Worker 72*9e94795aSAndroid Build Coastguard Worker def signapk_exists(path): 73*9e94795aSAndroid Build Coastguard Worker signapk_path = os.path.realpath( 74*9e94795aSAndroid Build Coastguard Worker os.path.join(path, 'framework', 'signapk.jar')) 75*9e94795aSAndroid Build Coastguard Worker return os.path.exists(signapk_path) 76*9e94795aSAndroid Build Coastguard Worker 77*9e94795aSAndroid Build Coastguard Worker # Try with ANDROID_BUILD_TOP first. 78*9e94795aSAndroid Build Coastguard Worker full_path = os.path.realpath(os.path.join( 79*9e94795aSAndroid Build Coastguard Worker os.environ.get('ANDROID_BUILD_TOP', ''), 'out', 'host', 'linux-x86')) 80*9e94795aSAndroid Build Coastguard Worker if signapk_exists(full_path): 81*9e94795aSAndroid Build Coastguard Worker return full_path 82*9e94795aSAndroid Build Coastguard Worker 83*9e94795aSAndroid Build Coastguard Worker # Otherwise try going with relative pathes. 84*9e94795aSAndroid Build Coastguard Worker current_dir = os.path.dirname(os.path.realpath(__file__)) 85*9e94795aSAndroid Build Coastguard Worker for path in ( 86*9e94795aSAndroid Build Coastguard Worker # In relative to 'build/make/tools/releasetools' in the Android source. 87*9e94795aSAndroid Build Coastguard Worker ['..'] * 4 + ['out', 'host', 'linux-x86'], 88*9e94795aSAndroid Build Coastguard Worker # Or running the script unpacked from otatools.zip. 89*9e94795aSAndroid Build Coastguard Worker ['..']): 90*9e94795aSAndroid Build Coastguard Worker full_path = os.path.realpath(os.path.join(current_dir, *path)) 91*9e94795aSAndroid Build Coastguard Worker if signapk_exists(full_path): 92*9e94795aSAndroid Build Coastguard Worker return full_path 93*9e94795aSAndroid Build Coastguard Worker return None 94*9e94795aSAndroid Build Coastguard Worker 95*9e94795aSAndroid Build Coastguard Worker 96*9e94795aSAndroid Build Coastguard Workerdef append_avb_footer(file_path: str, partition_name: str = ""): 97*9e94795aSAndroid Build Coastguard Worker avb = avbtool.AvbTool() 98*9e94795aSAndroid Build Coastguard Worker try: 99*9e94795aSAndroid Build Coastguard Worker args = ["avbtool", "add_hashtree_footer", "--image", file_path, 100*9e94795aSAndroid Build Coastguard Worker "--partition_name", partition_name, "--do_not_generate_fec"] 101*9e94795aSAndroid Build Coastguard Worker avb.run(args) 102*9e94795aSAndroid Build Coastguard Worker except SystemExit: 103*9e94795aSAndroid Build Coastguard Worker raise ValueError(f"Failed to append hashtree footer {args}") 104*9e94795aSAndroid Build Coastguard Worker 105*9e94795aSAndroid Build Coastguard Worker 106*9e94795aSAndroid Build Coastguard Workerdef erase_avb_footer(file_path: str): 107*9e94795aSAndroid Build Coastguard Worker avb = avbtool.AvbTool() 108*9e94795aSAndroid Build Coastguard Worker try: 109*9e94795aSAndroid Build Coastguard Worker args = ["avbtool", "erase_footer", "--image", file_path] 110*9e94795aSAndroid Build Coastguard Worker avb.run(args) 111*9e94795aSAndroid Build Coastguard Worker except SystemExit: 112*9e94795aSAndroid Build Coastguard Worker raise ValueError(f"Failed to erase hashtree footer {args}") 113*9e94795aSAndroid Build Coastguard Worker 114*9e94795aSAndroid Build Coastguard Worker 115*9e94795aSAndroid Build Coastguard Workerdef construct_sparse_image(chunks, partition_name: str = ""): 116*9e94795aSAndroid Build Coastguard Worker """Returns a sparse image file constructed from the given chunks. 117*9e94795aSAndroid Build Coastguard Worker 118*9e94795aSAndroid Build Coastguard Worker From system/core/libsparse/sparse_format.h. 119*9e94795aSAndroid Build Coastguard Worker typedef struct sparse_header { 120*9e94795aSAndroid Build Coastguard Worker __le32 magic; // 0xed26ff3a 121*9e94795aSAndroid Build Coastguard Worker __le16 major_version; // (0x1) - reject images with higher major versions 122*9e94795aSAndroid Build Coastguard Worker __le16 minor_version; // (0x0) - allow images with higer minor versions 123*9e94795aSAndroid Build Coastguard Worker __le16 file_hdr_sz; // 28 bytes for first revision of the file format 124*9e94795aSAndroid Build Coastguard Worker __le16 chunk_hdr_sz; // 12 bytes for first revision of the file format 125*9e94795aSAndroid Build Coastguard Worker __le32 blk_sz; // block size in bytes, must be a multiple of 4 (4096) 126*9e94795aSAndroid Build Coastguard Worker __le32 total_blks; // total blocks in the non-sparse output image 127*9e94795aSAndroid Build Coastguard Worker __le32 total_chunks; // total chunks in the sparse input image 128*9e94795aSAndroid Build Coastguard Worker __le32 image_checksum; // CRC32 checksum of the original data, counting 129*9e94795aSAndroid Build Coastguard Worker // "don't care" as 0. Standard 802.3 polynomial, 130*9e94795aSAndroid Build Coastguard Worker // use a Public Domain table implementation 131*9e94795aSAndroid Build Coastguard Worker } sparse_header_t; 132*9e94795aSAndroid Build Coastguard Worker 133*9e94795aSAndroid Build Coastguard Worker typedef struct chunk_header { 134*9e94795aSAndroid Build Coastguard Worker __le16 chunk_type; // 0xCAC1 -> raw; 0xCAC2 -> fill; 135*9e94795aSAndroid Build Coastguard Worker // 0xCAC3 -> don't care 136*9e94795aSAndroid Build Coastguard Worker __le16 reserved1; 137*9e94795aSAndroid Build Coastguard Worker __le32 chunk_sz; // in blocks in output image 138*9e94795aSAndroid Build Coastguard Worker __le32 total_sz; // in bytes of chunk input file including chunk header 139*9e94795aSAndroid Build Coastguard Worker // and data 140*9e94795aSAndroid Build Coastguard Worker } chunk_header_t; 141*9e94795aSAndroid Build Coastguard Worker 142*9e94795aSAndroid Build Coastguard Worker Args: 143*9e94795aSAndroid Build Coastguard Worker chunks: A list of chunks to be written. Each entry should be a tuple of 144*9e94795aSAndroid Build Coastguard Worker (chunk_type, block_number). 145*9e94795aSAndroid Build Coastguard Worker 146*9e94795aSAndroid Build Coastguard Worker Returns: 147*9e94795aSAndroid Build Coastguard Worker Filename of the created sparse image. 148*9e94795aSAndroid Build Coastguard Worker """ 149*9e94795aSAndroid Build Coastguard Worker SPARSE_HEADER_MAGIC = 0xED26FF3A 150*9e94795aSAndroid Build Coastguard Worker SPARSE_HEADER_FORMAT = "<I4H4I" 151*9e94795aSAndroid Build Coastguard Worker CHUNK_HEADER_FORMAT = "<2H2I" 152*9e94795aSAndroid Build Coastguard Worker 153*9e94795aSAndroid Build Coastguard Worker sparse_image = common.MakeTempFile(prefix='sparse-', suffix='.img') 154*9e94795aSAndroid Build Coastguard Worker with open(sparse_image, 'wb') as fp: 155*9e94795aSAndroid Build Coastguard Worker fp.write(struct.pack( 156*9e94795aSAndroid Build Coastguard Worker SPARSE_HEADER_FORMAT, SPARSE_HEADER_MAGIC, 1, 0, 28, 12, 4096, 157*9e94795aSAndroid Build Coastguard Worker sum(chunk[1] for chunk in chunks), 158*9e94795aSAndroid Build Coastguard Worker len(chunks), 0)) 159*9e94795aSAndroid Build Coastguard Worker 160*9e94795aSAndroid Build Coastguard Worker for chunk in chunks: 161*9e94795aSAndroid Build Coastguard Worker data_size = 0 162*9e94795aSAndroid Build Coastguard Worker if chunk[0] == 0xCAC1: 163*9e94795aSAndroid Build Coastguard Worker data_size = 4096 * chunk[1] 164*9e94795aSAndroid Build Coastguard Worker elif chunk[0] == 0xCAC2: 165*9e94795aSAndroid Build Coastguard Worker data_size = 4 166*9e94795aSAndroid Build Coastguard Worker elif chunk[0] == 0xCAC3: 167*9e94795aSAndroid Build Coastguard Worker pass 168*9e94795aSAndroid Build Coastguard Worker else: 169*9e94795aSAndroid Build Coastguard Worker assert False, "Unsupported chunk type: {}".format(chunk[0]) 170*9e94795aSAndroid Build Coastguard Worker 171*9e94795aSAndroid Build Coastguard Worker fp.write(struct.pack( 172*9e94795aSAndroid Build Coastguard Worker CHUNK_HEADER_FORMAT, chunk[0], 0, chunk[1], data_size + 12)) 173*9e94795aSAndroid Build Coastguard Worker if data_size != 0: 174*9e94795aSAndroid Build Coastguard Worker fp.write(os.urandom(data_size)) 175*9e94795aSAndroid Build Coastguard Worker 176*9e94795aSAndroid Build Coastguard Worker append_avb_footer(sparse_image, partition_name) 177*9e94795aSAndroid Build Coastguard Worker return sparse_image 178*9e94795aSAndroid Build Coastguard Worker 179*9e94795aSAndroid Build Coastguard Worker 180*9e94795aSAndroid Build Coastguard Workerclass MockScriptWriter(object): 181*9e94795aSAndroid Build Coastguard Worker """A class that mocks edify_generator.EdifyGenerator. 182*9e94795aSAndroid Build Coastguard Worker 183*9e94795aSAndroid Build Coastguard Worker It simply pushes the incoming arguments onto script stack, which is to assert 184*9e94795aSAndroid Build Coastguard Worker the calls to EdifyGenerator functions. 185*9e94795aSAndroid Build Coastguard Worker """ 186*9e94795aSAndroid Build Coastguard Worker 187*9e94795aSAndroid Build Coastguard Worker def __init__(self, enable_comments=False): 188*9e94795aSAndroid Build Coastguard Worker self.lines = [] 189*9e94795aSAndroid Build Coastguard Worker self.enable_comments = enable_comments 190*9e94795aSAndroid Build Coastguard Worker 191*9e94795aSAndroid Build Coastguard Worker def Mount(self, *args): 192*9e94795aSAndroid Build Coastguard Worker self.lines.append(('Mount',) + args) 193*9e94795aSAndroid Build Coastguard Worker 194*9e94795aSAndroid Build Coastguard Worker def AssertDevice(self, *args): 195*9e94795aSAndroid Build Coastguard Worker self.lines.append(('AssertDevice',) + args) 196*9e94795aSAndroid Build Coastguard Worker 197*9e94795aSAndroid Build Coastguard Worker def AssertOemProperty(self, *args): 198*9e94795aSAndroid Build Coastguard Worker self.lines.append(('AssertOemProperty',) + args) 199*9e94795aSAndroid Build Coastguard Worker 200*9e94795aSAndroid Build Coastguard Worker def AssertFingerprintOrThumbprint(self, *args): 201*9e94795aSAndroid Build Coastguard Worker self.lines.append(('AssertFingerprintOrThumbprint',) + args) 202*9e94795aSAndroid Build Coastguard Worker 203*9e94795aSAndroid Build Coastguard Worker def AssertSomeFingerprint(self, *args): 204*9e94795aSAndroid Build Coastguard Worker self.lines.append(('AssertSomeFingerprint',) + args) 205*9e94795aSAndroid Build Coastguard Worker 206*9e94795aSAndroid Build Coastguard Worker def AssertSomeThumbprint(self, *args): 207*9e94795aSAndroid Build Coastguard Worker self.lines.append(('AssertSomeThumbprint',) + args) 208*9e94795aSAndroid Build Coastguard Worker 209*9e94795aSAndroid Build Coastguard Worker def Comment(self, comment): 210*9e94795aSAndroid Build Coastguard Worker if not self.enable_comments: 211*9e94795aSAndroid Build Coastguard Worker return 212*9e94795aSAndroid Build Coastguard Worker self.lines.append('# {}'.format(comment)) 213*9e94795aSAndroid Build Coastguard Worker 214*9e94795aSAndroid Build Coastguard Worker def AppendExtra(self, extra): 215*9e94795aSAndroid Build Coastguard Worker self.lines.append(extra) 216*9e94795aSAndroid Build Coastguard Worker 217*9e94795aSAndroid Build Coastguard Worker def __str__(self): 218*9e94795aSAndroid Build Coastguard Worker return '\n'.join(self.lines) 219*9e94795aSAndroid Build Coastguard Worker 220*9e94795aSAndroid Build Coastguard Worker 221*9e94795aSAndroid Build Coastguard Workerclass ReleaseToolsTestCase(unittest.TestCase): 222*9e94795aSAndroid Build Coastguard Worker """A common base class for all the releasetools unittests.""" 223*9e94795aSAndroid Build Coastguard Worker 224*9e94795aSAndroid Build Coastguard Worker def tearDown(self): 225*9e94795aSAndroid Build Coastguard Worker common.Cleanup() 226*9e94795aSAndroid Build Coastguard Worker 227*9e94795aSAndroid Build Coastguard Worker 228*9e94795aSAndroid Build Coastguard Workerclass PropertyFilesTestCase(ReleaseToolsTestCase): 229*9e94795aSAndroid Build Coastguard Worker 230*9e94795aSAndroid Build Coastguard Worker @staticmethod 231*9e94795aSAndroid Build Coastguard Worker def construct_zip_package(entries): 232*9e94795aSAndroid Build Coastguard Worker zip_file = common.MakeTempFile(suffix='.zip') 233*9e94795aSAndroid Build Coastguard Worker with zipfile.ZipFile(zip_file, 'w', allowZip64=True) as zip_fp: 234*9e94795aSAndroid Build Coastguard Worker for entry in entries: 235*9e94795aSAndroid Build Coastguard Worker zip_fp.writestr( 236*9e94795aSAndroid Build Coastguard Worker entry, 237*9e94795aSAndroid Build Coastguard Worker entry.replace('.', '-').upper(), 238*9e94795aSAndroid Build Coastguard Worker zipfile.ZIP_STORED) 239*9e94795aSAndroid Build Coastguard Worker return zip_file 240*9e94795aSAndroid Build Coastguard Worker 241*9e94795aSAndroid Build Coastguard Worker @staticmethod 242*9e94795aSAndroid Build Coastguard Worker def _parse_property_files_string(data): 243*9e94795aSAndroid Build Coastguard Worker result = {} 244*9e94795aSAndroid Build Coastguard Worker for token in data.split(','): 245*9e94795aSAndroid Build Coastguard Worker name, info = token.split(':', 1) 246*9e94795aSAndroid Build Coastguard Worker result[name] = info 247*9e94795aSAndroid Build Coastguard Worker return result 248*9e94795aSAndroid Build Coastguard Worker 249*9e94795aSAndroid Build Coastguard Worker def setUp(self): 250*9e94795aSAndroid Build Coastguard Worker common.OPTIONS.no_signing = False 251*9e94795aSAndroid Build Coastguard Worker 252*9e94795aSAndroid Build Coastguard Worker def _verify_entries(self, input_file, tokens, entries): 253*9e94795aSAndroid Build Coastguard Worker for entry in entries: 254*9e94795aSAndroid Build Coastguard Worker offset, size = map(int, tokens[entry].split(':')) 255*9e94795aSAndroid Build Coastguard Worker with open(input_file, 'rb') as input_fp: 256*9e94795aSAndroid Build Coastguard Worker input_fp.seek(offset) 257*9e94795aSAndroid Build Coastguard Worker if entry == 'metadata': 258*9e94795aSAndroid Build Coastguard Worker expected = b'META-INF/COM/ANDROID/METADATA' 259*9e94795aSAndroid Build Coastguard Worker elif entry == 'metadata.pb': 260*9e94795aSAndroid Build Coastguard Worker expected = b'META-INF/COM/ANDROID/METADATA-PB' 261*9e94795aSAndroid Build Coastguard Worker else: 262*9e94795aSAndroid Build Coastguard Worker expected = entry.replace('.', '-').upper().encode() 263*9e94795aSAndroid Build Coastguard Worker self.assertEqual(expected, input_fp.read(size)) 264*9e94795aSAndroid Build Coastguard Worker 265*9e94795aSAndroid Build Coastguard Worker 266*9e94795aSAndroid Build Coastguard Workerif __name__ == '__main__': 267*9e94795aSAndroid Build Coastguard Worker # We only want to run tests from the top level directory. Unfortunately the 268*9e94795aSAndroid Build Coastguard Worker # pattern option of unittest.discover, internally using fnmatch, doesn't 269*9e94795aSAndroid Build Coastguard Worker # provide a good API to filter the test files based on directory. So we do an 270*9e94795aSAndroid Build Coastguard Worker # os walk and load them manually. 271*9e94795aSAndroid Build Coastguard Worker test_modules = [] 272*9e94795aSAndroid Build Coastguard Worker base_path = os.path.dirname(os.path.realpath(__file__)) 273*9e94795aSAndroid Build Coastguard Worker test_dirs = [base_path] + [ 274*9e94795aSAndroid Build Coastguard Worker os.path.join(base_path, subdir) for subdir in ALLOWED_TEST_SUBDIRS 275*9e94795aSAndroid Build Coastguard Worker ] 276*9e94795aSAndroid Build Coastguard Worker for dirpath, _, files in os.walk(base_path): 277*9e94795aSAndroid Build Coastguard Worker for fn in files: 278*9e94795aSAndroid Build Coastguard Worker if dirpath in test_dirs and re.match('test_.*\\.py$', fn): 279*9e94795aSAndroid Build Coastguard Worker test_modules.append(fn[:-3]) 280*9e94795aSAndroid Build Coastguard Worker 281*9e94795aSAndroid Build Coastguard Worker test_suite = unittest.TestLoader().loadTestsFromNames(test_modules) 282*9e94795aSAndroid Build Coastguard Worker 283*9e94795aSAndroid Build Coastguard Worker # atest needs a verbosity level of >= 2 to correctly parse the result. 284*9e94795aSAndroid Build Coastguard Worker unittest.TextTestRunner(verbosity=2).run(test_suite) 285