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      push(@incs, $_);
214    }
215    return @incs;
216}
217
218sub compile_screen_failed {
219    my $src = shift @_;
220    print STDERR <<EOF
221    MOCK Compilation is EXPERIMENTAL ONLY
222
223    ERROR Failed to compile \'$src\' NOTE: This does not mean
224    the mock is unusable as the tool only screens the compilation.
225
226    There could be one of 3 problems:
227    1. Undeclared external surface or dependency
228    2. C++ code or namespaces mixed in with C code
229    3. An issue with proper mock'ing with mockcify.
230EOF
231}
232
233sub compilation_screen {
234    my $base= shift @_;
235    my $src=$base . ".cc";
236    my $hdr=$base . ".h";
237
238    ## Verious external or generated header not needed for mocks
239    foreach((
240            "test/mock/mock.h",
241            "src/message_loop_thread.rs.h",
242            "android/hardware/bluetooth/audio/2.2/IBluetoothAudioProvidersFactory.h",
243            "android/hardware/bluetooth/audio/2.2/types.h",
244        )) {
245        system("mkdir -p $INCDIR". dirname($_));
246        system("touch $INCDIR/$_");
247    }
248    my @incs = (
249        $INCDIR,
250        $LIBCHROME,
251        ".",
252        "audio_hal_interface/",
253        "include/",
254        "stack/include/",
255        "btif/include/",
256        "internal_include",
257        "osi/include/",
258        "test/mock/",
259        "types/",
260    );
261    ## Any additional compiler definitions that may be required
262    my @compiler_defs = (
263    );
264
265    my $link="test/mock/$hdr";
266    unlink "$INCDIR/$link";
267    symlink "$OUTDIR/$hdr", "$INCDIR/$link";
268    system("$CC -c -std=c++20 -o /dev/null -D" . join(" -D", @compiler_defs) . " -I" . join(" -I", @incs) . " $OUTDIR/$src");
269    my $rc = $?;
270         ($? == 0)
271         ? printf(STDERR "SUCCESS Compiled unit \'$src\'\n")
272         : compile_screen_failed($src);
273    return $rc;
274}
275
276###
277### Phase 4.1: Print the source compilation unit and the associated structues
278###
279sub print_source {
280  my $FH = shift @_;
281  print_copyright($FH);
282  print_generated_note($FH);
283
284  print_mock_header_include($FH);
285  print_mock_decl_src($FH);
286  print_usings($FH);
287  print_internal_structs($FH);
288  print_source_namespace_structs($FH);
289  print_static_return_values($FH);
290  print_mocked_functions($FH);
291
292  print $FH "// END mockcify generation\n";
293}
294
295###
296### Phase 4.2 Print the header unit to be included with the test
297###
298sub print_header {
299  my $FH = shift @_;
300  print_copyright($FH);
301  print_pragma($FH);
302  print_generated_note($FH);
303  print_mock_decl_hdr($FH);
304
305  print_includes($FH);
306  print_usings($FH);
307  print_defs($FH);
308  print_header_test_mock_namespace_structs($FH);
309  print $FH "// END mockcify generation";
310}
311
312sub get_function_param_names {
313    my $name = shift @_;
314    my @param_names;
315    foreach (0..$#{$function_param_names{$name}}) {
316        my $param_name = $function_param_names{$name}[$_];
317        my $param_type = $function_param_types{$name}[$_];
318
319        if (!defined($param_type)) {
320          printf(STDERR "Unable to find param type def for $name\n");
321          next;
322        }
323        if ($param_type =~ /unique_ptr/) {
324            ## Wrap name in a move operation
325            push(@param_names, "std::move($param_name)");
326        } else {
327            push(@param_names, $param_name);
328        }
329    }
330    return join(',', @param_names);
331}
332
333##
334## Parse a function signature into 4 basic components and insert into
335## the global hashes and arrays.
336##  1. @function return type
337##  2. @function name
338##  3. %param types
339##  4. %param names
340##
341sub parse_function_into_components {
342  my $function = shift @_;
343  ## Ensure this is really a function string
344  assert(substr $function, -1 eq ')');
345
346  ## Split on first occurrence of open paren to get return
347  ## type and name of function
348  my ($return_type_and_name, $params) = split '\(', $function, 2;
349  if (!defined($params)) {
350      printf(STDERR "WARNING \'params\' is undefined \"$params\" function:\'$function\'\n");
351      return 0;
352  }
353  ## Remove input params closing paren
354  $params=~ s/\).*$//;
355
356  ## Parse the return type and function name
357  my ($return_type, $name) = $return_type_and_name =~ /(.*)\s(.*)/;
358
359  if (!defined($name)) {
360      printf(STDERR "WARNING \'name\' is undefined \"$return_type_and_name\" a [con|des]tructor ?\n");
361      return 0;
362  }
363  if ($name =~ /::/) {
364      printf(STDERR "WARNING \'name\' is unhandled class method \'$name\'\n");
365      return 0;
366  }
367
368  ## Store away complete function signature
369  $function_signature{$name} = $function;
370
371  ## Store away the parameter type and names
372  chomp($params);
373  $function_params{$name} = $params;
374
375  ## Parse the parameter types and names
376  my @param_types;
377  my @param_names;
378
379  ## Skip when void keyword used for no parameters
380  if ($params ne "void") {
381    ## TODO Replace all comma types within angle brackets before split
382    foreach (split ',', $params) {
383      s/^\s+//;
384      if (/\(/) {
385        ## TODO Parameter is a C style function
386        my @vars;
387        my @f = split /[\(\)]/;
388        push(@vars, substr $f[1], 1);
389      } else {
390        ## Store the type and name
391        my ($type, $name) = /(.*)\s(.*)/;
392        push(@param_names, $name);
393        push(@param_types, $type);
394      }
395    }
396  }
397  push(@function_names, $name);
398  $function_return_types{$name} = $return_type;
399  $function_param_types{$name} = \@param_types;
400  $function_param_names{$name} = \@param_names;
401  return 1;
402}
403
404##
405## Read a file from stdin and does a first pass simple
406## filtering that removes single lines.
407##
408sub read_stdin_and_filter_file {
409  my @filtered_lines;
410  my @clang_format=clang_format();
411  foreach (@clang_format) {
412    ## Update header guards with compiler #pragma for proper
413    ## decision processing of header or source
414    s/^#ifndef [A-Z_0-9]+_H/#pragma once/;
415
416    unless (/^extern/
417        or /^#define /
418        or / = \{/
419        or /^#if /
420        or /^constexpr/
421        or /^#ifdef/
422        or /^#ifndef/
423        or /^#else/
424        or /^enum/
425        or /^static.*;$/
426        or /^#endif/) {
427        ## Remove any single line C style comments
428        s:/\*.*\*/::;
429        push(@filtered_lines, $_);
430      }
431  }
432  return join('', @filtered_lines);
433}
434
435sub filter_lines {
436  $_ = shift @_;
437  ## Remove anonymous namespaces
438  ## $text =~ s/namespace \{.*\n\} \/\/ namespace/\n/sg;
439  s/namespace \{.*\n\} \/\/ namespace?/\n/sg;
440  s/namespace \{.?\n\}/\n/g;
441  ## Remove C style comments
442  s/\s*\/\*(?:(?!\*\/).)*\*\/\n?/\n/sg;
443  ## Remove Cpp style comments
444  s/\s*\/\/.*//g;
445  ## Remove unnecessary bluetooth osi specific modifier
446  s/UNUSED_ATTR//g;
447  ## Modify internally defined structure typedefs
448  s/typedef struct \{.*?\n\} (\w+);/typedef struct $MOCKCIFY_BRACKET_GROUP $1;/sg;
449  ## Modify internally defined structure typedefs
450  s/typedef struct (\w+) \{.*?\n\} (\w+);/struct $1 $MOCKCIFY_BRACKET_GROUP;/sg;
451  ## Modify internally defined structures
452  s/struct (\w+) \{.*?\n\};/struct $1 $MOCKCIFY_BRACKET_GROUP;/sg;
453  ## Remove lines only with spaces
454  s/^\s+$//sg;
455  return $_;
456}
457
458sub parse_info {
459    if (/\n#pragma once\n/) {
460        return parse_info_header(shift @_);
461    } else {
462        return parse_info_source(shift @_);
463    }
464}
465
466sub parse_info_header {
467  my (@includes, @typedefs, @functions, @usings, @namespaces);
468  foreach (split('\n')) {
469      chomp();
470      if (/^ /) {
471      } elsif (/^#include /) {
472          push(@includes, $_);
473      } elsif (/^typedef /) {
474          push @typedefs, $_;
475      } elsif ($_ =~ /^ *$/) {
476          # Skip function body indicated by indentation
477      } elsif ($_ =~ /^}/) {
478          # Skip function curly bracket closure
479      } elsif (/^namespace/) {
480          push @namespaces, $_;
481      } elsif (/\(/) {
482          # Add function signature
483          chomp();
484          ## Remove all function body after signature
485          s/{.*$//;
486          ## Remove whitespace on both ends
487          s/^\s+|\s+$//g;
488          ## Ignore locally linked functions
489          next if (/^static/);
490          ## Reduce all remaining whitespace to a single space
491          s/\s+/ /g;
492          ## Remove any semi colons
493          s/;//g;
494          push(@functions, "$_\n");
495      } else {
496          # Not a function. skip
497      }
498  }
499  printf(STDERR "Parsed HEADER lines includes:%d typedefs:%d functions:%d\n",
500      scalar(@includes), scalar(@typedefs), scalar(@functions));
501  return (\@includes, \@typedefs, \@functions, \@usings, \@namespaces);
502}
503
504sub parse_info_source{
505  my @s = split('\n', $_);
506  my (@includes, @typedefs, @functions, @usings, @namespaces);
507  foreach (@s) {
508      chomp();
509      if (/^ /) {
510      } elsif (/^#include /) {
511          push @includes, $_;
512      } elsif (/^typedef /) {
513          push @typedefs, $_;
514      } elsif (/^using /) {
515          push @usings, $_;
516      } elsif (/^namespace/) {
517          push @namespaces, $_;
518      } elsif ($_ =~ /^ *$/) {
519          # Skip function body indicated by indentation
520      } elsif ($_ =~ /^}/) {
521          # Skip function curly bracket closure
522        } elsif (/\{/) {
523          # Add function signature
524          chomp();
525          ## Remove all function body after signature
526          s/{.*$//;
527          ## Remove whitespace on both ends
528          s/^\s+|\s+$//g;
529          ## Ignore locally linked functions
530          next if (/^static/);
531          ## Reduce all remaining whitespace to a single space
532          s/\s+/ /g;
533          push(@functions, "$_\n");
534      } else {
535          # Not a function. skip
536      }
537  }
538  printf(STDERR "Parsed SOURCE lines includes:%d typedefs:%d functions:%d\n",
539      scalar(@includes), scalar(@typedefs), scalar(@functions));
540  return (\@includes, \@typedefs, \@functions, \@usings, \@namespaces);
541}
542
543## Returns the default type specified by the function return type.
544## These are processed in priority order.
545sub get_default_return_value_from_type {
546  $_ = shift @_;
547  assert($_ ne '');
548  if (/^bool/) {
549    return "false";
550  } elsif (/\*$/ or /^std::unique_ptr/ or /^std::shared_ptr/) {  ## Pointer return val
551    return "nullptr";
552  } elsif (/^void/) {
553    return "";
554  } elsif (/^std::string/) {
555    return "std::string()";
556  } elsif (/^std::list\<entry_t\>::iterator/) {
557    return "static std::list<entry_t> v";
558  } elsif (/^std::list\<section_t\>::iterator/) {
559    return "std::list<section_t>";
560  } elsif (/reactor_status_t/) {
561    return "REACTOR_STATUS_DONE";
562  } elsif (/tL2CAP_LE_RESULT_CODE/) {
563    return "L2CAP_LE_RESULT_CONN_OK";
564  } elsif (/std::vector/) {
565    return "retval";
566  } elsif (/tBT_TRANSPORT/) {
567    return "BT_TRANSPORT_BR_EDR";
568  } elsif (/tSDP_STATUS/) {
569    return "SDP_SUCCESS";
570  } elsif (/tGATT_STATUS/) {
571    return "GATT_SUCCESS";
572  } elsif (/tHID_STATUS/) {
573    return "HID_SUCCESS";
574  } elsif (/future_t\*/) {
575    return "FUTURE_FAIL";
576  } elsif(/bt_status_t/) {
577    return "BT_STATUS_SUCCESS";
578  } elsif(/.*module_t\*/) {
579    return "nullptr";
580  } elsif(/btav_a2dp_codec_index_t/) {
581    return "BTAV_A2DP_CODEC_INDEX_SOURCE_MIN";
582  } elsif(/tBTA_SDP_STATUS/) {
583    return "BTA_SDP_SUCCESS";
584  } elsif(/tBTA_STATUS/) {
585    return "BTA_SUCCESS";
586  } elsif(/tBTA_JV_STATUS/) {
587    return "tBTA_JV_STATUS::SUCCESS";
588  } else {
589    ## Decay to int type
590    return "0";
591  }
592}
593
594##
595## Various print output boilerplate
596###
597sub print_copyright {
598  my $FH = shift @_;
599print $FH <<EOF
600/*
601 * Copyright $YEAR The Android Open Source Project
602 *
603 * Licensed under the Apache License, Version 2.0 (the "License");
604 * you may not use this file except in compliance with the License.
605 * You may obtain a copy of the License at
606 *
607 *      http://www.apache.org/licenses/LICENSE-2.0
608 *
609 * Unless required by applicable law or agreed to in writing, software
610 * distributed under the License is distributed on an "AS IS" BASIS,
611 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
612 * See the License for the specific language governing permissions and
613 * limitations under the License.
614 */
615EOF
616}
617
618## Print body of each function
619sub print_mocked_functions {
620  my $FH = shift @_;
621  print $FH <<EOF;
622// Mocked functions, if any
623EOF
624  foreach my $name (sort @function_names) {
625      my $return_type = $function_return_types{$name};
626      assert($return_type ne '');
627
628      my $return_keyword = $return_type eq "void" ? "" : "return";
629      my $function_param_names = get_function_param_names($name);
630
631      print $FH <<EOF;
632$function_signature{$name} {
633    inc_func_call_count(__func__);
634    ${return_keyword} test::mock::${namespace}::${name}($function_param_names);
635}
636EOF
637  }
638  print $FH <<EOF;
639// Mocked functions complete
640EOF
641}
642
643sub print_static_return_values {
644  my $FH = shift @_;
645  print $FH <<EOF;
646// Mocked function return values, if any
647namespace test {
648namespace mock {
649namespace $namespace {
650
651EOF
652  foreach my $name (sort @function_names) {
653      $name =~ s/\s+$//;
654      my $return_type = $function_return_types{$name};
655      assert($return_type ne '');
656
657      next if ($return_type eq "void");
658      my $default_return_value = get_default_return_value_from_type($return_type);
659      print $FH "${return_type} ${name}::return_value = ${default_return_value};\n";
660  }
661  print $FH <<EOF;
662
663} // namespace $namespace
664} // namespace mock
665} // namespace test
666
667EOF
668}
669
670##
671## Collection of mocked functions
672sub print_source_namespace_structs {
673  my $FH = shift @_;
674  print $FH <<EOF;
675namespace test {
676namespace mock {
677namespace $namespace {
678
679// Function state capture and return values, if needed
680EOF
681    foreach my $name (sort @function_names) {
682      print $FH "struct $name $name;\n";
683    }
684    print $FH <<EOF;
685
686} // namespace $namespace
687} // namespace mock
688} // namespace test
689
690EOF
691}
692
693##
694##  Print the definitions of the various structures for the header files
695##
696sub print_header_test_mock_namespace_structs {
697  my $FH = shift @_;
698  print $FH <<EOF;
699namespace test {
700namespace mock {
701namespace $namespace {
702
703// Shared state between mocked functions and tests
704EOF
705  foreach my $name (sort @function_names) {
706      my $input_params = $function_params{$name};
707      my $vars_commented_out_input_params = comment_out_input_vars($input_params);
708      my $return_type = $function_return_types{$name};
709      my @param_names = $function_param_names{$name};
710      assert($return_type ne '');
711
712      my $function_param_names = get_function_param_names($name);
713      my $return_keyword = $return_type eq "void" ? "" : "return";
714      my $return_statement = $return_type eq "void" ? "" : "return return_value;";
715      my $return_definition = $return_type eq "void" ? "" : "static $return_type return_value;";
716
717print $FH <<EOF;
718// Name: $name
719// Params: $input_params
720// Return: $return_type
721struct $name {
722EOF
723       if ($return_definition ne "") {
724           print $FH "$return_definition\n";
725       }
726print $FH <<EOF;
727    std::function<$return_type($input_params)> body{[]($vars_commented_out_input_params){$return_statement}};
728    $return_type operator()($input_params) { ${return_keyword} body($function_param_names);};
729};
730extern struct $name $name;
731
732EOF
733    }
734print $FH <<EOF;
735} // namespace $namespace
736} // namespace mock
737} // namespace test
738
739EOF
740}
741
742sub print_pragma {
743  my $FH = shift @_;
744print $FH <<EOF
745#pragma once
746
747EOF
748}
749
750sub print_generated_note {
751  my $FH = shift @_;
752  my $gen = scalar(@functions);
753print $FH <<EOF;
754/*
755 * Generated mock file from original source file
756 *   Functions generated:$gen
757 *
758 *  mockcify.pl ver $VERSION
759 */
760
761EOF
762}
763
764sub print_usings {
765  my $FH = shift @_;
766print $FH <<EOF;
767// Original usings
768EOF
769  foreach (sort @usings) {
770    print $FH $_, "\n";
771  }
772  print($FH "\n");;
773}
774
775sub print_includes {
776  my $FH = shift @_;
777  print $FH <<EOF;
778// Original included files, if any
779// NOTE: Since this is a mock file with mock definitions some number of
780//       include files may not be required.  The include-what-you-use
781//       still applies, but crafting proper inclusion is out of scope
782//       for this effort.  This compilation unit may compile as-is, or
783//       may need attention to prune from (or add to ) the inclusion set.
784EOF
785  foreach (sort @includes) {
786    print $FH $_, "\n";
787  }
788  print($FH "\n");;
789}
790
791sub print_mock_header_include {
792  my $FH = shift @_;
793  print $FH <<EOF;
794// Mock include file to share data between tests and mock
795#include "test/mock/mock_${namespace}.h"
796
797EOF
798}
799
800sub print_mock_decl_hdr {
801  my $FH = shift @_;
802print $FH <<EOF;
803#include <cstdint>
804#include <functional>
805
806EOF
807}
808
809sub print_mock_decl_src {
810  my $FH = shift @_;
811print $FH <<EOF;
812#include <cstdint>
813
814#include "test/common/mock_functions.h"
815
816EOF
817}
818
819sub print_defs {
820  my $FH = shift @_;
821  print $FH <<EOF;
822// Mocked compile conditionals, if any
823
824EOF
825}
826
827sub print_internal_structs {
828  my $FH = shift @_;
829  print $FH <<EOF;
830// Mocked internal structures, if any
831EOF
832
833  foreach (sort @structs) {
834    print $FH $_,"\n"};
835  print $FH "\n";
836}
837
838sub assert {
839    my ($condition, $msg) = @_;
840    return if $condition;
841    if (!$msg) {
842        my ($pkg, $file, $line) = caller(0);
843        open my $fh, "<", $file;
844        my @lines = <$fh>;
845        close $fh;
846        $msg = "$file:$line: " . $lines[$line - 1];
847    }
848    die "Assertion failed: $msg";
849}
850