1# Copyright 2023 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"""Tests for the pw_build.gn_writer module.""" 15 16import os 17import unittest 18 19from io import StringIO 20from pathlib import PurePath 21from tempfile import TemporaryDirectory 22 23from pw_build.gn_writer import ( 24 COPYRIGHT_HEADER, 25 GnFile, 26 GnWriter, 27) 28from pw_build.gn_target import GnTarget 29 30 31class TestGnWriter(unittest.TestCase): 32 """Tests for gn_writer.GnWriter.""" 33 34 def setUp(self): 35 """Creates a GnWriter that writes to a StringIO.""" 36 self.output = StringIO() 37 self.writer = GnWriter(self.output) 38 39 def test_write_comment(self): 40 """Writes a GN comment.""" 41 self.writer.write_comment('hello, world!') 42 self.assertEqual( 43 self.output.getvalue(), 44 '# hello, world!\n', 45 ) 46 47 def test_write_comment_wrap(self): 48 """Writes a GN comment that is exactly 80 characters.""" 49 extra_long = ( 50 "This line is a " + ("really, " * 5) + "REALLY extra long comment" 51 ) 52 self.writer.write_comment(extra_long) 53 self.assertEqual( 54 self.output.getvalue(), 55 '# This line is a really, really, really, really, really, REALLY ' 56 'extra long\n# comment\n', 57 ) 58 59 def test_write_comment_nowrap(self): 60 """Writes a long GN comment without whitespace to wrap on.""" 61 no_breaks = 'A' + ('a' * 76) + 'h!' 62 self.writer.write_comment(no_breaks) 63 self.assertEqual( 64 self.output.getvalue(), 65 '# Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 66 'aaaaaaaaaaaaah!\n', 67 ) 68 69 def test_write_target(self): 70 """Tests writing the target using a GnWriter.""" 71 target = GnTarget('custom_type', 'my-target') 72 target.attrs = { 73 'public': ['$src/foo/my-header.h'], 74 'sources': ['$src/foo/my-source.cc'], 75 'inputs': ['$src/bar/my.data'], 76 'cflags': ['-frobinator'], 77 'public_deps': [ 78 ':foo', 79 '$dir_pw_third_party/repo/bar', 80 ], 81 'deps': [ 82 '$dir_pw_third_party/repo:top-level', 83 '../another-pkg/baz', 84 ], 85 } 86 target.origin = '//my-package:my-target' 87 self.writer.write_target(target) 88 89 self.assertEqual( 90 self.output.getvalue(), 91 ''' 92# Generated from //my-package:my-target 93custom_type("my-target") { 94 public = [ 95 "$src/foo/my-header.h", 96 ] 97 sources = [ 98 "$src/foo/my-source.cc", 99 ] 100 inputs = [ 101 "$src/bar/my.data", 102 ] 103 cflags = [ 104 "-frobinator", 105 ] 106 public_deps = [ 107 "$dir_pw_third_party/repo/bar", 108 ":foo", 109 ] 110 deps = [ 111 "$dir_pw_third_party/repo:top-level", 112 "../another-pkg/baz", 113 ] 114} 115'''.lstrip(), 116 ) 117 118 def test_write_list(self): 119 """Writes a GN list assigned to a variable.""" 120 self.writer.write_list('empty', []) 121 self.writer.write_list('items', ['foo', 'bar', 'baz']) 122 lines = [ 123 'items = [', 124 ' "bar",', 125 ' "baz",', 126 ' "foo",', 127 ']', 128 ] 129 self.assertEqual('\n'.join(lines), self.output.getvalue().strip()) 130 131 def test_write_file(self): 132 """Writes a BUILD.gn file.""" 133 imports = { 134 '$dir_pw_third_party/foo/foo.gni', 135 '$dir_pw_third_party/bar/bar.gni', 136 } 137 gn_targets = [ 138 GnTarget('foo_source_set', 'foo'), 139 GnTarget('bar_source_set', 'bar'), 140 ] 141 gn_targets[0].attrs = { 142 'public': ['$dir_pw_third_party_foo/foo.h'], 143 'sources': ['$dir_pw_third_party_foo/foo.cc'], 144 'deps': [':bar'], 145 } 146 gn_targets[1].attrs = { 147 'public': ['$dir_pw_third_party_bar/bar.h'], 148 } 149 self.writer.write_file(imports, gn_targets) 150 lines = [ 151 'import("//build_overrides/pigweed.gni")', 152 '', 153 'import("$dir_pw_third_party/bar/bar.gni")', 154 'import("$dir_pw_third_party/foo/foo.gni")', 155 '', 156 'bar_source_set("bar") {', 157 ' public = [', 158 ' "$dir_pw_third_party_bar/bar.h",', 159 ' ]', 160 '}', 161 '', 162 'foo_source_set("foo") {', 163 ' public = [', 164 ' "$dir_pw_third_party_foo/foo.h",', 165 ' ]', 166 ' sources = [', 167 ' "$dir_pw_third_party_foo/foo.cc",', 168 ' ]', 169 ' deps = [', 170 ' ":bar",', 171 ' ]', 172 '}', 173 ] 174 self.assertEqual('\n'.join(lines), self.output.getvalue().strip()) 175 176 177class TestGnFile(unittest.TestCase): 178 """Tests for gn_writer.GnFile.""" 179 180 def test_format_on_close(self): 181 """Verifies the GN file is formatted when the file is closed.""" 182 with TemporaryDirectory() as tmpdirname: 183 with GnFile(PurePath(tmpdirname, 'BUILD.gn')) as build_gn: 184 build_gn.write(' correct = "indent"') 185 build_gn.write_comment('comment') 186 build_gn.write_list('single_item', ['is.inlined']) 187 188 filename = PurePath('pw_build', 'gn_writer.py') 189 expected = ( 190 COPYRIGHT_HEADER 191 + f''' 192# This file was automatically generated by {filename} 193 194correct = "indent" 195 196# comment 197single_item = [ "is.inlined" ] 198''' 199 ) 200 with open(os.path.join(tmpdirname, 'BUILD.gn'), 'r') as build_gn: 201 self.assertEqual(expected.strip(), build_gn.read().strip()) 202 203 204if __name__ == '__main__': 205 unittest.main() 206