| # Copyright 2021 The Pigweed Authors |
| # |
| # 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 |
| # |
| # https://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. |
| """Unit tests for generate_cc_blob_library.py""" |
| |
| from pathlib import Path |
| import tempfile |
| import unittest |
| |
| from pw_build import generate_cc_blob_library |
| |
| COMMENT = """\ |
| // This file was generated by generate_cc_blob_library.py. |
| // |
| // DO NOT EDIT! |
| // |
| // This file contains declarations for byte arrays created from files during the |
| // build. The byte arrays are constant initialized and are safe to access at any |
| // time, including before main(). |
| // |
| // See https://pigweed.dev/pw_build/#pw-cc-blob-library for details. |
| """ |
| |
| COMMON_HEADER_START = COMMENT + """\ |
| #pragma once |
| |
| #include <array> |
| #include <cstddef> |
| """ |
| |
| COMMON_SOURCE_START = COMMENT + """\ |
| |
| #include "header.h" |
| |
| #include <array> |
| #include <cstddef> |
| |
| #include "pw_preprocessor/compiler.h" |
| """ |
| |
| FOO_BLOB = """\ |
| constexpr std::array<std::byte, 2> fooBlob = { |
| std::byte{0x01}, std::byte{0x02}, |
| }; |
| """ |
| |
| BAR_BLOB = """\ |
| constexpr std::array<std::byte, 10> barBlob = { |
| std::byte{0x01}, std::byte{0x02}, std::byte{0x03}, std::byte{0x04}, |
| std::byte{0x05}, std::byte{0x06}, std::byte{0x07}, std::byte{0x08}, |
| std::byte{0x09}, std::byte{0x0A}, |
| }; |
| """ |
| |
| |
| class TestSplitIntoChunks(unittest.TestCase): |
| """Unit tests for the split_into_chunks() function.""" |
| def test_perfect_split(self): |
| """Tests basic splitting where the iterable divides perfectly.""" |
| data = (1, 7, 0, 1) |
| self.assertEqual( |
| ((1, 7), (0, 1)), |
| tuple(generate_cc_blob_library.split_into_chunks(data, 2))) |
| |
| def test_split_with_remainder(self): |
| """Tests basic splitting where there is a remainder.""" |
| data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) |
| self.assertEqual( |
| ((1, 2, 3), (4, 5, 6), (7, 8, 9), (10, )), |
| tuple(generate_cc_blob_library.split_into_chunks(data, 3))) |
| |
| |
| class TestHeaderFromBlobs(unittest.TestCase): |
| """Unit tests for the header_from_blobs() function.""" |
| def test_single_blob_header(self): |
| """Tests the generation of a header for a single blob.""" |
| foo_blob = Path(tempfile.NamedTemporaryFile().name) |
| foo_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6))) |
| blobs = [generate_cc_blob_library.Blob('fooBlob', foo_blob, None)] |
| |
| header = generate_cc_blob_library.header_from_blobs(blobs) |
| expected_header = ( |
| f'{COMMON_HEADER_START}' |
| '\n\n' # No namespace, so two blank lines |
| 'extern const std::array<std::byte, 6> fooBlob;\n') |
| self.assertEqual(expected_header, header) |
| |
| def test_multi_blob_header(self): |
| """Tests the generation of a header for multiple blobs.""" |
| foo_blob = Path(tempfile.NamedTemporaryFile().name) |
| foo_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6))) |
| bar_blob = Path(tempfile.NamedTemporaryFile().name) |
| bar_blob.write_bytes(bytes((10, 9, 8, 7, 6))) |
| blobs = [ |
| generate_cc_blob_library.Blob('fooBlob', foo_blob, None), |
| generate_cc_blob_library.Blob('barBlob', bar_blob, None), |
| ] |
| |
| header = generate_cc_blob_library.header_from_blobs(blobs) |
| expected_header = (f'{COMMON_HEADER_START}\n' |
| '\n' |
| 'extern const std::array<std::byte, 6> fooBlob;\n' |
| '\n' |
| 'extern const std::array<std::byte, 5> barBlob;\n') |
| |
| self.assertEqual(expected_header, header) |
| |
| def test_header_with_namespace(self): |
| """Tests the header generation of namespace definitions.""" |
| foo_blob = Path(tempfile.NamedTemporaryFile().name) |
| foo_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6))) |
| blobs = [generate_cc_blob_library.Blob('fooBlob', foo_blob, None)] |
| |
| header = generate_cc_blob_library.header_from_blobs(blobs, 'pw::foo') |
| expected_header = (f'{COMMON_HEADER_START}' |
| '\n' |
| 'namespace pw::foo {\n' |
| '\n' |
| 'extern const std::array<std::byte, 6> fooBlob;\n' |
| '\n' |
| '} // namespace pw::foo\n') |
| |
| self.assertEqual(expected_header, header) |
| |
| |
| class TestArrayDefFromBlobData(unittest.TestCase): |
| """Unit tests for the array_def_from_blob_data() function.""" |
| def test_single_line(self): |
| """Tests the generation of array definitions with one line of data.""" |
| foo_data = bytes((1, 2)) |
| |
| foo_definition = generate_cc_blob_library.array_def_from_blob_data( |
| generate_cc_blob_library.Blob('fooBlob', Path(), None), foo_data) |
| self.assertEqual(f'\n{FOO_BLOB}', foo_definition) |
| |
| def test_multi_line(self): |
| """Tests the generation of multi-line array definitions.""" |
| bar_data = bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) |
| |
| bar_definition = generate_cc_blob_library.array_def_from_blob_data( |
| generate_cc_blob_library.Blob('barBlob', Path(), None), bar_data) |
| self.assertEqual(f'\n{BAR_BLOB}', bar_definition) |
| |
| |
| class TestSourceFromBlobs(unittest.TestCase): |
| """Unit tests for the source_from_blobs() function.""" |
| def test_source_with_mixed_blobs(self): |
| """Tests generation of a source file with single- and multi-liners.""" |
| foo_blob = Path(tempfile.NamedTemporaryFile().name) |
| foo_blob.write_bytes(bytes((1, 2))) |
| bar_blob = Path(tempfile.NamedTemporaryFile().name) |
| bar_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10))) |
| blobs = [ |
| generate_cc_blob_library.Blob('fooBlob', foo_blob, None), |
| generate_cc_blob_library.Blob('barBlob', bar_blob, None), |
| ] |
| |
| source = generate_cc_blob_library.source_from_blobs( |
| blobs, 'path/to/header.h', 'path/to/source.cc') |
| expected_source = (f'{COMMON_SOURCE_START}' |
| '\n' |
| '\n' |
| f'{FOO_BLOB}' |
| '\n' |
| f'{BAR_BLOB}') |
| |
| self.assertEqual(expected_source, source) |
| |
| def test_source_with_namespace(self): |
| """Tests the source generation of namespace definitions.""" |
| foo_blob = Path(tempfile.NamedTemporaryFile().name) |
| foo_blob.write_bytes(bytes((1, 2))) |
| blobs = [generate_cc_blob_library.Blob('fooBlob', foo_blob, None)] |
| |
| source = generate_cc_blob_library.source_from_blobs( |
| blobs, 'path/to/header.h', 'path/to/source.cc', 'pw::foo') |
| expected_source = (f'{COMMON_SOURCE_START}' |
| '\n' |
| 'namespace pw::foo {\n' |
| '\n' |
| f'{FOO_BLOB}' |
| '\n' |
| '} // namespace pw::foo\n') |
| |
| self.assertEqual(expected_source, source) |
| |
| def test_source_with_linker_sections(self): |
| """Tests generation of a source file with defined linker sections.""" |
| foo_blob = Path(tempfile.NamedTemporaryFile().name) |
| foo_blob.write_bytes(bytes((1, 2))) |
| bar_blob = Path(tempfile.NamedTemporaryFile().name) |
| bar_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10))) |
| blobs = [ |
| generate_cc_blob_library.Blob('fooBlob', foo_blob, '.foo_section'), |
| generate_cc_blob_library.Blob('barBlob', bar_blob, '.bar_section'), |
| ] |
| |
| source = generate_cc_blob_library.source_from_blobs( |
| blobs, 'path/to/header.h', 'path/to/source.cc') |
| expected_source = (f'{COMMON_SOURCE_START}' |
| '\n' |
| '\n' |
| 'PW_PLACE_IN_SECTION(".foo_section")\n' |
| f'{FOO_BLOB}' |
| '\n' |
| 'PW_PLACE_IN_SECTION(".bar_section")\n' |
| f'{BAR_BLOB}') |
| |
| self.assertEqual(expected_source, source) |
| |
| def test_source_with_alignas(self): |
| """Tests generation of a source file with alignas specified.""" |
| foo_blob = Path(tempfile.NamedTemporaryFile().name) |
| foo_blob.write_bytes(bytes((1, 2))) |
| bar_blob = Path(tempfile.NamedTemporaryFile().name) |
| bar_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10))) |
| blobs = [ |
| generate_cc_blob_library.Blob('fooBlob', foo_blob, None, '64'), |
| generate_cc_blob_library.Blob('barBlob', bar_blob, '.abc', 'int'), |
| ] |
| |
| source = generate_cc_blob_library.source_from_blobs( |
| blobs, 'path/to/header.h', 'path/to/source.cc') |
| expected_source = (f'{COMMON_SOURCE_START}' |
| '\n' |
| '\n' |
| f'alignas(64) {FOO_BLOB}' |
| '\n' |
| 'PW_PLACE_IN_SECTION(".abc")\n' |
| f'alignas(int) {BAR_BLOB}') |
| |
| self.assertEqual(expected_source, source) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |