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