Add a function to properly write virt-builder source index entries.
Note that this function is very similar to Index.print_entry that is
meant for debugging purposes.
---
.gitignore | 1 +
builder/Makefile.am | 36 +++++++++++-
builder/index.mli | 3 +
builder/index_parser.ml | 46 +++++++++++++++
builder/index_parser.mli | 4 ++
builder/index_parser_tests.ml | 130 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 218 insertions(+), 2 deletions(-)
create mode 100644 builder/index_parser_tests.ml
diff --git a/.gitignore b/.gitignore
index 59bf52f2b..30165d59e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -108,6 +108,7 @@ Makefile.in
/builder/virt-index-validate
/builder/virt-index-validate.1
/builder/*.xz
+/builder/index_parser_tests
/builder/yajl_tests
/cat/stamp-virt-*.pod
/cat/virt-cat
diff --git a/builder/Makefile.am b/builder/Makefile.am
index 88392d327..e4a347a09 100644
--- a/builder/Makefile.am
+++ b/builder/Makefile.am
@@ -239,13 +239,36 @@ yajl_tests_BOBJECTS = \
yajl_tests.cmo
yajl_tests_XOBJECTS = $(yajl_tests_BOBJECTS:.cmo=.cmx)
+index_parser_tests_SOURCES = \
+ index-scan.c \
+ index-struct.c \
+ index-parser-c.c \
+ index-parse.c
+index_parser_tests_CPPFLAGS = $(virt_builder_CPPFLAGS)
+index_parser_tests_BOBJECTS = \
+ utils.cmo \
+ cache.cmo \
+ downloader.cmo \
+ sigchecker.cmo \
+ index.cmo \
+ ini_reader.cmo \
+ index_parser.cmo \
+ index_parser_tests.cmo
+index_parser_tests_XOBJECTS = $(index_parser_tests_BOBJECTS:.cmo=.cmx)
+
# Can't call the following as <test>_OBJECTS because automake gets confused.
if HAVE_OCAMLOPT
yajl_tests_THEOBJECTS = $(yajl_tests_XOBJECTS)
yajl_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS)
+
+index_parser_tests_THEOBJECTS = $(index_parser_tests_XOBJECTS)
+index_parser_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS)
else
yajl_tests_THEOBJECTS = $(yajl_tests_BOBJECTS)
yajl_tests.cmo: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS)
+
+index_parser_tests_THEOBJECTS = $(index_parser_tests_BOBJECTS)
+index_parser_tests.cmo: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS)
endif
yajl_tests_DEPENDENCIES = \
@@ -261,6 +284,15 @@ yajl_tests_LINK = \
$(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS)
$(OCAMLLINKFLAGS) \
$(yajl_tests_THEOBJECTS) -o $@
+index_parser_tests_DEPENDENCIES = \
+ $(index_parser_tests_THEOBJECTS) \
+ ../common/mltools/mltools.$(MLARCHIVE) \
+ $(top_srcdir)/ocaml-link.sh
+index_parser_tests_LINK = \
+ $(top_srcdir)/ocaml-link.sh -cclib '$(OCAMLCLIBS)' -- \
+ $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS)
$(OCAMLLINKFLAGS) \
+ $(index_parser_tests_THEOBJECTS) -o $@
+
TESTS = \
test-docs.sh \
test-virt-builder-list.sh \
@@ -274,8 +306,8 @@ if ENABLE_APPLIANCE
TESTS += test-virt-builder.sh
endif ENABLE_APPLIANCE
if HAVE_OCAML_PKG_OUNIT
-check_PROGRAMS += yajl_tests
-TESTS += yajl_tests
+check_PROGRAMS += yajl_tests index_parser_tests
+TESTS += yajl_tests index_parser_tests
endif
check-valgrind:
diff --git a/builder/index.mli b/builder/index.mli
index 43d5485fb..3ed633ddc 100644
--- a/builder/index.mli
+++ b/builder/index.mli
@@ -44,3 +44,6 @@ and entry = {
}
val print_entry : out_channel -> (string * entry) -> unit
+(** Debugging helper function dumping an index entry to a stream.
+ To write entries for non-debugging purpose, use the
+ [Index_parser.write_entry] function. *)
diff --git a/builder/index_parser.ml b/builder/index_parser.ml
index 7f64d0d98..0fc3ecc06 100644
--- a/builder/index_parser.ml
+++ b/builder/index_parser.ml
@@ -256,3 +256,49 @@ let get_index ~downloader ~sigchecker ?(template = false) {
Sources.uri; proxy }
in
get_index ()
+
+let write_entry chan (name, { Index.printable_name; file_uri; arch; osinfo;
+ signature_uri; checksums; revision; format; size;
+ compressed_size; expand; lvexpand; notes;
+ aliases; hidden}) =
+ let fp fs = fprintf chan fs in
+ fp "[%s]\n" name;
+ Option.may (fp "name=%s\n") printable_name;
+ Option.may (fp "osinfo=%s\n") osinfo;
+ fp "file=%s\n" file_uri;
+ match arch with
+ | Either arch
+ | Or Some arch -> fp "arch=%s\n" arch
+ | Or None -> ();
+ Option.may (fp "sig=%s\n") signature_uri;
+ (match checksums with
+ | None -> ()
+ | Some checksums ->
+ List.iter (
+ fun c ->
+ fp "checksum[%s]=%s\n"
+ (Checksums.string_of_csum_t c) (Checksums.string_of_csum c)
+ ) checksums
+ );
+ fp "revision=%s\n" (string_of_revision revision);
+ Option.may (fp "format=%s\n") format;
+ fp "size=%Ld\n" size;
+ Option.may (fp "compressed_size=%Ld\n") compressed_size;
+ Option.may (fp "expand=%s\n") expand;
+ Option.may (fp "lvexpand=%s\n") lvexpand;
+
+ let format_notes notes =
+ String.concat "\n " (String.nsplit "\n" notes) in
+
+ List.iter (
+ fun (lang, notes) ->
+ match lang with
+ | "" -> fp "notes=%s\n" (format_notes notes)
+ | lang -> fp "notes[%s]=%s\n" lang (format_notes notes)
+ ) notes;
+ (match aliases with
+ | None -> ()
+ | Some l -> fp "aliases=%s\n" (String.concat " " l)
+ );
+ if hidden then fp "hidden=true\n";
+ fp "\n"
diff --git a/builder/index_parser.mli b/builder/index_parser.mli
index 324f4fc5a..dc6b0b407 100644
--- a/builder/index_parser.mli
+++ b/builder/index_parser.mli
@@ -20,3 +20,7 @@ val get_index : downloader:Downloader.t -> sigchecker:Sigchecker.t
-> ?template:
(** [get_index download sigchecker template source] will parse the source
index file into an index entry list. If the template flag is set to
true, the parser will be less picky about missing values. *)
+
+val write_entry : out_channel -> (string * Index.entry) -> unit
+(** [write_entry chan entry] writes the index entry to the chan output
+ stream.*)
diff --git a/builder/index_parser_tests.ml b/builder/index_parser_tests.ml
new file mode 100644
index 000000000..2315ebe27
--- /dev/null
+++ b/builder/index_parser_tests.ml
@@ -0,0 +1,130 @@
+(* builder
+ * Copyright (C) 2017 SUSE Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+(* This file tests the Index_parser module. *)
+
+open Printf
+
+open OUnit2
+
+open Std_utils
+open Unix_utils
+open Tools_utils
+
+let tmpdir =
+ let tmpdir = Mkdtemp.temp_dir "guestfs-tests." in
+ rmdir_on_exit tmpdir;
+ tmpdir
+
+let dummy_sigchecker = Sigchecker.create ~gpg:"gpg"
+ ~check_signature:false
+ ~gpgkey:Utils.No_Key
+ ~tmpdir
+
+let dummy_downloader = Downloader.create ~curl:"do-not-use-curl"
+ ~cache:None ~tmpdir
+
+(* Utils. *)
+let write_entries file entries =
+ let chan = open_out (tmpdir // file) in
+ List.iter (Index_parser.write_entry chan) entries;
+ close_out chan
+
+let read_file file =
+ read_whole_file (tmpdir // "out")
+
+let parse_file file =
+ let source = { Sources.name = "input";
+ uri = tmpdir // file;
+ gpgkey = Utils.No_Key;
+ proxy = Curl.SystemProxy;
+ format = Sources.FormatNative } in
+ let entries = Index_parser.get_index ~downloader:dummy_downloader
+ ~sigchecker:dummy_sigchecker
+ source in
+ List.map (
+ fun (id, e) -> (id, { e with Index.file_uri = Filename.basename e.Index.file_uri
})
+ ) entries
+
+let format_entries entries =
+ let format_entry entry =
+ write_entries "out" [entry];
+ read_file "out" in
+ List.map format_entry entries
+
+let assert_equal_string = assert_equal ~printer:(fun x -> sprintf
"\"%s\"" x)
+let assert_equal_list formatter =
+ let printer = (
+ fun x -> "(" ^ (String.escaped (String.concat "," (formatter
x))) ^ ")"
+ ) in
+ assert_equal ~printer
+
+let test_write_complete ctx =
+ let entry =
+ ("test-id", { Index.printable_name = Some "test_name";
+ osinfo = Some "osinfo_data";
+ file_uri = "image_path";
+ arch = "test_arch";
+ signature_uri = None;
+ checksums = Some [Checksums.SHA512 "512checksum"];
+ revision = Utils.Rev_int 42;
+ format = Some "qcow2";
+ size = Int64.of_int 123456;
+ compressed_size = Some (Int64.of_int 12345);
+ expand = Some "/dev/sda1";
+ lvexpand = Some "/some/lv";
+ notes = [ ("", "Notes split\non several lines\n\n with starting
space ") ];
+ hidden = false;
+ aliases = Some ["alias1"; "alias2"];
+ sigchecker = dummy_sigchecker;
+ proxy = Curl.SystemProxy }) in
+
+ write_entries "out" [entry];
+ let actual = read_file "out" in
+ let expected = "[test-id]
+name=test_name
+osinfo=osinfo_data
+file=image_path
+arch=test_arch
+checksum[sha512]=512checksum
+revision=42
+format=qcow2
+size=123456
+compressed_size=12345
+expand=/dev/sda1
+lvexpand=/some/lv
+notes=Notes split
+ on several lines
+
+ with starting space
+aliases=alias1 alias2
+
+" in
+ assert_equal_string expected actual;
+
+ let parsed_entries = parse_file "out" in
+ assert_equal_list format_entries [entry] parsed_entries
+
+let suite =
+ "builder Index_parser" >:::
+ [
+ "write.complete" >:: test_write_complete;
+ ]
+
+let () =
+ run_test_tt_main suite
--
2.13.2