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 15 #[allow(unused_imports)] 16 use pw_format::{macros::FormatParams, Style}; 17 18 // Used to record calls into the test generator from `generator_test_macro!`. 19 #[derive(Debug, PartialEq)] 20 pub enum TestGeneratorOps { 21 Finalize, 22 StringFragment(String), 23 IntegerConversion { 24 params: FormatParams, 25 signed: bool, 26 type_width: u8, 27 arg: String, 28 }, 29 StringConversion(String), 30 CharConversion(String), 31 UntypedConversion(String), 32 } 33 34 // Used to record calls into the test generator from `printf_generator_test_macro!` and friends. 35 #[derive(Clone, Debug, PartialEq)] 36 pub enum PrintfTestGeneratorOps { 37 Finalize, 38 StringFragment(String), 39 IntegerConversion { ty: String, arg: String }, 40 StringConversion(String), 41 CharConversion(String), 42 UntypedConversion(String), 43 } 44 45 #[cfg(test)] 46 mod tests { 47 use pw_format_test_macros::{ 48 char_sub_printf_format_core_fmt_generator_test_macro, 49 char_sub_printf_format_printf_generator_test_macro, 50 integer_sub_printf_format_core_fmt_generator_test_macro, 51 integer_sub_printf_format_printf_generator_test_macro, 52 printf_format_core_fmt_generator_test_macro, printf_format_generator_test_macro, 53 printf_format_printf_generator_test_macro, 54 string_sub_printf_format_core_fmt_generator_test_macro, 55 string_sub_printf_format_printf_generator_test_macro, 56 }; 57 58 // Create an alias to ourselves so that the proc macro can name our crate. 59 use crate as pw_format_test_macros_test; 60 61 use super::*; 62 63 #[test] generate_calls_generator_correctly()64 fn generate_calls_generator_correctly() { 65 assert_eq!( 66 printf_format_generator_test_macro!("test %ld %s %c %v", 5, "test", 'c', 1), 67 vec![ 68 TestGeneratorOps::StringFragment("test ".to_string()), 69 TestGeneratorOps::IntegerConversion { 70 params: FormatParams { 71 style: Style::None, 72 min_field_width: None, 73 zero_padding: false, 74 }, 75 signed: true, 76 type_width: 32, 77 arg: "5".to_string(), 78 }, 79 TestGeneratorOps::StringFragment(" ".to_string()), 80 TestGeneratorOps::StringConversion("\"test\"".to_string()), 81 TestGeneratorOps::StringFragment(" ".to_string()), 82 TestGeneratorOps::CharConversion("'c'".to_string()), 83 TestGeneratorOps::StringFragment(" ".to_string()), 84 TestGeneratorOps::UntypedConversion("1".to_string()), 85 TestGeneratorOps::Finalize 86 ] 87 ); 88 } 89 90 #[test] generate_printf_calls_generator_correctly()91 fn generate_printf_calls_generator_correctly() { 92 assert_eq!( 93 printf_format_printf_generator_test_macro!( 94 "test %ld %s %c %v %v", 95 5, 96 "test", 97 'c', 98 1 as i32, 99 "string" as &str 100 ), 101 ( 102 // %ld gets converted to %d because they are equivalent for 32 bit 103 // systems. 104 // %v gets converted to %d since we pass in a signed integer. 105 "test %d %s %c %d %s", 106 vec![ 107 PrintfTestGeneratorOps::StringFragment("test ".to_string()), 108 PrintfTestGeneratorOps::IntegerConversion { 109 ty: "i32".to_string(), 110 arg: "5".to_string(), 111 }, 112 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 113 PrintfTestGeneratorOps::StringConversion("\"test\"".to_string()), 114 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 115 PrintfTestGeneratorOps::CharConversion("'c'".to_string()), 116 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 117 PrintfTestGeneratorOps::UntypedConversion("1 as i32".to_string()), 118 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 119 PrintfTestGeneratorOps::UntypedConversion("\"string\" as & str".to_string()), 120 PrintfTestGeneratorOps::Finalize 121 ] 122 ) 123 ); 124 } 125 126 #[test] generate_printf_translates_field_width_and_leading_zeros_correctly()127 fn generate_printf_translates_field_width_and_leading_zeros_correctly() { 128 let expected_fragments = vec![ 129 PrintfTestGeneratorOps::StringFragment("Test ".to_string()), 130 PrintfTestGeneratorOps::IntegerConversion { 131 ty: "u32".to_string(), 132 arg: "0x42".to_string(), 133 }, 134 PrintfTestGeneratorOps::StringFragment(" test".to_string()), 135 PrintfTestGeneratorOps::Finalize, 136 ]; 137 138 // No field width. 139 assert_eq!( 140 printf_format_printf_generator_test_macro!("Test %x test", 0x42), 141 ("Test %x test", expected_fragments.clone()) 142 ); 143 144 // Field width without zero padding. 145 assert_eq!( 146 printf_format_printf_generator_test_macro!("Test %8x test", 0x42), 147 ("Test %8x test", expected_fragments.clone()) 148 ); 149 150 // Field width with zero padding. 151 assert_eq!( 152 printf_format_printf_generator_test_macro!("Test %08x test", 0x42), 153 ("Test %08x test", expected_fragments.clone()) 154 ); 155 156 // Test another format base. 157 assert_eq!( 158 printf_format_printf_generator_test_macro!("Test %08u test", 0x42), 159 ("Test %08u test", expected_fragments.clone()) 160 ); 161 } 162 163 #[test] generate_core_fmt_translates_field_width_and_leading_zeros_correctly()164 fn generate_core_fmt_translates_field_width_and_leading_zeros_correctly() { 165 let expected_fragments = vec![ 166 PrintfTestGeneratorOps::StringFragment("Test ".to_string()), 167 PrintfTestGeneratorOps::IntegerConversion { 168 ty: "u32".to_string(), 169 arg: "0x42".to_string(), 170 }, 171 PrintfTestGeneratorOps::StringFragment(" test".to_string()), 172 PrintfTestGeneratorOps::Finalize, 173 ]; 174 175 // No field width. 176 assert_eq!( 177 printf_format_core_fmt_generator_test_macro!("Test %x test", 0x42), 178 ("Test {:x} test", expected_fragments.clone()) 179 ); 180 181 // Field width without zero padding. 182 assert_eq!( 183 printf_format_core_fmt_generator_test_macro!("Test %8x test", 0x42), 184 ("Test {:8x} test", expected_fragments.clone()) 185 ); 186 187 // Field width with zero padding. 188 assert_eq!( 189 printf_format_core_fmt_generator_test_macro!("Test %08x test", 0x42), 190 ("Test {:08x} test", expected_fragments.clone()) 191 ); 192 193 // Test another format base 194 assert_eq!( 195 printf_format_core_fmt_generator_test_macro!("Test %08u test", 0x42), 196 ("Test {:08} test", expected_fragments.clone()) 197 ); 198 } 199 200 // Test that a generator returning an overridden integer conversion specifier 201 // changes that and only that conversion specifier in the format string. 202 #[test] generate_printf_substitutes_integer_conversion()203 fn generate_printf_substitutes_integer_conversion() { 204 assert_eq!( 205 integer_sub_printf_format_printf_generator_test_macro!( 206 "test %ld %s %c", 207 5, 208 "test", 209 'c' 210 ), 211 ( 212 "test %K %s %c", 213 vec![ 214 PrintfTestGeneratorOps::StringFragment("test ".to_string()), 215 PrintfTestGeneratorOps::IntegerConversion { 216 ty: "i32".to_string(), 217 arg: "5".to_string(), 218 }, 219 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 220 PrintfTestGeneratorOps::StringConversion("\"test\"".to_string()), 221 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 222 PrintfTestGeneratorOps::CharConversion("'c'".to_string()), 223 PrintfTestGeneratorOps::Finalize 224 ] 225 ) 226 ); 227 } 228 229 // Test that a generator returning an overridden string conversion specifier 230 // changes that and only that conversion specifier in the format string. 231 #[test] generate_printf_substitutes_string_conversion()232 fn generate_printf_substitutes_string_conversion() { 233 assert_eq!( 234 string_sub_printf_format_printf_generator_test_macro!("test %ld %s %c", 5, "test", 'c'), 235 ( 236 // %ld gets converted to %d because they are equivalent for 32 bit 237 // systems. 238 "test %d %K %c", 239 vec![ 240 PrintfTestGeneratorOps::StringFragment("test ".to_string()), 241 PrintfTestGeneratorOps::IntegerConversion { 242 ty: "i32".to_string(), 243 arg: "5".to_string(), 244 }, 245 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 246 PrintfTestGeneratorOps::StringConversion("\"test\"".to_string()), 247 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 248 PrintfTestGeneratorOps::CharConversion("'c'".to_string()), 249 PrintfTestGeneratorOps::Finalize 250 ] 251 ) 252 ); 253 } 254 255 // Test that a generator returning an overridden character conversion specifier 256 // changes that and only that conversion specifier in the format string. 257 #[test] generate_printf_substitutes_char_conversion()258 fn generate_printf_substitutes_char_conversion() { 259 assert_eq!( 260 char_sub_printf_format_printf_generator_test_macro!("test %ld %s %c", 5, "test", 'c'), 261 ( 262 // %ld gets converted to %d because they are equivalent for 32 bit 263 // systems. 264 "test %d %s %K", 265 vec![ 266 PrintfTestGeneratorOps::StringFragment("test ".to_string()), 267 PrintfTestGeneratorOps::IntegerConversion { 268 ty: "i32".to_string(), 269 arg: "5".to_string() 270 }, 271 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 272 PrintfTestGeneratorOps::StringConversion("\"test\"".to_string()), 273 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 274 PrintfTestGeneratorOps::CharConversion("'c'".to_string()), 275 PrintfTestGeneratorOps::Finalize 276 ] 277 ) 278 ); 279 } 280 281 #[test] generate_core_fmt_calls_generator_correctly()282 fn generate_core_fmt_calls_generator_correctly() { 283 assert_eq!( 284 printf_format_core_fmt_generator_test_macro!("test %ld %s %c %v", 5, "test", 'c', 1), 285 ( 286 "test {} {} {} {}", 287 vec![ 288 PrintfTestGeneratorOps::StringFragment("test ".to_string()), 289 PrintfTestGeneratorOps::IntegerConversion { 290 ty: "i32".to_string(), 291 arg: "5".to_string() 292 }, 293 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 294 PrintfTestGeneratorOps::StringConversion("\"test\"".to_string()), 295 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 296 PrintfTestGeneratorOps::CharConversion("'c'".to_string()), 297 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 298 PrintfTestGeneratorOps::UntypedConversion("1".to_string()), 299 PrintfTestGeneratorOps::Finalize 300 ] 301 ) 302 ); 303 } 304 305 // Test that a generator returning an overridden integer conversion specifier 306 // changes that and only that conversion specifier in the format string. 307 #[test] generate_core_fmt_substitutes_integer_conversion()308 fn generate_core_fmt_substitutes_integer_conversion() { 309 assert_eq!( 310 integer_sub_printf_format_core_fmt_generator_test_macro!( 311 "test %ld %s %c", 312 5, 313 "test", 314 'c' 315 ), 316 ( 317 "test {:?} {} {}", 318 vec![ 319 PrintfTestGeneratorOps::StringFragment("test ".to_string()), 320 PrintfTestGeneratorOps::IntegerConversion { 321 ty: "i32".to_string(), 322 arg: "5".to_string() 323 }, 324 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 325 PrintfTestGeneratorOps::StringConversion("\"test\"".to_string()), 326 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 327 PrintfTestGeneratorOps::CharConversion("'c'".to_string()), 328 PrintfTestGeneratorOps::Finalize 329 ] 330 ) 331 ); 332 } 333 334 // Test that a generator returning an overridden string conversion specifier 335 // changes that and only that conversion specifier in the format string. 336 #[test] generate_core_fmt_substitutes_string_conversion()337 fn generate_core_fmt_substitutes_string_conversion() { 338 assert_eq!( 339 string_sub_printf_format_core_fmt_generator_test_macro!( 340 "test %ld %s %c", 341 5, 342 "test", 343 'c' 344 ), 345 ( 346 "test {} {:?} {}", 347 vec![ 348 PrintfTestGeneratorOps::StringFragment("test ".to_string()), 349 PrintfTestGeneratorOps::IntegerConversion { 350 ty: "i32".to_string(), 351 arg: "5".to_string(), 352 }, 353 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 354 PrintfTestGeneratorOps::StringConversion("\"test\"".to_string()), 355 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 356 PrintfTestGeneratorOps::CharConversion("'c'".to_string()), 357 PrintfTestGeneratorOps::Finalize 358 ] 359 ) 360 ); 361 } 362 363 // Test that a generator returning an overridden character conversion specifier 364 // changes that and only that conversion specifier in the format string. 365 #[test] generate_core_fmt_substitutes_char_conversion()366 fn generate_core_fmt_substitutes_char_conversion() { 367 assert_eq!( 368 char_sub_printf_format_core_fmt_generator_test_macro!("test %ld %s %c", 5, "test", 'c'), 369 ( 370 "test {} {} {:?}", 371 vec![ 372 PrintfTestGeneratorOps::StringFragment("test ".to_string()), 373 PrintfTestGeneratorOps::IntegerConversion { 374 ty: "i32".to_string(), 375 arg: "5".to_string(), 376 }, 377 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 378 PrintfTestGeneratorOps::StringConversion("\"test\"".to_string()), 379 PrintfTestGeneratorOps::StringFragment(" ".to_string()), 380 PrintfTestGeneratorOps::CharConversion("'c'".to_string()), 381 PrintfTestGeneratorOps::Finalize 382 ] 383 ) 384 ); 385 } 386 387 #[test] multiple_format_strings_are_concatenated()388 fn multiple_format_strings_are_concatenated() { 389 assert_eq!( 390 printf_format_generator_test_macro!("a" PW_FMT_CONCAT "b"), 391 vec![ 392 TestGeneratorOps::StringFragment("ab".to_string()), 393 TestGeneratorOps::Finalize 394 ] 395 ); 396 } 397 } 398