• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2021 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Unit tests for generate_cc_blob_library.py"""
15
16from pathlib import Path
17import tempfile
18import unittest
19
20from pw_build import generate_cc_blob_library
21
22COMMENT = """\
23// This file was generated by generate_cc_blob_library.py.
24//
25// DO NOT EDIT!
26//
27// This file contains declarations for byte arrays created from files during the
28// build. The byte arrays are constant initialized and are safe to access at any
29// time, including before main().
30//
31// See https://pigweed.dev/pw_build/#pw-cc-blob-library for details.
32"""
33
34COMMON_HEADER_START = (
35    COMMENT
36    + """\
37#pragma once
38
39#include <array>
40#include <cstddef>
41"""
42)
43
44COMMON_SOURCE_START = (
45    COMMENT
46    + """\
47
48#include "path/to/header.h"
49
50#include <array>
51#include <cstddef>
52
53#include "pw_preprocessor/compiler.h"
54"""
55)
56
57FOO_BLOB = """\
58constexpr std::array<std::byte, 2> fooBlob = {
59    std::byte{0x01}, std::byte{0x02},
60};
61"""
62
63BAR_BLOB = """\
64constexpr std::array<std::byte, 10> barBlob = {
65    std::byte{0x01}, std::byte{0x02}, std::byte{0x03}, std::byte{0x04},
66    std::byte{0x05}, std::byte{0x06}, std::byte{0x07}, std::byte{0x08},
67    std::byte{0x09}, std::byte{0x0A},
68};
69"""
70
71
72class TestSplitIntoChunks(unittest.TestCase):
73    """Unit tests for the split_into_chunks() function."""
74
75    def test_perfect_split(self):
76        """Tests basic splitting where the iterable divides perfectly."""
77        data = (1, 7, 0, 1)
78        self.assertEqual(
79            ((1, 7), (0, 1)),
80            tuple(generate_cc_blob_library.split_into_chunks(data, 2)),
81        )
82
83    def test_split_with_remainder(self):
84        """Tests basic splitting where there is a remainder."""
85        data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
86        self.assertEqual(
87            ((1, 2, 3), (4, 5, 6), (7, 8, 9), (10,)),
88            tuple(generate_cc_blob_library.split_into_chunks(data, 3)),
89        )
90
91
92class TestHeaderFromBlobs(unittest.TestCase):
93    """Unit tests for the header_from_blobs() function."""
94
95    def test_single_blob_header(self):
96        """Tests the generation of a header for a single blob."""
97        foo_blob = Path(tempfile.NamedTemporaryFile().name)
98        foo_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6)))
99        blobs = [generate_cc_blob_library.Blob('fooBlob', foo_blob, None)]
100
101        header = generate_cc_blob_library.header_from_blobs(blobs)
102        expected_header = (
103            f'{COMMON_HEADER_START}'
104            '\n\n'  # No namespace, so two blank lines
105            'extern const std::array<std::byte, 6> fooBlob;\n'
106        )
107        self.assertEqual(expected_header, header)
108
109    def test_multi_blob_header(self):
110        """Tests the generation of a header for multiple blobs."""
111        foo_blob = Path(tempfile.NamedTemporaryFile().name)
112        foo_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6)))
113        bar_blob = Path(tempfile.NamedTemporaryFile().name)
114        bar_blob.write_bytes(bytes((10, 9, 8, 7, 6)))
115        blobs = [
116            generate_cc_blob_library.Blob('fooBlob', foo_blob, None),
117            generate_cc_blob_library.Blob('barBlob', bar_blob, None),
118        ]
119
120        header = generate_cc_blob_library.header_from_blobs(blobs)
121        expected_header = (
122            f'{COMMON_HEADER_START}\n'
123            '\n'
124            'extern const std::array<std::byte, 6> fooBlob;\n'
125            '\n'
126            'extern const std::array<std::byte, 5> barBlob;\n'
127        )
128
129        self.assertEqual(expected_header, header)
130
131    def test_header_with_namespace(self):
132        """Tests the header generation of namespace definitions."""
133        foo_blob = Path(tempfile.NamedTemporaryFile().name)
134        foo_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6)))
135        blobs = [generate_cc_blob_library.Blob('fooBlob', foo_blob, None)]
136
137        header = generate_cc_blob_library.header_from_blobs(blobs, 'pw::foo')
138        expected_header = (
139            f'{COMMON_HEADER_START}'
140            '\n'
141            'namespace pw::foo {\n'
142            '\n'
143            'extern const std::array<std::byte, 6> fooBlob;\n'
144            '\n'
145            '}  // namespace pw::foo\n'
146        )
147
148        self.assertEqual(expected_header, header)
149
150
151class TestArrayDefFromBlobData(unittest.TestCase):
152    """Unit tests for the array_def_from_blob_data() function."""
153
154    def test_single_line(self):
155        """Tests the generation of array definitions with one line of data."""
156        foo_data = bytes((1, 2))
157
158        foo_definition = generate_cc_blob_library.array_def_from_blob_data(
159            generate_cc_blob_library.Blob('fooBlob', Path(), None), foo_data
160        )
161        self.assertEqual(f'\n{FOO_BLOB}', foo_definition)
162
163    def test_multi_line(self):
164        """Tests the generation of multi-line array definitions."""
165        bar_data = bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
166
167        bar_definition = generate_cc_blob_library.array_def_from_blob_data(
168            generate_cc_blob_library.Blob('barBlob', Path(), None), bar_data
169        )
170        self.assertEqual(f'\n{BAR_BLOB}', bar_definition)
171
172
173class TestSourceFromBlobs(unittest.TestCase):
174    """Unit tests for the source_from_blobs() function."""
175
176    def test_source_with_mixed_blobs(self):
177        """Tests generation of a source file with single- and multi-liners."""
178        foo_blob = Path(tempfile.NamedTemporaryFile().name)
179        foo_blob.write_bytes(bytes((1, 2)))
180        bar_blob = Path(tempfile.NamedTemporaryFile().name)
181        bar_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
182        blobs = [
183            generate_cc_blob_library.Blob('fooBlob', foo_blob, None),
184            generate_cc_blob_library.Blob('barBlob', bar_blob, None),
185        ]
186
187        source = generate_cc_blob_library.source_from_blobs(
188            blobs, 'path/to/header.h'
189        )
190        expected_source = (
191            f'{COMMON_SOURCE_START}' '\n' '\n' f'{FOO_BLOB}' '\n' f'{BAR_BLOB}'
192        )
193
194        self.assertEqual(expected_source, source)
195
196    def test_source_with_namespace(self):
197        """Tests the source generation of namespace definitions."""
198        foo_blob = Path(tempfile.NamedTemporaryFile().name)
199        foo_blob.write_bytes(bytes((1, 2)))
200        blobs = [generate_cc_blob_library.Blob('fooBlob', foo_blob, None)]
201
202        source = generate_cc_blob_library.source_from_blobs(
203            blobs, 'path/to/header.h', 'pw::foo'
204        )
205        expected_source = (
206            f'{COMMON_SOURCE_START}'
207            '\n'
208            'namespace pw::foo {\n'
209            '\n'
210            f'{FOO_BLOB}'
211            '\n'
212            '}  // namespace pw::foo\n'
213        )
214
215        self.assertEqual(expected_source, source)
216
217    def test_source_with_linker_sections(self):
218        """Tests generation of a source file with defined linker sections."""
219        foo_blob = Path(tempfile.NamedTemporaryFile().name)
220        foo_blob.write_bytes(bytes((1, 2)))
221        bar_blob = Path(tempfile.NamedTemporaryFile().name)
222        bar_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
223        blobs = [
224            generate_cc_blob_library.Blob('fooBlob', foo_blob, '.foo_section'),
225            generate_cc_blob_library.Blob('barBlob', bar_blob, '.bar_section'),
226        ]
227
228        source = generate_cc_blob_library.source_from_blobs(
229            blobs, 'path/to/header.h'
230        )
231        expected_source = (
232            f'{COMMON_SOURCE_START}'
233            '\n'
234            '\n'
235            'PW_PLACE_IN_SECTION(".foo_section")\n'
236            f'{FOO_BLOB}'
237            '\n'
238            'PW_PLACE_IN_SECTION(".bar_section")\n'
239            f'{BAR_BLOB}'
240        )
241
242        self.assertEqual(expected_source, source)
243
244    def test_source_with_alignas(self):
245        """Tests generation of a source file with alignas specified."""
246        foo_blob = Path(tempfile.NamedTemporaryFile().name)
247        foo_blob.write_bytes(bytes((1, 2)))
248        bar_blob = Path(tempfile.NamedTemporaryFile().name)
249        bar_blob.write_bytes(bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
250        blobs = [
251            generate_cc_blob_library.Blob('fooBlob', foo_blob, None, '64'),
252            generate_cc_blob_library.Blob('barBlob', bar_blob, '.abc', 'int'),
253        ]
254
255        source = generate_cc_blob_library.source_from_blobs(
256            blobs, 'path/to/header.h'
257        )
258        expected_source = (
259            f'{COMMON_SOURCE_START}'
260            '\n'
261            '\n'
262            f'alignas(64) {FOO_BLOB}'
263            '\n'
264            'alignas(int) PW_PLACE_IN_SECTION(".abc")\n'
265            f'{BAR_BLOB}'
266        )
267
268        self.assertEqual(expected_source, source)
269
270
271if __name__ == '__main__':
272    unittest.main()
273