1#! /usr/bin/perl 2## 3## Copyright 2019 The Android Open Source Project 4## 5## Licensed under the Apache License, Version 2.0 (the "License"); 6## you may not use this file except in compliance with the License. 7## You may obtain a copy of the License at 8## 9## http://www.apache.org/licenses/LICENSE-2.0 10## 11## Unless required by applicable law or agreed to in writing, software 12## distributed under the License is distributed on an "AS IS" BASIS, 13## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14## See the License for the specific language governing permissions and 15## limitations under the License. 16 17use File::Basename; 18 19## mockcify version 20## 21## 0.6.0 Replace `extern` with `include` for mock_function_count_map 22## 23## 0.5.0 Add compilation check 24## 25## 0.4.0 Second re-write 26## 27## 0.3.2 Remove pragma from source file 28## 29## 0.3.1 Statically link return value to prevent 'this' pointer in function 30## 31## 0.3.0 Re-write parser. 32## 33## 0.2.1 Compilation units only include types and a single related header file 34## Alphabetically sort functions by return value 35## Add non-primative return data values in structure 36## 37## 0.2.0 First version 38## 39my $VERSION = "0.6.0"; 40 41use strict; 42use warnings; 43 44my $YEAR = "2023"; 45my $TOKEN = "MOCKCIFY_TOKEN"; 46my $MOCKCIFY_BRACKET_GROUP = "MOCKCIFY_BRACKET_GROUP"; 47my $CLANG_FORMAT = "/usr/bin/clang-format-13"; 48my $CC = "g++"; 49my $LIBCHROME = "../../../../external/libchrome/"; 50my $COMPILE_SCREEN_ENABLED = 0; 51 52my @structs; 53 54my %function_signature; 55my %function_return_types; 56my @function_names; 57my %function_params; 58my %function_param_names; 59my %function_param_types; 60 61sub clang_format { 62 return `$CLANG_FORMAT --style="{ColumnLimit: 10000, PointerAlignment: Left, PointerBindsToType: true, FixNamespaceComments: true }"`; 63} 64 65## Create a temp directory for any cruft 66my $TMPDIR="/tmp/mockcify"; 67system("mkdir -p $TMPDIR"); 68my $OUTDIR = "$TMPDIR/out/"; 69system("mkdir -p $OUTDIR"); 70my $INCDIR = "$TMPDIR/include/"; 71system("mkdir -p $INCDIR"); 72 73if (scalar(@ARGV == 0)) { 74 printf(STDERR "ERROR Must supply at least one argument\n"); 75 exit 1; 76} 77 78my $arg = shift @ARGV; 79## Check only argument for debug vector 80if ($arg =~ /--cla[ng]/) { 81 exit print clang_format(); 82} elsif ($arg =~ /--f[ilter]/) { 83 exit print read_stdin_and_filter_file(); 84} elsif ($arg =~ /--l[ines]/) { 85 exit print filter_lines(read_stdin_and_filter_file()); 86} elsif ($arg =~ /--i[nfo]/) { 87 my ($incs, $types, $funcs) = parse_info(filter_lines(read_stdin_and_filter_file())); 88 exit print @{$incs}, @{$types}, @{$funcs}; 89} elsif ($arg =~ /--co[mpile]/) { 90 exit compilation_screen("mock_" . shift @ARGV); 91} elsif ($arg =~ /--cle[an]/) { 92 exit system("mv $TMPDIR $TMPDIR.deleted"); 93} elsif ($arg =~ /--u[nittest]/) { 94 print(STDERR "unit testing device"); 95} 96 97sub help { 98 print <<EOF 99 Usage: 100 Specify a namespace on the command line for the shared structure data. 101 Then pipe the C file on stdin and one source and one header file will 102 be created based upon the namespace convention 103 104 mockcify.pl stack_l2cap_api < stack/l2cap/l2c_api.cc 105 106 Output files: 107 mock_stack_l2cap_api.cc 108 mock_stack_l2cap_api.h 109 110 The tool is not capable of parsing C++ and a workaround is to remove 111 C++ in the source prior to mock-C-fying the source. 112 113EOF 114} 115 116## Only single arg is taken 117my $namespace = $arg; 118 119if ($namespace =~ /^--/) { 120 print(STDERR "ERROR Halting due to ill-formed namespace expression \'$namespace\'\n"); 121 exit -1; 122} 123 124### 125### Phase 0: Prepare input and output file streams 126### 127 128## Default to stdout 129my $FH_SRC; 130my $FH_HDR; 131 132my $src_filename; 133my $hdr_filename; 134## If namepace specified then write to that source and header 135if ($namespace eq "TESTING") { 136 $FH_SRC = *STDOUT; 137 $FH_HDR = *STDOUT; 138} else { 139 $src_filename="mock_" . $namespace . ".cc"; 140 $hdr_filename="mock_" . $namespace . ".h"; 141 142 open($FH_SRC, ">", $OUTDIR .$src_filename) 143 or die $!; 144 145 open($FH_HDR, ">", $OUTDIR .$hdr_filename) 146 or die $!; 147} 148 149### 150### Phase 1: Read input file and apply single line filtering 151### 152my $text = read_stdin_and_filter_file(); 153 154### 155### Phase 2: Apply Multiline filters 156### 157$text = filter_lines($text); 158 159## 160## Phase 3: Extract required mock information 161## 162my ($includes_ref, $typedefs_ref, $functions_ref, $usings_ref, $namespaces_ref) = parse_info($text); 163my @includes = @{$includes_ref}; 164my @typedefs = @{$typedefs_ref}; 165my @functions = @{$functions_ref}; 166my @namespaces = @{$namespaces_ref}; 167my @usings = @{$usings_ref}; 168 169@includes = reject_include_list(@includes); 170 171@functions = grep { parse_function_into_components ($_) } @functions; 172 173## 174## Phase 4: Output the mocks source and header 175## 176print_source($FH_SRC); 177print_header($FH_HDR); 178 179close ($FH_SRC); 180close ($FH_HDR); 181 182## Format the final source code files 183if (defined $src_filename) { 184 system("clang-format", "-i", $OUTDIR . $src_filename); 185 system("clang-format", "-i", $OUTDIR . $hdr_filename); 186} 187 188print(STDERR "Generated files:", $OUTDIR . $src_filename, " ", $OUTDIR . $hdr_filename, "\n"); 189 190if ($COMPILE_SCREEN_ENABLED) { 191 my $rc = compilation_screen("mock_" . $namespace); 192 exit ($rc == 256) ?1 : 0; 193} 194 195sub reject_include_list { 196 my @incs = (); 197 foreach (@_) { 198 next if (/init_flags/); 199 push(@incs, $_); 200 } 201 return @incs; 202} 203 204sub compile_screen_failed { 205 my $src = shift @_; 206 print STDERR <<EOF 207 MOCK Compilation is EXPERIMENTAL ONLY 208 209 ERROR Failed to compile \'$src\' NOTE: This does not mean 210 the mock is unusable as the tool only screens the compilation. 211 212 There could be one of 3 problems: 213 1. Undeclared external surface or dependency 214 2. C++ code or namespaces mixed in with C code 215 3. An issue with proper mock'ing with mockcify. 216EOF 217} 218 219sub compilation_screen { 220 my $base= shift @_; 221 my $src=$base . ".cc"; 222 my $hdr=$base . ".h"; 223 224 ## Verious external or generated header not needed for mocks 225 foreach(( 226 "test/mock/mock.h", 227 "src/init_flags.rs.h", 228 "src/message_loop_thread.rs.h", 229 "android/hardware/bluetooth/audio/2.2/IBluetoothAudioProvidersFactory.h", 230 "android/hardware/bluetooth/audio/2.2/types.h", 231 )) { 232 system("mkdir -p $INCDIR". dirname($_)); 233 system("touch $INCDIR/$_"); 234 } 235 my @incs = ( 236 $INCDIR, 237 $LIBCHROME, 238 ".", 239 "audio_hal_interface/", 240 "include/", 241 "stack/include/", 242 "btif/include/", 243 "internal_include", 244 "osi/include/", 245 "test/mock/", 246 "types/", 247 ); 248 my @defs = ( 249 "HAS_NO_BDROID_BUILDCFG", 250 ); 251 252 my $link="test/mock/$hdr"; 253 unlink "$INCDIR/$link"; 254 symlink "$OUTDIR/$hdr", "$INCDIR/$link"; 255 system("$CC -c -std=c++17 -o /dev/null -D" . join(" -D", @defs) . " -I" . join(" -I", @incs) . " $OUTDIR/$src"); 256 my $rc = $?; 257 ($? == 0) 258 ? printf(STDERR "SUCCESS Compiled unit \'$src\'\n") 259 : compile_screen_failed($src); 260 return $rc; 261} 262 263### 264### Phase 4.1: Print the source compilation unit and the associated structues 265### 266sub print_source { 267 my $FH = shift @_; 268 print_copyright($FH); 269 print_generated_note($FH); 270 print_mock_decl_src($FH); 271 272 print_mock_header_include($FH); 273 print_usings($FH); 274 print_internal_structs($FH); 275 print_source_namespace_structs($FH); 276 print_static_return_values($FH); 277 print_mocked_functions($FH); 278 279 print $FH "// END mockcify generation\n"; 280} 281 282### 283### Phase 4.2 Print the header unit to be included with the test 284### 285sub print_header { 286 my $FH = shift @_; 287 print_copyright($FH); 288 print_pragma($FH); 289 print_generated_note($FH); 290 print_mock_decl_hdr($FH); 291 292 print_includes($FH); 293 print_usings($FH); 294 print_defs($FH); 295 print_header_test_mock_namespace_structs($FH); 296 print $FH "// END mockcify generation"; 297} 298 299sub get_function_param_names { 300 my $name = shift @_; 301 my @param_names; 302 foreach (0..$#{$function_param_names{$name}}) { 303 my $param_name = $function_param_names{$name}[$_]; 304 my $param_type = $function_param_types{$name}[$_]; 305 306 if ($param_type =~ /unique_ptr/) { 307 ## Wrap name in a move operation 308 push(@param_names, "std::move($param_name)"); 309 } else { 310 push(@param_names, $param_name); 311 } 312 } 313 return join(',', @param_names); 314} 315 316## 317## Parse a function signature into 4 basic components and insert into 318## the global hashes and arrays. 319## 1. @function return type 320## 2. @function name 321## 3. %param types 322## 4. %param names 323## 324sub parse_function_into_components { 325 my $function = shift @_; 326 ## Ensure this is really a function string 327 assert(substr $function, -1 eq ')'); 328 329 ## Split on first occurrence of open paren to get return 330 ## type and name of function 331 my ($return_type_and_name, $params) = split '\(', $function, 2; 332 if (!defined($params)) { 333 printf(STDERR "WARNING \'params\' is undefined \"$params\" function:\'$function\'\n"); 334 return 0; 335 } 336 ## Remove input params closing paren 337 $params=~ s/\).*$//; 338 339 ## Parse the return type and function name 340 my ($return_type, $name) = $return_type_and_name =~ /(.*)\s(.*)/; 341 342 if (!defined($name)) { 343 printf(STDERR "WARNING \'name\' is undefined \"$return_type_and_name\" a [con|des]tructor ?\n"); 344 return 0; 345 } 346 if ($name =~ /::/) { 347 printf(STDERR "WARNING \'name\' is unhandled class method \'$name\'\n"); 348 return 0; 349 } 350 351 ## Store away complete function signature 352 $function_signature{$name} = $function; 353 354 ## Store away the parameter type and names 355 chomp($params); 356 $function_params{$name} = $params; 357 358 ## Parse the parameter types and names 359 my @param_types; 360 my @param_names; 361 362 ## Skip when void keyword used for no parameters 363 if ($params ne "void") { 364 foreach (split ',', $params) { 365 s/^\s+//; 366 if (/\(/) { 367 ## TODO Parameter is a C style function 368 my @vars; 369 my @f = split /[\(\)]/; 370 push(@vars, substr $f[1], 1); 371 } else { 372 ## Store the type and name 373 my ($type, $name) = /(.*)\s(.*)/; 374 push(@param_names, $name); 375 push(@param_types, $type); 376 } 377 } 378 } 379 push(@function_names, $name); 380 $function_return_types{$name} = $return_type; 381 $function_param_types{$name} = \@param_types; 382 $function_param_names{$name} = \@param_names; 383 return 1; 384} 385 386## 387## Read a file from stdin and does a first pass simple 388## filtering that removes single lines. 389## 390sub read_stdin_and_filter_file { 391 my @filtered_lines; 392 my @clang_format=clang_format(); 393 foreach (@clang_format) { 394 ## Update header guards with compiler #pragma for proper 395 ## decision processing of header or source 396 s/^#ifndef [A-Z_0-9]+_H/#pragma once/; 397 398 unless (/^extern/ 399 or /^#define / 400 or / = \{/ 401 or /^#if / 402 or /^constexpr/ 403 or /^#ifdef/ 404 or /^#ifndef/ 405 or /^#else/ 406 or /^enum/ 407 or /^static.*;$/ 408 or /^#endif/) { 409 ## Remove any single line C style comments 410 s:/\*.*\*/::; 411 push(@filtered_lines, $_); 412 } 413 } 414 return join('', @filtered_lines); 415} 416 417sub filter_lines { 418 $_ = shift @_; 419 ## Remove anonymous namespaces 420 ## $text =~ s/namespace \{.*\n\} \/\/ namespace/\n/sg; 421 s/namespace \{.*\n\} \/\/ namespace?/\n/sg; 422 s/namespace \{.?\n\}/\n/g; 423 ## Remove C style comments 424 s/\s*\/\*(?:(?!\*\/).)*\*\/\n?/\n/sg; 425 ## Remove Cpp style comments 426 s/\s*\/\/.*//g; 427 ## Remove unnecessary bluetooth osi specific modifier 428 s/UNUSED_ATTR//g; 429 ## Modify internally defined structure typedefs 430 s/typedef struct \{.*?\n\} (\w+);/typedef struct $MOCKCIFY_BRACKET_GROUP $1;/sg; 431 ## Modify internally defined structure typedefs 432 s/typedef struct (\w+) \{.*?\n\} (\w+);/struct $1 $MOCKCIFY_BRACKET_GROUP;/sg; 433 ## Modify internally defined structures 434 s/struct (\w+) \{.*?\n\};/struct $1 $MOCKCIFY_BRACKET_GROUP;/sg; 435 ## Remove lines only with spaces 436 s/^\s+$//sg; 437 return $_; 438} 439 440sub parse_info { 441 if (/\n#pragma once\n/) { 442 return parse_info_header(shift @_); 443 } else { 444 return parse_info_source(shift @_); 445 } 446} 447 448sub parse_info_header { 449 my (@includes, @typedefs, @functions, @usings, @namespaces); 450 foreach (split('\n')) { 451 chomp(); 452 if (/^ /) { 453 } elsif (/^#include /) { 454 push(@includes, $_); 455 } elsif (/^typedef /) { 456 push @typedefs, $_; 457 } elsif ($_ =~ /^ *$/) { 458 # Skip function body indicated by indentation 459 } elsif ($_ =~ /^}/) { 460 # Skip function curly bracket closure 461 } elsif (/^namespace/) { 462 push @namespaces, $_; 463 } elsif (/\(/) { 464 # Add function signature 465 chomp(); 466 ## Remove all function body after signature 467 s/{.*$//; 468 ## Remove whitespace on both ends 469 s/^\s+|\s+$//g; 470 ## Ignore locally linked functions 471 next if (/^static/); 472 ## Reduce all remaining whitespace to a single space 473 s/\s+/ /g; 474 ## Remove any semi colons 475 s/;//g; 476 push(@functions, "$_\n"); 477 } else { 478 # Not a function. skip 479 } 480 } 481 printf(STDERR "Parsed HEADER lines includes:%d typedefs:%d functions:%d\n", 482 scalar(@includes), scalar(@typedefs), scalar(@functions)); 483 return (\@includes, \@typedefs, \@functions, \@usings, \@namespaces); 484} 485 486sub parse_info_source{ 487 my @s = split('\n', $_); 488 my (@includes, @typedefs, @functions, @usings, @namespaces); 489 foreach (@s) { 490 chomp(); 491 if (/^ /) { 492 } elsif (/^#include /) { 493 push @includes, $_; 494 } elsif (/^typedef /) { 495 push @typedefs, $_; 496 } elsif (/^using /) { 497 push @usings, $_; 498 } elsif (/^namespace/) { 499 push @namespaces, $_; 500 } elsif ($_ =~ /^ *$/) { 501 # Skip function body indicated by indentation 502 } elsif ($_ =~ /^}/) { 503 # Skip function curly bracket closure 504 } elsif (/\{/) { 505 # Add function signature 506 chomp(); 507 ## Remove all function body after signature 508 s/{.*$//; 509 ## Remove whitespace on both ends 510 s/^\s+|\s+$//g; 511 ## Ignore locally linked functions 512 next if (/^static/); 513 ## Reduce all remaining whitespace to a single space 514 s/\s+/ /g; 515 push(@functions, "$_\n"); 516 } else { 517 # Not a function. skip 518 } 519 } 520 printf(STDERR "Parsed SOURCE lines includes:%d typedefs:%d functions:%d\n", 521 scalar(@includes), scalar(@typedefs), scalar(@functions)); 522 return (\@includes, \@typedefs, \@functions, \@usings, \@namespaces); 523} 524 525## Returns the default type specified by the function return type. 526## These are processed in priority order. 527sub get_default_return_value_from_type { 528 $_ = shift @_; 529 assert($_ ne ''); 530 if (/^bool/) { 531 return "false"; 532 } elsif (/\*$/ or /^std::unique_ptr/ or /^std::shared_ptr/) { ## Pointer return val 533 return "nullptr"; 534 } elsif (/^void/) { 535 return ""; 536 } elsif (/^std::string/) { 537 return "std::string()"; 538 } elsif (/^std::list\<entry_t\>::iterator/) { 539 return "static std::list<entry_t> v"; 540 } elsif (/^std::list\<section_t\>::iterator/) { 541 return "std::list<section_t>"; 542 } elsif (/reactor_status_t/) { 543 return "REACTOR_STATUS_DONE"; 544 } elsif (/tL2CAP_LE_RESULT_CODE/) { 545 return "L2CAP_LE_RESULT_CONN_OK"; 546 } elsif (/std::vector/) { 547 return "retval"; 548 } elsif (/tBT_TRANSPORT/) { 549 return "BT_TRANSPORT_BR_EDR"; 550 } elsif (/tSDP_STATUS/) { 551 return "SDP_SUCCESS"; 552 } elsif (/tGATT_STATUS/) { 553 return "GATT_SUCCESS"; 554 } elsif (/tHID_STATUS/) { 555 return "HID_SUCCESS"; 556 } elsif (/future_t\*/) { 557 return "FUTURE_FAIL"; 558 } elsif(/bt_status_t/) { 559 return "BT_STATUS_SUCCESS"; 560 } elsif(/.*module_t\*/) { 561 return "nullptr"; 562 } elsif(/btav_a2dp_codec_index_t/) { 563 return "BTAV_A2DP_CODEC_INDEX_SOURCE_MIN"; 564 } else { 565 ## Decay to int type 566 return "0"; 567 } 568} 569 570## 571## Various print output boilerplate 572### 573sub print_copyright { 574 my $FH = shift @_; 575print $FH <<EOF 576/* 577 * Copyright $YEAR The Android Open Source Project 578 * 579 * Licensed under the Apache License, Version 2.0 (the "License"); 580 * you may not use this file except in compliance with the License. 581 * You may obtain a copy of the License at 582 * 583 * http://www.apache.org/licenses/LICENSE-2.0 584 * 585 * Unless required by applicable law or agreed to in writing, software 586 * distributed under the License is distributed on an "AS IS" BASIS, 587 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 588 * See the License for the specific language governing permissions and 589 * limitations under the License. 590 */ 591EOF 592} 593 594## Print body of each function 595sub print_mocked_functions { 596 my $FH = shift @_; 597 print $FH <<EOF; 598// Mocked functions, if any 599EOF 600 foreach my $name (sort @function_names) { 601 my $return_type = $function_return_types{$name}; 602 assert($return_type ne ''); 603 604 my $return_keyword = $return_type eq "void" ? "" : "return"; 605 my $function_param_names = get_function_param_names($name); 606 607 print $FH <<EOF; 608$function_signature{$name} { 609 inc_func_call_count(__func__); 610 ${return_keyword} test::mock::${namespace}::${name}($function_param_names); 611} 612EOF 613 } 614 print $FH <<EOF; 615// Mocked functions complete 616EOF 617} 618 619sub print_static_return_values { 620 my $FH = shift @_; 621 print $FH <<EOF; 622// Mocked function return values, if any 623namespace test { 624namespace mock { 625namespace $namespace { 626 627EOF 628 foreach my $name (sort @function_names) { 629 $name =~ s/\s+$//; 630 my $return_type = $function_return_types{$name}; 631 assert($return_type ne ''); 632 633 next if ($return_type eq "void"); 634 my $default_return_value = get_default_return_value_from_type($return_type); 635 print $FH "${return_type} ${name}::return_value = ${default_return_value};\n"; 636 } 637 print $FH <<EOF; 638 639} // namespace $namespace 640} // namespace mock 641} // namespace test 642 643EOF 644} 645 646## 647## Collection of mocked functions 648sub print_source_namespace_structs { 649 my $FH = shift @_; 650 print $FH <<EOF; 651namespace test { 652namespace mock { 653namespace $namespace { 654 655// Function state capture and return values, if needed 656EOF 657 foreach my $name (sort @function_names) { 658 print $FH "struct $name $name;\n"; 659 } 660 print $FH <<EOF; 661 662} // namespace $namespace 663} // namespace mock 664} // namespace test 665 666EOF 667} 668 669## 670## Print the definitions of the various structures for the header files 671## 672sub print_header_test_mock_namespace_structs { 673 my $FH = shift @_; 674 print $FH <<EOF; 675namespace test { 676namespace mock { 677namespace $namespace { 678 679// Shared state between mocked functions and tests 680EOF 681 foreach my $name (sort @function_names) { 682 my $input_params = $function_params{$name}; 683 my $return_type = $function_return_types{$name}; 684 my @param_names = $function_param_names{$name}; 685 assert($return_type ne ''); 686 687 my $function_param_names = get_function_param_names($name); 688 my $return_keyword = $return_type eq "void" ? "" : "return"; 689 my $return_statement = $return_type eq "void" ? "" : "return return_value;"; 690 my $return_definition = $return_type eq "void" ? "" : "static $return_type return_value;"; 691 692print $FH <<EOF; 693// Name: $name 694// Params: $input_params 695// Return: $return_type 696struct $name { 697EOF 698 if ($return_definition ne "") { 699 print $FH "$return_definition\n"; 700 } 701print $FH <<EOF; 702 std::function<$return_type($input_params)> body{[]($input_params){$return_statement}}; 703 $return_type operator()($input_params) { ${return_keyword} body($function_param_names);}; 704}; 705extern struct $name $name; 706 707EOF 708 } 709print $FH <<EOF; 710} // namespace $namespace 711} // namespace mock 712} // namespace test 713 714EOF 715} 716 717sub print_pragma { 718 my $FH = shift @_; 719print $FH <<EOF 720#pragma once 721 722EOF 723} 724 725sub print_generated_note { 726 my $FH = shift @_; 727 my $gen = scalar(@functions); 728print $FH <<EOF; 729/* 730 * Generated mock file from original source file 731 * Functions generated:$gen 732 * 733 * mockcify.pl ver $VERSION 734 */ 735 736EOF 737} 738 739sub print_usings { 740 my $FH = shift @_; 741print $FH <<EOF; 742// Original usings 743EOF 744 foreach (sort @usings) { 745 print $FH $_, "\n"; 746 } 747 print($FH "\n");; 748} 749 750sub print_includes { 751 my $FH = shift @_; 752 print $FH <<EOF; 753// Original included files, if any 754// NOTE: Since this is a mock file with mock definitions some number of 755// include files may not be required. The include-what-you-use 756// still applies, but crafting proper inclusion is out of scope 757// for this effort. This compilation unit may compile as-is, or 758// may need attention to prune from (or add to ) the inclusion set. 759EOF 760 foreach (sort @includes) { 761 print $FH $_, "\n"; 762 } 763 print($FH "\n");; 764} 765 766sub print_mock_header_include { 767 my $FH = shift @_; 768 print $FH <<EOF; 769// Mock include file to share data between tests and mock 770#include "test/mock/mock_${namespace}.h" 771 772EOF 773} 774 775sub print_mock_decl_hdr { 776 my $FH = shift @_; 777print $FH <<EOF; 778#include <cstdint> 779#include <functional> 780#include <map> 781#include <string> 782 783#include "test/common/mock_functions.h" 784 785EOF 786} 787 788sub print_mock_decl_src { 789 my $FH = shift @_; 790print $FH <<EOF; 791#include <cstdint> 792#include <functional> 793#include <map> 794#include <string> 795 796EOF 797} 798 799sub print_defs { 800 my $FH = shift @_; 801 print $FH <<EOF; 802// Mocked compile conditionals, if any 803 804EOF 805} 806 807sub print_internal_structs { 808 my $FH = shift @_; 809 print $FH <<EOF; 810// Mocked internal structures, if any 811EOF 812 813 foreach (sort @structs) { 814 print $FH $_,"\n"}; 815 print $FH "\n"; 816} 817 818sub assert { 819 my ($condition, $msg) = @_; 820 return if $condition; 821 if (!$msg) { 822 my ($pkg, $file, $line) = caller(0); 823 open my $fh, "<", $file; 824 my @lines = <$fh>; 825 close $fh; 826 $msg = "$file:$line: " . $lines[$line - 1]; 827 } 828 die "Assertion failed: $msg"; 829} 830