c2606eb548
When building a system image with system_root_image enabled, the size computation should include files under both of in_dir (i.e. /system files) and root (pointed by 'root_dir'). Because files from both locations will end up into the built image. The files under root are usually only a few MiBs, but should be accounted for especially in the context of logical partitions (where the partition size will be allocated based on the actual need). Note that we will still need some "reserved space" (defined via BOARD_*_PARTITION_RESERVED_SIZE) to cover the cost for filesystem and/or verity metadata. This CL moves the combination of the two dirs up, before parsing and computing other properties. This doesn't affect anything for a successful image building path. It may however increase the time to error out in certain error path, since it copies the files earlier now. Test: python -m unittest test_build_image Test: `make dist` Test: Setup a target with PRODUCT_USE_LOGICAL_PARTITIONS == true and BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE == 20MiB. Build system image successfully. Test: Setup a target with PRODUCT_USE_LOGICAL_PARTITIONS == true and BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE == 20MiB. Write a large file to root dir (PRODUCT_OUT_ROOT). The image building fails, but reporting a size that accounts for both of /system and root. Change-Id: Idfb26b8e259626ba57ec3bd4f85d357c30e56163
190 lines
6.3 KiB
Python
190 lines
6.3 KiB
Python
#
|
|
# Copyright (C) 2017 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
import filecmp
|
|
import os.path
|
|
import unittest
|
|
|
|
import common
|
|
from build_image import CheckHeadroom, RunCommand, SetUpInDirAndFsConfig
|
|
|
|
|
|
class BuildImageTest(unittest.TestCase):
|
|
|
|
# Available: 1000 blocks.
|
|
EXT4FS_OUTPUT = (
|
|
"Created filesystem with 2777/129024 inodes and 515099/516099 blocks")
|
|
|
|
def tearDown(self):
|
|
common.Cleanup()
|
|
|
|
def test_CheckHeadroom_SizeUnderLimit(self):
|
|
# Required headroom: 1000 blocks.
|
|
prop_dict = {
|
|
'fs_type' : 'ext4',
|
|
'partition_headroom' : '4096000',
|
|
'mount_point' : 'system',
|
|
}
|
|
self.assertTrue(CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict))
|
|
|
|
def test_CheckHeadroom_InsufficientHeadroom(self):
|
|
# Required headroom: 1001 blocks.
|
|
prop_dict = {
|
|
'fs_type' : 'ext4',
|
|
'partition_headroom' : '4100096',
|
|
'mount_point' : 'system',
|
|
}
|
|
self.assertFalse(CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict))
|
|
|
|
def test_CheckHeadroom_WrongFsType(self):
|
|
prop_dict = {
|
|
'fs_type' : 'f2fs',
|
|
'partition_headroom' : '4100096',
|
|
'mount_point' : 'system',
|
|
}
|
|
self.assertRaises(
|
|
AssertionError, CheckHeadroom, self.EXT4FS_OUTPUT, prop_dict)
|
|
|
|
def test_CheckHeadroom_MissingProperties(self):
|
|
prop_dict = {
|
|
'fs_type' : 'ext4',
|
|
'partition_headroom' : '4100096',
|
|
}
|
|
self.assertRaises(
|
|
AssertionError, CheckHeadroom, self.EXT4FS_OUTPUT, prop_dict)
|
|
|
|
prop_dict = {
|
|
'fs_type' : 'ext4',
|
|
'mount_point' : 'system',
|
|
}
|
|
self.assertRaises(
|
|
AssertionError, CheckHeadroom, self.EXT4FS_OUTPUT, prop_dict)
|
|
|
|
def test_CheckHeadroom_WithMke2fsOutput(self):
|
|
"""Tests the result parsing from actual call to mke2fs."""
|
|
input_dir = common.MakeTempDir()
|
|
output_image = common.MakeTempFile(suffix='.img')
|
|
command = ['mkuserimg_mke2fs.sh', input_dir, output_image, 'ext4',
|
|
'/system', '409600', '-j', '0']
|
|
ext4fs_output, exit_code = RunCommand(command)
|
|
self.assertEqual(0, exit_code)
|
|
|
|
prop_dict = {
|
|
'fs_type' : 'ext4',
|
|
'partition_headroom' : '40960',
|
|
'mount_point' : 'system',
|
|
}
|
|
self.assertTrue(CheckHeadroom(ext4fs_output, prop_dict))
|
|
|
|
prop_dict = {
|
|
'fs_type' : 'ext4',
|
|
'partition_headroom' : '413696',
|
|
'mount_point' : 'system',
|
|
}
|
|
self.assertFalse(CheckHeadroom(ext4fs_output, prop_dict))
|
|
|
|
def test_SetUpInDirAndFsConfig_SystemRootImageFalse(self):
|
|
prop_dict = {
|
|
'fs_config': 'fs-config',
|
|
'mount_point': 'system',
|
|
}
|
|
in_dir, fs_config = SetUpInDirAndFsConfig('/path/to/in_dir', prop_dict)
|
|
self.assertEqual('/path/to/in_dir', in_dir)
|
|
self.assertEqual('fs-config', fs_config)
|
|
self.assertEqual('system', prop_dict['mount_point'])
|
|
|
|
def test_SetUpInDirAndFsConfig_SystemRootImageTrue_NonSystem(self):
|
|
prop_dict = {
|
|
'fs_config': 'fs-config',
|
|
'mount_point': 'vendor',
|
|
'system_root_image': 'true',
|
|
}
|
|
in_dir, fs_config = SetUpInDirAndFsConfig('/path/to/in_dir', prop_dict)
|
|
self.assertEqual('/path/to/in_dir', in_dir)
|
|
self.assertEqual('fs-config', fs_config)
|
|
self.assertEqual('vendor', prop_dict['mount_point'])
|
|
|
|
@staticmethod
|
|
def _gen_fs_config(partition):
|
|
fs_config = common.MakeTempFile(suffix='.txt')
|
|
with open(fs_config, 'w') as fs_config_fp:
|
|
fs_config_fp.write('fs-config-{}\n'.format(partition))
|
|
return fs_config
|
|
|
|
def test_SetUpInDirAndFsConfig_SystemRootImageTrue(self):
|
|
root_dir = common.MakeTempDir()
|
|
with open(os.path.join(root_dir, 'init'), 'w') as init_fp:
|
|
init_fp.write('init')
|
|
|
|
origin_in = common.MakeTempDir()
|
|
with open(os.path.join(origin_in, 'file'), 'w') as in_fp:
|
|
in_fp.write('system-file')
|
|
os.symlink('../etc', os.path.join(origin_in, 'symlink'))
|
|
|
|
fs_config_system = self._gen_fs_config('system')
|
|
|
|
prop_dict = {
|
|
'fs_config': fs_config_system,
|
|
'mount_point': 'system',
|
|
'root_dir': root_dir,
|
|
'system_root_image': 'true',
|
|
}
|
|
in_dir, fs_config = SetUpInDirAndFsConfig(origin_in, prop_dict)
|
|
|
|
self.assertTrue(filecmp.cmp(
|
|
os.path.join(in_dir, 'init'), os.path.join(root_dir, 'init')))
|
|
self.assertTrue(filecmp.cmp(
|
|
os.path.join(in_dir, 'system', 'file'),
|
|
os.path.join(origin_in, 'file')))
|
|
self.assertTrue(os.path.islink(os.path.join(in_dir, 'system', 'symlink')))
|
|
|
|
self.assertTrue(filecmp.cmp(fs_config_system, fs_config))
|
|
self.assertEqual('/', prop_dict['mount_point'])
|
|
|
|
def test_SetUpInDirAndFsConfig_SystemRootImageTrue_WithRootFsConfig(self):
|
|
root_dir = common.MakeTempDir()
|
|
with open(os.path.join(root_dir, 'init'), 'w') as init_fp:
|
|
init_fp.write('init')
|
|
|
|
origin_in = common.MakeTempDir()
|
|
with open(os.path.join(origin_in, 'file'), 'w') as in_fp:
|
|
in_fp.write('system-file')
|
|
os.symlink('../etc', os.path.join(origin_in, 'symlink'))
|
|
|
|
fs_config_system = self._gen_fs_config('system')
|
|
fs_config_root = self._gen_fs_config('root')
|
|
|
|
prop_dict = {
|
|
'fs_config': fs_config_system,
|
|
'mount_point': 'system',
|
|
'root_dir': root_dir,
|
|
'root_fs_config': fs_config_root,
|
|
'system_root_image': 'true',
|
|
}
|
|
in_dir, fs_config = SetUpInDirAndFsConfig(origin_in, prop_dict)
|
|
|
|
self.assertTrue(filecmp.cmp(
|
|
os.path.join(in_dir, 'init'), os.path.join(root_dir, 'init')))
|
|
self.assertTrue(filecmp.cmp(
|
|
os.path.join(in_dir, 'system', 'file'),
|
|
os.path.join(origin_in, 'file')))
|
|
self.assertTrue(os.path.islink(os.path.join(in_dir, 'system', 'symlink')))
|
|
|
|
with open(fs_config) as fs_config_fp:
|
|
fs_config_data = fs_config_fp.readlines()
|
|
self.assertIn('fs-config-system\n', fs_config_data)
|
|
self.assertIn('fs-config-root\n', fs_config_data)
|
|
self.assertEqual('/', prop_dict['mount_point'])
|