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