| #!/usr/bin/env python3.8 |
| # Copyright 2021 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. |
| """Unit tests for the distribution_manifest.py Python module.""" |
| |
| import json |
| import os |
| import tempfile |
| import unittest |
| |
| # From current directory |
| import distribution_manifest as dm |
| |
| Entry = dm.Entry |
| |
| |
| class TestExpandManifestItems(unittest.TestCase): |
| |
| def test_regular_entries(self): |
| input = [ |
| { |
| 'source': 'some/file', |
| 'destination': 'bin/foo', |
| 'label': '//src/foo', |
| }, |
| { |
| 'source': 'other/stuff', |
| 'destination': 'bin/bar', |
| 'label': '//src/bar', |
| }, |
| ] |
| |
| opened_files = set() |
| result = dm.expand_manifest_items(input, opened_files) |
| |
| expected = [ |
| Entry(destination='bin/foo', source='some/file', label='//src/foo'), |
| Entry( |
| destination='bin/bar', source='other/stuff', label='//src/bar'), |
| ] |
| |
| self.assertListEqual(result, expected) |
| self.assertEqual(opened_files, set()) |
| |
| def test_regular_entries_with_elf_runtime_dir(self): |
| input = [ |
| { |
| 'source': 'some/file', |
| 'destination': 'bin/foo', |
| 'label': '//src/foo', |
| 'elf_runtime_dir': 'lib', |
| }, |
| { |
| 'source': 'other/stuff', |
| 'destination': 'bin/bar', |
| 'label': '//src/bar', |
| 'elf_runtime_dir': 'lib/asan', |
| }, |
| { |
| 'source': 'other/stuff2', |
| 'destination': 'bin/tool', |
| 'label': '//src/tool', |
| }, |
| ] |
| |
| opened_files = set() |
| result = dm.expand_partial_manifest_items(input, opened_files) |
| |
| # Here the elf_runtime_dir should be ignored / removed from the output |
| expected = [ |
| Entry(destination='bin/foo', source='some/file', label='//src/foo'), |
| Entry( |
| destination='bin/bar', source='other/stuff', label='//src/bar'), |
| Entry( |
| destination='bin/tool', |
| source='other/stuff2', |
| label='//src/tool'), |
| ] |
| |
| self.assertListEqual(result.entries, expected) |
| |
| expected_elf_runtime_map = { |
| 'bin/foo': 'lib', |
| 'bin/bar': 'lib/asan', |
| } |
| |
| self.assertFalse(result.errors) |
| self.assertDictEqual(result.elf_runtime_map, expected_elf_runtime_map) |
| self.assertEqual(opened_files, set()) |
| |
| def test_regular_entries_with_elf_runtime_dir_conflicts(self): |
| input = [ |
| { |
| 'source': 'some/file', |
| 'destination': 'bin/foo', |
| 'label': '//src/foo', |
| 'elf_runtime_dir': 'lib', |
| }, |
| { |
| 'source': 'some/alt/file', |
| 'destination': 'bin/foo', |
| 'label': '//src/foo', |
| 'elf_runtime_dir': 'lib/alt', |
| }, |
| ] |
| |
| opened_files = set() |
| result = dm.expand_partial_manifest_items(input, opened_files) |
| |
| expected_errors = [ |
| 'ERROR: Entries with same destination path have different ELF runtime dir:', |
| ' - destination=bin/foo source=some/file label=//src/foo elf_runtime_dir=lib', |
| ' - destination=bin/foo source=some/alt/file label=//src/foo elf_runtime_dir=lib/alt', |
| ] |
| self.assertEqual(opened_files, set()) |
| self.assertListEqual(result.errors, expected_errors) |
| |
| def test_default_label(self): |
| input = [ |
| { |
| 'source': 'some/file', |
| 'destination': 'bin/foo', |
| }, |
| { |
| 'source': 'other/stuff', |
| 'destination': 'bin/bar', |
| 'label': '//src/bar', |
| }, |
| ] |
| |
| opened_files = set() |
| result = dm.expand_manifest_items( |
| input, opened_files, default_label='//default') |
| |
| expected = [ |
| Entry(source='some/file', destination='bin/foo', label='//default'), |
| Entry( |
| source='other/stuff', destination='bin/bar', label='//src/bar'), |
| ] |
| |
| self.assertListEqual(result, expected) |
| self.assertEqual(opened_files, set()) |
| |
| def test_file_entries(self): |
| with tempfile.TemporaryDirectory() as tmpdirname: |
| inner_file1 = os.path.join(tmpdirname, '1.dist') |
| inner1 = [ |
| { |
| 'source': 'some/file', |
| 'destination': 'bin/foo', |
| }, |
| ] |
| with open(inner_file1, 'w') as f: |
| json.dump(inner1, f) |
| |
| inner_file2 = os.path.join(tmpdirname, '2.dist') |
| inner2 = [ |
| { |
| 'file': inner_file1, |
| 'label': '//inner2', |
| }, |
| ] |
| with open(inner_file2, 'w') as f: |
| json.dump(inner2, f) |
| |
| input = [ |
| { |
| 'file': inner_file2, |
| 'label': '//default', |
| }, |
| ] |
| |
| opened_files = set() |
| result = dm.expand_manifest_items(input, opened_files, '//other') |
| |
| expected = [ |
| Entry( |
| source='some/file', destination='bin/foo', |
| label='//inner2'), |
| ] |
| |
| self.assertListEqual(result, expected) |
| self.assertSetEqual(opened_files, {inner_file1, inner_file2}) |
| |
| def test_renamed_entries(self): |
| input = [ |
| { |
| 'destination': 'bin/foo', |
| 'source': 'some/file', |
| 'label': '//src/foo', |
| }, |
| { |
| 'destination': 'bin/bar', |
| 'renamed_source': 'some/file', |
| }, |
| { |
| 'destination': 'bin/zoo', |
| 'renamed_source': 'some/file', |
| 'label': '//other', |
| }, |
| ] |
| opened_files = set() |
| result, error = dm.expand_manifest(input, opened_files) |
| |
| expected = [ |
| Entry(destination='bin/bar', source='some/file', label='//src/foo'), |
| Entry(destination='bin/zoo', source='some/file', label='//other'), |
| ] |
| self.assertListEqual(result, expected) |
| self.assertEqual(opened_files, set()) |
| self.assertFalse(error) |
| |
| def test_renamed_entries_with_persistent_entry(self): |
| input = [ |
| { |
| 'destination': 'bin/foo', |
| 'source': 'some/file', |
| 'label': '//src/foo', |
| }, |
| { |
| 'destination': 'bin/bar', |
| 'renamed_source': 'some/file', |
| 'label': '//src/bar', |
| 'keep_original': True, |
| }, |
| ] |
| opened_files = set() |
| result, error = dm.expand_manifest(input, opened_files) |
| |
| expected = [ |
| Entry(destination='bin/bar', source='some/file', label='//src/bar'), |
| Entry(destination='bin/foo', source='some/file', label='//src/foo'), |
| ] |
| self.assertListEqual(result, expected) |
| self.assertEqual(opened_files, set()) |
| self.assertFalse(error) |
| |
| def test_renamed_entries_with_errors(self): |
| input = [ |
| { |
| 'destination': 'bin/foo', |
| 'source': 'some/foo', |
| 'label': '//src/foo', |
| }, |
| { |
| 'destination': 'bin/bar', |
| 'renamed_source': 'something/missing', |
| }, |
| { |
| 'destination': 'bin/zoo', |
| 'renamed_source': 'some/foo', |
| 'label': '//other', |
| }, |
| { |
| 'destination': 'bin/tool', |
| 'renamed_source': 'some/foo2', |
| }, |
| ] |
| opened_files = set() |
| result = dm.expand_partial_manifest_items(input, opened_files) |
| |
| expected_errors = [] |
| expected_errors += [ |
| 'ERROR: Renamed distribution entries have unknown source destination:' |
| ] |
| expected_errors += [ |
| ' - {"destination": "bin/bar", "renamed_source": "something/missing"}' |
| ] |
| expected_errors += [ |
| ' - {"destination": "bin/tool", "renamed_source": "some/foo2"}' |
| ] |
| self.assertEqual(result.errors, expected_errors) |
| self.assertEqual(opened_files, set()) |
| |
| def test_renamed_entries_with_resource_errors(self): |
| input = [ |
| { |
| 'destination': 'bin/foo', |
| 'source': 'some/foo', |
| 'label': '//src/foo', |
| }, |
| { |
| 'destination': 'bin/bar', |
| 'source': 'some/foo', |
| 'label': '//src/bar' |
| }, |
| { |
| 'destination': 'bin/zoo', |
| 'renamed_source': 'some/foo', |
| 'label': '//other', |
| }, |
| ] |
| opened_files = set() |
| result = dm.expand_partial_manifest_items(input, opened_files) |
| |
| expected_errors = [] |
| expected_errors += [ |
| 'ERROR: Multiple regular entries with the same source path:', |
| ' - destination=bin/bar source=some/foo label=//src/bar', |
| ' - destination=bin/foo source=some/foo label=//src/foo', |
| ] |
| expected_errors.append( |
| '\nThis generally means a mix of renamed_binary() and resource() targets\n' |
| + |
| 'that reference the same source. Try replacing the resource() targets by\n' |
| + 'renamed_binary() ones to fix the problem\n') |
| |
| self.assertEqual(result.errors, expected_errors) |
| self.assertEqual(opened_files, set()) |
| |
| def test_renamed_entries_with_copy(self): |
| input = [ |
| { |
| 'destination': 'bin/foo', |
| 'source': 'some-variant/foo', |
| 'label': '//src/foo(variant)', |
| }, |
| { |
| 'copy_from': 'some-variant/foo', |
| 'copy_to': 'foo', |
| 'label': '//src/foo', |
| }, |
| { |
| 'destination': 'bin/foo_renamed', |
| 'renamed_source': 'foo', |
| }, |
| ] |
| opened_files = set() |
| result, errors = dm.expand_manifest(input, opened_files) |
| |
| expected = [ |
| Entry( |
| destination='bin/foo_renamed', |
| source='some-variant/foo', |
| label='//src/foo(variant)') |
| ] |
| self.assertListEqual(result, expected) |
| self.assertEqual(opened_files, set()) |
| self.assertFalse(errors) |
| |
| |
| class TestExpandManifest(unittest.TestCase): |
| |
| def test_simple_case_no_conflicts(self): |
| input = [ |
| { |
| 'source': 'some/file', |
| 'destination': 'bin/foo', |
| 'label': '//src/foo', |
| }, |
| { |
| 'source': 'other/stuff', |
| 'destination': 'bin/bar', |
| 'label': '//src/bar', |
| }, |
| ] |
| opened_files = set() |
| result, error = dm.expand_manifest(input, opened_files) |
| |
| expected = [ |
| Entry( |
| destination='bin/bar', source='other/stuff', label='//src/bar'), |
| Entry(destination='bin/foo', source='some/file', label='//src/foo'), |
| ] |
| |
| self.assertListEqual(result, expected) |
| self.assertEqual(opened_files, set()) |
| self.assertFalse(error) |
| |
| def test_duplicates_same_path(self): |
| input = [ |
| { |
| 'source': 'some/file', |
| 'destination': 'bin/foo', |
| 'label': '//src/foo', |
| }, |
| { |
| 'source': 'other/stuff', |
| 'destination': 'bin/bar', |
| 'label': '//src/bar', |
| }, |
| { |
| 'source': 'some/file', |
| 'destination': 'bin/foo', |
| 'label': '//src/foo', |
| }, |
| ] |
| opened_files = set() |
| result, error = dm.expand_manifest(input, opened_files) |
| |
| expected = [ |
| Entry( |
| destination='bin/bar', source='other/stuff', label='//src/bar'), |
| Entry(destination='bin/foo', source='some/file', label='//src/foo'), |
| ] |
| |
| self.assertListEqual(result, expected) |
| self.assertEqual(opened_files, set()) |
| self.assertFalse(error) |
| |
| def test_duplicates_same_content(self): |
| with tempfile.TemporaryDirectory() as tmpdirname: |
| # Create two different files with the same content. |
| content = 'Some test data' |
| file1_path = os.path.join(tmpdirname, 'file1') |
| file2_path = os.path.join(tmpdirname, 'file2') |
| with open(file1_path, 'w') as f: |
| f.write(content) |
| with open(file2_path, 'w') as f: |
| f.write(content) |
| |
| input = [ |
| { |
| 'source': file1_path, |
| 'destination': 'bin/foo', |
| 'label': '//src/foo', |
| }, |
| { |
| 'source': 'other/stuff', |
| 'destination': 'bin/bar', |
| 'label': '//src/bar', |
| }, |
| { |
| 'source': file2_path, |
| 'destination': 'bin/foo', |
| 'label': '//src/foo', |
| }, |
| ] |
| opened_files = set() |
| result, error = dm.expand_manifest(input, opened_files) |
| |
| expected = [ |
| Entry( |
| destination='bin/bar', |
| source='other/stuff', |
| label='//src/bar'), |
| Entry( |
| destination='bin/foo', source=file1_path, |
| label='//src/foo'), |
| ] |
| |
| self.assertListEqual(result, expected) |
| self.assertEqual(opened_files, {file1_path, file2_path}) |
| self.assertFalse(error) |
| |
| def test_duplicates_source_conflict(self): |
| with tempfile.TemporaryDirectory() as tmpdirname: |
| # Create two different files with different content. |
| content = 'Some test data' |
| file1_path = os.path.join(tmpdirname, 'file1') |
| file2_path = os.path.join(tmpdirname, 'file2') |
| with open(file1_path, 'w') as f: |
| f.write(content) |
| with open(file2_path, 'w') as f: |
| f.write(content + '!') |
| |
| input = [ |
| { |
| 'source': file1_path, |
| 'destination': 'bin/foo', |
| 'label': '//src/foo1', |
| }, |
| { |
| 'source': 'other/stuff', |
| 'destination': 'bin/bar', |
| 'label': '//src/bar', |
| }, |
| { |
| 'source': file2_path, |
| 'destination': 'bin/foo', |
| 'label': '//src/foo2', |
| }, |
| ] |
| opened_files = set() |
| result, error = dm.expand_manifest(input, opened_files) |
| |
| expected = [ |
| Entry( |
| destination='bin/bar', |
| source='other/stuff', |
| label='//src/bar'), |
| Entry( |
| destination='bin/foo', |
| source=file1_path, |
| label='//src/foo1'), |
| ] |
| |
| self.assertListEqual(result, expected) |
| self.assertEqual(opened_files, {file1_path, file2_path}) |
| expected_error = 'ERROR: Conflicting distribution entries!\n' |
| expected_error += ' Conflicting source paths for destination path: bin/foo\n' |
| expected_error += ' - source=%s label=//src/foo1\n' % file1_path |
| expected_error += ' - source=%s label=//src/foo2\n' % file2_path |
| |
| self.assertEqual(error, expected_error) |
| |
| |
| class TestVerifyElfDependencies(unittest.TestCase): |
| |
| def test_simple_dependencies(self): |
| binary = 'bin/foo' |
| binary_deps = ['libbar.so', 'libc.so'] |
| lib_dir = 'LIB' |
| |
| def get_deps(lib_path): |
| _DEPS_MAP = { |
| 'LIB/ld.so.1': [], |
| 'LIB/libbar.so': ['libzoo.so'], |
| 'LIB/libzoo.so': ['libc.so'], |
| } |
| return _DEPS_MAP.get(lib_path, None) |
| |
| visited_libs = set() |
| error = dm.verify_elf_dependencies( |
| binary, lib_dir, binary_deps, get_deps, visited_libs) |
| self.assertFalse(error) |
| self.assertSetEqual( |
| visited_libs, {'LIB/libbar.so', 'LIB/libzoo.so', 'LIB/ld.so.1'}) |
| |
| def test_circular_dependencies(self): |
| binary = 'bin/foo' |
| binary_deps = ['libbar.so', 'libc.so'] |
| lib_dir = 'LIB' |
| |
| def get_deps(lib_path): |
| _DEPS_MAP = { |
| 'LIB/ld.so.1': [], |
| 'LIB/libbar.so': ['libzoo.so'], |
| 'LIB/libzoo.so': ['libbar.so', 'libc.so'], |
| } |
| return _DEPS_MAP.get(lib_path, None) |
| |
| visited_libs = set() |
| error = dm.verify_elf_dependencies( |
| binary, lib_dir, binary_deps, get_deps, visited_libs) |
| self.assertFalse(error) |
| self.assertSetEqual( |
| visited_libs, {'LIB/libbar.so', 'LIB/libzoo.so', 'LIB/ld.so.1'}) |
| |
| def test_missing_dependencies(self): |
| binary = 'bin/foo' |
| binary_deps = ['libbar.so', 'libc.so'] |
| lib_dir = 'LIB' |
| |
| def get_deps(lib_path): |
| _DEPS_MAP = { |
| 'LIB/ld.so.1': [], |
| 'LIB/libbar.so': ['libzoo.so'], |
| 'LIB/libzoo.so': ['libmissing.so', 'libc.so'], |
| } |
| return _DEPS_MAP.get(lib_path, None) |
| |
| visited_libs = set() |
| error = dm.verify_elf_dependencies( |
| binary, lib_dir, binary_deps, get_deps, visited_libs) |
| |
| expected_error = [ |
| '%s missing dependency %s/libmissing.so' % (binary, lib_dir) |
| ] |
| self.assertListEqual(error, expected_error) |
| self.assertSetEqual( |
| visited_libs, {'LIB/libbar.so', 'LIB/libzoo.so', 'LIB/ld.so.1'}) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |