blob: a2a7f4c1de70dad56c0945d87eb82a93aef4dbd6 [file] [log] [blame]
#!/usr/bin/env python3.8
# Copyright 2020 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
r"""Unit test for verify_build.py.
Need to have SCRUTINY and ZBI environmental variables set.
To manually run this test:
SCRUTINY=~/fuchsia/out/default/host_x64/scrutiny \
ZBI=~/fuchsia/out/default/host_x64/zbi python3 \
verify_build_test.py
"""
import os
import subprocess
import sys
import tempfile
import unittest
import unittest.mock as mock
import verify_build
SUBPROCESS_RUN = subprocess.run
class RunVerifyZbiKernelCmdlineTest(unittest.TestCase):
def verify_kernel_cmdline(self, golden, actual):
with tempfile.TemporaryDirectory() as test_folder:
golden_file = os.path.join(test_folder, 'golden')
stamp_file = os.path.join(test_folder, 'stamp')
fuchsia_folder = os.path.join(test_folder, 'fuchsia')
test_zbi = os.path.join(test_folder, 'test.zbi')
cmdline_file = os.path.join(test_folder, 'cmdline')
scrutiny = os.environ['SCRUTINY']
with open(golden_file, 'w+') as f:
f.write(golden)
with open(cmdline_file, 'wb+') as f:
f.write(actual)
# Use ZBI to create a test.zbi that only contains cmdline.
subprocess.check_call(
[
os.environ['ZBI'], '-o', test_zbi, '-T', 'CMDLINE',
cmdline_file
])
os.mkdir(fuchsia_folder)
args = [
'--type', 'kernel_cmdline', '--zbi-file', test_zbi,
'--scrutiny', scrutiny, '--golden-files', golden_file,
'--stamp', stamp_file
]
# Verify the cmdline in the generated ZBI.
result = verify_build.main(args)
if result == 0:
# Verify stamp file is created.
self.assertTrue(os.path.isfile(stamp_file))
return result
def verify_bootfs_filelist(self, want_filelist, got_files):
with tempfile.TemporaryDirectory() as test_folder:
golden_file = os.path.join(test_folder, 'golden')
stamp_file = os.path.join(test_folder, 'stamp')
fuchsia_folder = os.path.join(test_folder, 'fuchsia')
os.mkdir(fuchsia_folder)
test_zbi = os.path.join(test_folder, 'test.zbi')
with open(golden_file, 'w+') as f:
f.write(want_filelist)
fake_scrutiny = os.path.join(test_folder, 'fake_scrutiny')
with open(fake_scrutiny, 'w+') as f:
f.write('fake scrutiny')
# Create a dummy test.zbi. We are not going to use the real scrutiny
# to parse it so its content doesn't matter.
with open(test_zbi, 'w+') as f:
f.write('test ZBI')
zbi_files = {}
for file in got_files:
zbi_files[os.path.join('bootfs', file)] = 'bootfs file'
fake_subprocess = FakeSubprocess(zbi_files, {})
with mock.patch('subprocess.run') as mock_run:
mock_run.side_effect = fake_subprocess.run
args = [
'--type', 'bootfs_filelist', '--zbi-file', test_zbi,
'--scrutiny', fake_scrutiny, '--golden-files', golden_file,
'--stamp', stamp_file
]
result = verify_build.main(args)
if result == 0:
# Verify stamp file is created.
self.assertTrue(os.path.isfile(stamp_file))
return result
def verify_static_pkgs(
self, want_pkgs, zbi_files, blobfs_files, system_image_files):
with tempfile.TemporaryDirectory() as test_folder:
golden_file = os.path.join(test_folder, 'golden')
stamp_file = os.path.join(test_folder, 'stamp')
depfile = os.path.join(test_folder, 'depfile')
fuchsia_folder = os.path.join(test_folder, 'fuchsia')
os.mkdir(fuchsia_folder)
test_zbi = os.path.join(test_folder, 'test.zbi')
test_blobfs = os.path.join(test_folder, 'test.blob')
with open(golden_file, 'w+') as f:
f.write(want_pkgs)
fake_scrutiny = os.path.join(test_folder, 'fake_scrutiny')
with open(fake_scrutiny, 'w+') as f:
f.write('fake scrutiny')
fake_far = os.path.join(test_folder, 'fake_far')
with open(fake_scrutiny, 'w+') as f:
f.write('fake far')
# Create a dummy test.zbi. We are not going to use the real scrutiny
# to parse it so its content doesn't matter.
with open(test_zbi, 'w+') as f:
f.write('test ZBI')
blobs_folder = os.path.join(test_folder, 'blobs')
os.mkdir(blobs_folder)
blobfs_manifest = os.path.join(blobs_folder, 'blobs.manifest')
with open(blobfs_manifest, 'w+') as bf:
for blobfs_file in blobfs_files:
# We use the blob merkle as the blob content file name here.
with open(os.path.join(blobs_folder, blobfs_file),
'w+') as f:
f.write(blobfs_files[blobfs_file])
bf.write(blobfs_file + '=' + blobfs_file + '\n')
fake_subprocess = FakeSubprocess(zbi_files, system_image_files)
with mock.patch('subprocess.run') as mock_run:
mock_run.side_effect = fake_subprocess.run
args = [
'--type',
'static_pkgs',
'--zbi-file',
test_zbi,
'--blobfs-manifest',
blobfs_manifest,
'--scrutiny',
fake_scrutiny,
'--far',
fake_far,
'--golden-files',
golden_file,
'--stamp',
stamp_file,
'--depfile',
depfile,
]
result = verify_build.main(args)
if result == 0:
# Verify stamp file is created.
self.assertTrue(os.path.isfile(stamp_file))
# Verify depfile is created.
self.assertTrue(os.path.isfile(depfile))
return result
def test_verify_kernel_cmdline_sucess_normal_case(self):
self.assertEqual(
0,
self.verify_kernel_cmdline(
'key1=v1\nkey2=v2\nkey3=v3', b'key1=v1 key2=v2 key3=v3'))
def test_verify_kernel_cmdline_success_order_diff(self):
self.assertEqual(
0,
self.verify_kernel_cmdline(
'key1=v1\nkey2=v2\nkey3=v3', b'key2=v2 key1=v1 key3=v3'))
def test_verify_kernel_cmdline_success_no_value_option(self):
self.assertEqual(
0,
self.verify_kernel_cmdline('option1\noption2', b'option1 option2'))
def test_verify_kernel_cmdline_fail_golden_empty(self):
self.assertEqual(
1, self.verify_kernel_cmdline('', b'key2=v2 key1=v1 key3=v3'))
def test_verify_kernel_cmdline_fail_missing_key2(self):
self.assertEqual(
1, self.verify_kernel_cmdline('key1=v1\nkey2=v2', b'key1=v1'))
def test_verify_kernel_cmdline_fail_key1_mismatch(self):
self.assertEqual(
1,
self.verify_kernel_cmdline('key1=v1\nkey2=v2', b'key1=v2 key2=v2'))
def test_verify_kernel_cmdline_fail_key2_mismatch(self):
self.assertEqual(
1,
self.verify_kernel_cmdline('key1=v1\nkey2=v2', b'key1=v1 key2=v1'))
def test_verify_kernel_cmdline_fail_additional_key3(self):
self.assertEqual(
1,
self.verify_kernel_cmdline(
'key1=v1\nkey2=v2', b'key1=v1 key2=v2 key3=v3'))
def test_verify_kernel_cmdline_fail_invalid_format(self):
self.assertEqual(
1,
self.verify_kernel_cmdline('key1=v1\nkey2=v2', b'invalid=format=1'))
def test_verify_kernel_cmdline_fail_option1_missing(self):
self.assertEqual(
1, self.verify_kernel_cmdline('option1\noption2', b'option2'))
def test_verify_kernel_cmdline_fail_additional_option3(self):
self.assertEqual(
1,
self.verify_kernel_cmdline(
'option1\noption2', b'option1 option2 option3'))
def test_verify_kernel_cmdline_zbi_not_found(self):
with tempfile.TemporaryDirectory() as test_folder:
golden_file = os.path.join(test_folder, 'golden')
stamp_file = os.path.join(test_folder, 'stamp')
fuchsia_folder = os.path.join(test_folder, 'fuchsia')
test_zbi = os.path.join(test_folder, 'test.zbi')
scrutiny = os.environ['SCRUTINY']
with open(golden_file, 'w+') as f:
f.write('option1')
# Do not create test_zbi
os.mkdir(fuchsia_folder)
args = [
'--type', 'kernel_cmdline', '--zbi-file', test_zbi,
'--scrutiny', scrutiny, '--golden-files', golden_file,
'--stamp', stamp_file
]
self.assertEqual(1, verify_build.main(args))
def test_verify_kernel_cmdline_success_no_cmdline_found(self):
with tempfile.TemporaryDirectory() as test_folder:
golden_file = os.path.join(test_folder, 'golden')
stamp_file = os.path.join(test_folder, 'stamp')
fuchsia_folder = os.path.join(test_folder, 'fuchsia')
test_zbi = os.path.join(test_folder, 'test.zbi')
scrutiny = os.environ['SCRUTINY']
# Create an empty golden file
with open(golden_file, 'w+') as f:
f.write('')
# Use ZBI to create a test.zbi with no cmdline.
subprocess.check_call([os.environ['ZBI'], '-o', test_zbi])
os.mkdir(fuchsia_folder)
args = [
'--type', 'kernel_cmdline', '--zbi-file', test_zbi,
'--scrutiny', scrutiny, '--golden-files', golden_file,
'--stamp', stamp_file
]
self.assertEqual(0, verify_build.main(args))
def test_verify_kernel_cmdline_fail_golden_empty_cmdline_found(self):
self.assertEqual(1, self.verify_kernel_cmdline('', b'option2'))
def test_verify_kernel_cmdline_fail_golden_not_empty_cmdline_not_found(
self):
with tempfile.TemporaryDirectory() as test_folder:
golden_file = os.path.join(test_folder, 'golden')
stamp_file = os.path.join(test_folder, 'stamp')
fuchsia_folder = os.path.join(test_folder, 'fuchsia')
test_zbi = os.path.join(test_folder, 'test.zbi')
scrutiny = os.environ['SCRUTINY']
# Create an empty golden file
with open(golden_file, 'w+') as f:
f.write('option1')
# Use ZBI to create a test.zbi with no cmdline.
subprocess.check_call([os.environ['ZBI'], '-o', test_zbi])
os.mkdir(fuchsia_folder)
args = [
'--type', 'kernel_cmdline', '--zbi-file', test_zbi,
'--scrutiny', scrutiny, '--golden-files', golden_file,
'--stamp', stamp_file
]
self.assertEqual(1, verify_build.main(args))
def test_verify_kernel_cmdline_multiple_golden_files_one_match(self):
with tempfile.TemporaryDirectory() as test_folder:
golden_file_1 = os.path.join(test_folder, 'golden_1')
golden_file_2 = os.path.join(test_folder, 'golden_2')
stamp_file = os.path.join(test_folder, 'stamp')
fuchsia_folder = os.path.join(test_folder, 'fuchsia')
test_zbi = os.path.join(test_folder, 'test.zbi')
scrutiny = os.environ['SCRUTINY']
cmdline_file = os.path.join(test_folder, 'cmdline')
# golden_file_1 does not match.
with open(golden_file_1, 'w+') as f:
f.write('option1')
# golden_file_2 matches.
with open(golden_file_2, 'w+') as f:
f.write('option1 option2')
with open(cmdline_file, 'wb+') as f:
f.write(b'option1 option2')
# Use ZBI to create a test.zbi that only contains cmdline.
subprocess.check_call(
[
os.environ['ZBI'], '-o', test_zbi, '-T', 'CMDLINE',
cmdline_file
])
os.mkdir(fuchsia_folder)
args = [
'--type', 'kernel_cmdline', '--zbi-file', test_zbi,
'--scrutiny', scrutiny, '--golden-files', golden_file_1,
golden_file_2, '--stamp', stamp_file
]
self.assertEqual(1, verify_build.main(args))
def test_verify_kernel_cmdline_three_golden_files_not_supported(self):
with tempfile.TemporaryDirectory() as test_folder:
golden_file_1 = os.path.join(test_folder, 'golden_1')
golden_file_2 = os.path.join(test_folder, 'golden_2')
golden_file_3 = os.path.join(test_folder, 'golden_3')
stamp_file = os.path.join(test_folder, 'stamp')
fuchsia_folder = os.path.join(test_folder, 'fuchsia')
test_zbi = os.path.join(test_folder, 'test.zbi')
scrutiny = os.environ['SCRUTINY']
cmdline_file = os.path.join(test_folder, 'cmdline')
with open(golden_file_1, 'w+') as f:
f.write('option1')
with open(golden_file_2, 'w+') as f:
f.write('option1')
with open(golden_file_3, 'w+') as f:
f.write('option1')
with open(cmdline_file, 'wb+') as f:
f.write(b'option1')
# Use ZBI to create a test.zbi that only contains cmdline.
subprocess.check_call(
[
os.environ['ZBI'], '-o', test_zbi, '-T', 'CMDLINE',
cmdline_file
])
os.mkdir(fuchsia_folder)
args = [
'--type', 'kernel_cmdline', '--zbi-file', test_zbi,
'--scrutiny', scrutiny, '--golden-files', golden_file_1,
golden_file_2, golden_file_3, '--stamp', stamp_file
]
# We do not support more than two golden files.
self.assertEqual(0, verify_build.main(args))
def test_verify_bootfs_filelist_normal_case(self):
self.assertEqual(
0,
self.verify_bootfs_filelist(
'fileA\nfileB\nfileC', ['fileA', 'fileC', 'fileB']))
def test_verify_bootfs_filelist_sub_dir(self):
self.assertEqual(
0,
self.verify_bootfs_filelist(
'dir/fileA\ndir/fileC\nfileB',
['dir/fileA', 'dir/fileC', 'fileB']))
def test_verify_bootfs_filelist_mismatch(self):
self.assertEqual(
1,
self.verify_bootfs_filelist(
'fileA\nfileB\nfileC', ['fileA', 'fileC']))
def test_verify_bootfs_filelist_sub_dir_mismatch(self):
self.assertEqual(
1,
self.verify_bootfs_filelist(
'dir/fileA\ndir/fileC\nfileB',
['dir1/fileA', 'dir/fileC', 'fileB']))
def test_verify_static_pkgs_normal_case(self):
static_packages = 'pkg0/0=1\npkg1/0=1\npkg2/0=2'
zbi_files = {
'bootfs/config/devmgr': 'zircon.system.pkgfs.cmd=bin/pkgsvr+1234'
}
blobfs_files = {'1234': 'system_image', '2345': static_packages}
system_image_files = {'meta/contents': 'data/static_packages=2345'}
self.assertEqual(
0,
self.verify_static_pkgs(
'pkg0\npkg1\npkg2', zbi_files, blobfs_files,
system_image_files))
def test_verify_static_pkgs_order(self):
static_packages = 'pkg2/2=1\npkg1/1=1\npkg0/0=2'
zbi_files = {
'bootfs/config/devmgr': 'zircon.system.pkgfs.cmd=bin/pkgsvr+1234'
}
blobfs_files = {'1234': 'system_image', '2345': static_packages}
system_image_files = {'meta/contents': 'data/static_packages=2345'}
self.assertEqual(
0,
self.verify_static_pkgs(
'pkg0\npkg1\npkg2', zbi_files, blobfs_files,
system_image_files))
def test_verify_static_pkgs_mismatch(self):
static_packages = 'pkg0/0=1\npkg1/0=1'
zbi_files = {
'bootfs/config/devmgr': 'zircon.system.pkgfs.cmd=bin/pkgsvr+1234'
}
blobfs_files = {'1234': 'system_image', '2345': static_packages}
system_image_files = {'meta/contents': 'data/static_packages=2345'}
self.assertEqual(
1,
self.verify_static_pkgs(
'pkg0\npkg1\npkg2', zbi_files, blobfs_files,
system_image_files))
def test_verify_static_pkgs_no_devmgr_config(self):
static_packages = 'pkg0/0=1\npkg1/0=1\npkg2/0=2'
zbi_files = {}
blobfs_files = {'1234': 'system_image', '2345': static_packages}
system_image_files = {'meta/contents': 'data/static_packages=2345'}
self.assertEqual(
1,
self.verify_static_pkgs(
'pkg0\npkg1\npkg2', zbi_files, blobfs_files,
system_image_files))
def test_verify_static_pkgs_invalid_devmgr_config(self):
static_packages = 'pkg0/0=1\npkg1/0=1\npkg2/0=2'
zbi_files = {'bootfs/config/devmgr': 'zircon.system.pkgfs.cmd'}
blobfs_files = {'1234': 'system_image', '2345': static_packages}
system_image_files = {'meta/contents': 'data/static_packages=2345'}
self.assertEqual(
1,
self.verify_static_pkgs(
'pkg0\npkg1\npkg2', zbi_files, blobfs_files,
system_image_files))
def test_verify_static_pkgs_system_image_blob_not_found(self):
static_packages = 'pkg0/0=1\npkg1/0=1\npkg2/0=2'
zbi_files = {
'bootfs/config/devmgr': 'zircon.system.pkgfs.cmd=bin/pkgsvr+1234'
}
blobfs_files = {'2345': static_packages}
system_image_files = {'meta/contents': 'data/static_packages=2345'}
self.assertEqual(
1,
self.verify_static_pkgs(
'pkg0\npkg1\npkg2', zbi_files, blobfs_files,
system_image_files))
def test_verify_static_pkgs_invalid_system_image(self):
static_packages = 'pkg0/0=1\npkg1/0=1\npkg2/0=2'
zbi_files = {
'bootfs/config/devmgr': 'zircon.system.pkgfs.cmd=bin/pkgsvr+1234'
}
blobfs_files = {'1234': 'system_image', '2345': static_packages}
system_image_files = {}
self.assertEqual(
1,
self.verify_static_pkgs(
'pkg0\npkg1\npkg2', zbi_files, blobfs_files,
system_image_files))
def test_verify_static_pkgs_static_pkgs_blob_not_found(self):
zbi_files = {
'bootfs/config/devmgr': 'zircon.system.pkgfs.cmd=bin/pkgsvr+1234'
}
blobfs_files = {'1234': 'system_image'}
system_image_files = {'meta/contents': 'data/static_packages=2345'}
self.assertEqual(
1,
self.verify_static_pkgs(
'pkg0\npkg1\npkg2', zbi_files, blobfs_files,
system_image_files))
def test_verify_static_pkgs_invalid_static_pkgs_list(self):
static_packages = 'pkg0/0'
zbi_files = {
'bootfs/config/devmgr': 'zircon.system.pkgfs.cmd=bin/pkgsvr+1234'
}
blobfs_files = {'1234': 'system_image', '2345': static_packages}
system_image_files = {'meta/contents': 'data/static_packages=2345'}
self.assertEqual(
1,
self.verify_static_pkgs(
'pkg0\npkg1\npkg2', zbi_files, blobfs_files,
system_image_files))
class FakeSubprocess(object):
def __init__(self, zbi_files, system_image_files):
self.zbi_files = zbi_files
self.system_image_files = system_image_files
def _write_files(files, output):
for file in files:
dirpath = os.path.dirname(os.path.join(output, file))
if not os.path.exists(dirpath):
os.makedirs(dirpath, exist_ok=True)
with open(os.path.join(output, file), 'w+') as f:
f.write(files[file])
def run(self, *argv, **kwargs):
del kwargs
command = argv[0]
if command[0].endswith('fake_scrutiny'):
output = ''
input = ''
scrutiny_commands = command[2].split(' ')
for i in range(0, len(scrutiny_commands) - 1):
if scrutiny_commands[i] == '--output':
output = scrutiny_commands[i + 1]
if scrutiny_commands[i] == '--input':
input = scrutiny_commands[i + 1]
if not os.path.exists(input):
raise subprocess.CalledProcessError(
cmd=command,
returncode=1,
stderr=('input: ' + input + ' not found').encode())
op = scrutiny_commands[0]
if op == 'tool.zbi.extract':
FakeSubprocess._write_files(self.zbi_files, output)
else:
raise subprocess.CalledProcessError(
cmd=command,
returncode=1,
stderr=('unknown scrutiny command: ' + op).encode())
return subprocess.CompletedProcess(
args=[], returncode=0, stdout=b'{"status":"ok"}')
elif command[0].endswith('fake_far'):
input = (command[2].split('='))[1]
if not os.path.exists(input):
raise subprocess.CalledProcessError(
cmd=command,
returncode=1,
stderr=('input: ' + input + ' not found').encode())
output = (command[3].split('='))[1]
os.mkdir(output)
FakeSubprocess._write_files(self.system_image_files, output)
return subprocess.CompletedProcess(
args=[], returncode=0, stdout=b'')
raise subprocess.CalledProcessError(
cmd=command, returncode=1, stderr=b'unsupported command')
if __name__ == '__main__':
if 'SCRUTINY' not in os.environ or 'ZBI' not in os.environ:
print('Please set SCRUTINY and ZBI environmental path')
sys.exit(1)
unittest.main()