From fad0f53dc8a21b61dc5ff011d836f5fd5fdab0c3 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 15 Aug 2012 14:07:35 +0100 Subject: [PATCH] New internal APIs: internal-test-set-output, internal-test-close-output. These internal (testing) APIs allow the bindtests output to be sent to some other place than stdout. This is necessary for Erlang, since stdout is used to communicate with the Erlang interpreter. --- generator/generator_actions.ml | 32 +++++++++- generator/generator_bindtests.ml | 100 +++++++++++++++++++++++-------- src/guestfs-internal.h | 3 + src/guestfs.c | 4 ++ 4 files changed, 114 insertions(+), 25 deletions(-) diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index 7db88ceac..f370b7957 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -83,7 +83,8 @@ This is an internal test function which is used to test whether the automatically generated bindings can handle every possible parameter type correctly. -It echos the contents of each parameter to stdout. +It echos the contents of each parameter to stdout (by default) +or to a file (if C was called). You probably don't want to call this function." } ] @ List.flatten ( @@ -125,6 +126,35 @@ You probably don't want to call this function." } *) let non_daemon_functions = test_functions @ [ + { defaults with + name = "internal_test_set_output"; + style = RErr, [String "filename"], []; + in_fish = false; in_docs = false; + shortdesc = "internal test function - do not use"; + longdesc = "\ +This is an internal test function which is used to test whether +the automatically generated bindings can handle every possible +parameter type correctly. + +It sets the output file used by C. + +You probably don't want to call this function." }; + + { defaults with + name = "internal_test_close_output"; + style = RErr, [], []; + in_fish = false; in_docs = false; + shortdesc = "internal test function - do not use"; + longdesc = "\ +This is an internal test function which is used to test whether +the automatically generated bindings can handle every possible +parameter type correctly. + +It closes the output file previously opened by +C. + +You probably don't want to call this function." }; + { defaults with name = "launch"; style = RErr, [], []; diff --git a/generator/generator_bindtests.ml b/generator/generator_bindtests.ml index 67a5bbac4..aaadd370b 100644 --- a/generator/generator_bindtests.ml +++ b/generator/generator_bindtests.ml @@ -45,20 +45,68 @@ let rec generate_bindtests () = #include \"guestfs-internal-actions.h\" #include \"guestfs_protocol.h\" -static void -print_strings (char *const *argv) +int +guestfs__internal_test_set_output (guestfs_h *g, const char *filename) { - size_t argc; + FILE *fp; - printf (\"[\"); - for (argc = 0; argv[argc] != NULL; ++argc) { - if (argc > 0) printf (\", \"); - printf (\"\\\"%%s\\\"\", argv[argc]); + fp = fopen (filename, \"w\"); + if (fp == NULL) { + perrorf (g, \"cannot open output file %%s\\n\", filename); + return -1; } - printf (\"]\\n\"); + + if (guestfs_internal_test_close_output (g) == -1) { + fclose (fp); + return -1; + } + + g->test_fp = fp; + + return 0; } -/* The internal_test function prints its parameters to stdout. */ +int +guestfs__internal_test_close_output (guestfs_h *g) +{ + if (g->test_fp != NULL) { + if (fclose (g->test_fp) == EOF) { + perrorf (g, \"fclose\"); + g->test_fp = NULL; + return -1; + } + g->test_fp = NULL; + } + + return 0; +} + +static inline FILE * +get_fp (guestfs_h *g) +{ + if (g->test_fp) + return g->test_fp; + else + return stdout; +} + +static void +print_strings (guestfs_h *g, char *const *argv) +{ + FILE *fp = get_fp (g); + size_t argc; + + fprintf (fp, \"[\"); + for (argc = 0; argv[argc] != NULL; ++argc) { + if (argc > 0) fprintf (fp, \", \"); + fprintf (fp, \"\\\"%%s\\\"\", argv[argc]); + } + fprintf (fp, \"]\\n\"); +} + +/* The internal_test function prints its parameters to stdout or the + * file set by internal_test_set_output. + */ "; let test, tests = @@ -71,6 +119,9 @@ print_strings (char *const *argv) generate_prototype ~extern:false ~semicolon:false ~newline:true ~handle:"g" ~prefix:"guestfs__" ~optarg_proto:Argv name style; pr "{\n"; + pr " FILE *fp = get_fp (g);\n"; + pr "\n"; + List.iter ( function | Pathname n @@ -78,28 +129,29 @@ print_strings (char *const *argv) | String n | FileIn n | FileOut n - | Key n -> pr " printf (\"%%s\\n\", %s);\n" n + | Key n -> pr " fprintf (fp, \"%%s\\n\", %s);\n" n | BufferIn n -> pr " {\n"; pr " size_t i;\n"; pr " for (i = 0; i < %s_size; ++i)\n" n; - pr " printf (\"<%%02x>\", %s[i]);\n" n; - pr " printf (\"\\n\");\n"; + pr " fprintf (fp, \"<%%02x>\", %s[i]);\n" n; + pr " fprintf (fp, \"\\n\");\n"; pr " }\n"; - | OptString n -> pr " printf (\"%%s\\n\", %s ? %s : \"null\");\n" n n - | StringList n | DeviceList n -> pr " print_strings (%s);\n" n - | Bool n -> pr " printf (\"%%s\\n\", %s ? \"true\" : \"false\");\n" n - | Int n -> pr " printf (\"%%d\\n\", %s);\n" n - | Int64 n -> pr " printf (\"%%\" PRIi64 \"\\n\", %s);\n" n + | OptString n -> pr " fprintf (fp, \"%%s\\n\", %s ? %s : \"null\");\n" n n + | StringList n | DeviceList n -> pr " print_strings (g, %s);\n" n + | Bool n -> pr " fprintf (fp, \"%%s\\n\", %s ? \"true\" : \"false\");\n" n + | Int n -> pr " fprintf (fp, \"%%d\\n\", %s);\n" n + | Int64 n -> pr " fprintf (fp, \"%%\" PRIi64 \"\\n\", %s);\n" n | Pointer _ -> assert false ) args; + let check_optarg n printf_args = - pr " printf (\"%s: \");\n" n; + pr " fprintf (fp, \"%s: \");\n" n; pr " if (optargs->bitmask & GUESTFS_INTERNAL_TEST_%s_BITMASK) {\n" (String.uppercase n); - pr " printf (%s);\n" printf_args; + pr " fprintf (fp, %s);\n" printf_args; pr " } else {\n"; - pr " printf (\"unset\\n\");\n"; + pr " fprintf (fp, \"unset\\n\");\n"; pr " }\n"; in List.iter ( @@ -118,16 +170,16 @@ print_strings (char *const *argv) let printf_args = sprintf "\"%%s\\n\", optargs->%s" n in check_optarg n printf_args; | OStringList n -> - pr " printf (\"%s: \");\n" n; + pr " fprintf (fp, \"%s: \");\n" n; pr " if (optargs->bitmask & GUESTFS_INTERNAL_TEST_%s_BITMASK) {\n" (String.uppercase n); - pr " print_strings (optargs->%s);\n" n; + pr " print_strings (g, optargs->%s);\n" n; pr " } else {\n"; - pr " printf (\"unset\\n\");\n"; + pr " fprintf (fp, \"unset\\n\");\n"; pr " }\n"; ) optargs; pr " /* Java changes stdout line buffering so we need this: */\n"; - pr " fflush (stdout);\n"; + pr " fflush (fp);\n"; pr " return 0;\n"; pr "}\n"; pr "\n" in diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 37526063e..dc6f01a2f 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -245,6 +245,9 @@ struct guestfs_h struct timeval launch_t; /* The time that we called guestfs_launch. */ + /* Used by bindtests. */ + FILE *test_fp; + /*** Protocol. ***/ int fd[2]; /* Stdin/stdout of qemu. */ int sock; /* Daemon communications socket. */ diff --git a/src/guestfs.c b/src/guestfs.c index b5874a883..c50625755 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -270,6 +270,10 @@ guestfs_close (guestfs_h *g) /* Remove whole temporary directory. */ guestfs___remove_tmpdir (g->tmpdir); + /* Test output file used by bindtests. */ + if (g->test_fp != NULL) + fclose (g->test_fp); + /* Mark the handle as dead and then free up all memory. */ g->state = NO_HANDLE;