Merge branch 'feature/test-suite'
authorKim Nguyễn <kn@lri.fr>
Sun, 10 Mar 2013 09:27:43 +0000 (10:27 +0100)
committerKim Nguyễn <kn@lri.fr>
Sun, 10 Mar 2013 09:27:43 +0000 (10:27 +0100)
* feature/test-suite:
  Fix the xml_diff program to also check trailing events (after </root>)
  Adds a set of testing script

.gitignore
scripts/gen_mlpack.sh [deleted file]
src/main.ml
tests/alphabet.xml [new file with mode: 0644]
tests/alphabet.xml.queries [new file with mode: 0644]
tools/Makefile [new file with mode: 0644]
tools/XPathEval.java [new file with mode: 0644]
tools/do_test.sh [new file with mode: 0755]
tools/gen_mlpack.sh [new file with mode: 0755]
tools/gen_test.sh [new file with mode: 0755]
tools/xml_diff.ml [new file with mode: 0644]

index 60db691..dae0385 100644 (file)
@@ -1,3 +1,5 @@
 _build
 *.native
 *.byte
+tests/*.results/*
+
diff --git a/scripts/gen_mlpack.sh b/scripts/gen_mlpack.sh
deleted file mode 100755 (executable)
index 95f78ec..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/sh
-
-cd src
-echo `pwd`
-for dir in *
-do
-    if [ -d "$dir" ]
-    then
-        echo "$dir"
-        rm -f "$dir".mlpack
-        cd "$dir"
-        for i in *.ml *.mly *.mll
-        do
-            if [ ! -f "$i" ]
-            then
-                continue
-            fi
-            echo "$i"
-            f=`echo "$i" | cut -b1`
-            l=`basename "$i" .mll`
-            l=`basename "$l" .mly`
-            l=`basename "$l" .ml | cut -b2-`
-            o=`echo "$f" | tr a-z A-Z`
-            echo "$dir"/"$o""$l" >> ../"$dir".mlpack
-        done
-        cd ..
-    fi
-done
-cd ..
\ No newline at end of file
index 3216e71..af1c930 100644 (file)
@@ -14,7 +14,7 @@
 (***********************************************************************)
 
 (*
-  Time-stamp: <Last modified on 2013-03-05 15:26:51 CET by Kim Nguyen>
+  Time-stamp: <Last modified on 2013-03-10 09:49:00 CET by Kim Nguyen>
 *)
 
 (** use: xml_file "XPath querie"
@@ -50,10 +50,10 @@ let () =
   fprintf err_formatter "Evaluating automaton:\n%!";
   let module Naive = Auto.Eval.Make(Tree.Naive) in
   let results = Naive.eval auto doc (Tree.Naive.root doc) in
+  output_string stdout "<xml_results>\n"
   List.iter (fun n ->
-    Tree.Naive.print_xml stderr doc n;
-    flush stderr;
-    output_string stderr "\n-------------------\n";
-  ) results
-
-
+    Tree.Naive.print_xml stdout doc n;
+    output_char stdout '\n'
+  ) results;
+  output_string stdout "\n<xml_results>";
+  flush stdout
diff --git a/tests/alphabet.xml b/tests/alphabet.xml
new file mode 100644 (file)
index 0000000..f0412a9
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><A id="n1" pre="1" post="26" xml:lang="en"><B id="n2" pre="2" post="3"><C id="n3" pre="3" post="1">clergywoman</C><D id="n4" pre="4" post="2">decadent</D></B><E id="n5" pre="5" post="22"><F id="n6" pre="6" post="6"><G id="n7" pre="7" post="4">gentility</G><H id="n8" pre="8" post="5" idrefs="n17 n26">happy-go-lucky man</H></F><I id="n9" pre="9" post="9"><J id="n10" pre="10" post="7">jigsaw</J><K id="n11" pre="11" post="8">kerchief</K></I><L id="n12" pre="12" post="15"><!--L is the twelve-th letter of the English alphabet-->The letter L is followed by the letter:<M id="n13" pre="13" post="10"/>which is followed by the letter:<N id="n14" pre="14" post="13"><O id="n15" pre="15" post="11">ovenware</O><P id="n16" pre="16" post="12">plentiful</P></N><?myPI value="XPath is nice"?><Q id="n17" pre="17" post="14" idrefs="n8 n26">quarrelsome</Q></L><R id="n18" pre="18" post="18"><S id="n19" pre="19" post="16">sage</S><T id="n20" pre="20" post="17">tattered</T></R><U id="n21" pre="21" post="21"><V id="n22" pre="22" post="19">voluptuary</V><W id="n23" pre="23" post="20">wriggle</W></U></E><X id="n24" pre="24" post="25"><Y id="n25" pre="25" post="23">yawn</Y><Z id="n26" pre="26" post="24" idrefs="n8 n17" xml:lang="it">zuzzurellone</Z></X></A>
diff --git a/tests/alphabet.xml.queries b/tests/alphabet.xml.queries
new file mode 100644 (file)
index 0000000..740d084
--- /dev/null
@@ -0,0 +1,66 @@
+A1 //L/*
+A2 //L/parent::*
+A3 //L/descendant::*
+A4 //L/descendant-or-self::*
+A5 //L/ancestor::*
+A6 //L/ancestor-or-self::*
+A7 //L/following-sibling::*
+A8 //L/preceding-sibling::*
+A9 //L/following::*
+A10 //L/preceding::*
+A11 //L/self::*
+A12 //L/@id/parent::*
+P1 //*[L]
+P2 //*[parent::L]
+P3 //*[descendant::L]
+P4 //*[descendant-or-self::L]
+P5 //*[ancestor::L]
+P6 //*[ancestor-or-self::L]
+P7 //*[following-sibling::L]
+P8 //*[preceding-sibling::L]
+P9 //*[following::L]
+P10 //*[preceding::L]
+P11 //*[self::L]
+P12 //*[@id]
+T1 //L/text()
+T2 //L/comment()
+# T3 //L/processing-instruction()
+# T4 //L/processing-instruction("myPI")
+T5 //L/node()
+T6 //L/N
+T7 //L/*
+O1 //*[child::* and preceding::Q]
+O2 //*[not(child::*) and preceding::Q]
+O3 //*[preceding::L or following::L]
+O4 //L/ancestor::* | //L/descendant::*
+# O5 //*[.="happy-go-lucky man"]
+# O6 //*[@pre > 12 and @post < 15]
+# O7 //*[@pre != @post]
+# O8 //*[((@post * @post + @pre * @pre) div (@post + @pre)) > ((@post - @pre) * (@post - @pre))]
+# O9 //*[@pre mod 2 = 0]
+# F1 //*[contains(.,"plentiful")]
+# F2 //*[starts-with(.,"plentiful")]
+# F3 //*[substring(.,1,9) = "plentiful"]
+# F4 //*[substring-after(.,"oven") = "ware"]
+# F5 //*[substring-before(.,"ful") = "plenti"]
+# F6 //*[string-length(translate(normalize-space(.)," ","")) > 100]
+# F7 //*[concat(.,..) = ..]
+# F8 //*[ceiling(@pre div @post) = 1]
+# F9 //*[floor(@pre div @post) = 0]
+# F10 //*[round(@pre div @post) = 0]
+# F11 //*[name(.) = "X"]
+# F12 //*[lang("it")]
+# F13 //L/child::*[last()]
+# F14 //L/descendant::*[4]
+# F15 //L/ancestor::*[2]
+# F16 //L/following-sibling::*[1]
+# F17 //L/preceding-sibling::*[1]
+# F18 //L/following::*[7]
+# F19 //L/preceding::*[7]
+# F20 //*[count(ancestor::*) > 3]
+# F21 //*[sum(ancestor::*/@pre) < sum(descendant::*/@pre)]
+# F22 id("n1 n26")
+# F23 id(id(//*[.="happy-go-lucky man"]/@idrefs)/@idrefs)
+# F24 //*[number(@pre) < number(@post)]
+# F25 //*[string(@pre - 1) = "0"]
+# F26 //*[boolean(@id) = true() and boolean(@idrefs) = false()]
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644 (file)
index 0000000..89d8986
--- /dev/null
@@ -0,0 +1,12 @@
+all: XPathEval.class  xml_diff
+
+
+xml_diff: xml_diff.ml
+       ocamlfind ocamlopt -o xml_diff -package expat -linkpkg xml_diff.ml
+
+XPathEval.class: XPathEval.java
+       javac XPathEval.java
+
+
+clean:
+       rm -f XPathEval.class xml_diff *.o *.cm*
diff --git a/tools/XPathEval.java b/tools/XPathEval.java
new file mode 100644 (file)
index 0000000..8c854ac
--- /dev/null
@@ -0,0 +1,36 @@
+import javax.xml.xpath.*;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.*;
+import javax.xml.transform.stream.*;
+
+public class XPathEval {
+
+
+  public static void main(String args[]) {
+    try {
+
+
+      XPath xpath = XPathFactory.newInstance().newXPath();
+      String expression = args[1];
+      InputSource inputSource = new InputSource(args[0]);
+      NodeList nodes = (NodeList) xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
+      Transformer serializer = TransformerFactory.newInstance().newTransformer();
+      serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+      StreamResult o = new StreamResult(System.out);
+      System.out.println("<xml_result>");
+      for(int i = 0; i < nodes.getLength(); i++){
+        serializer.transform(new DOMSource(nodes.item(i)), o);
+        System.out.println("");
+      };
+      System.out.println("</xml_result>");
+    } catch (XPathException e) {
+      System.out.println (e.getCause());
+    } catch (Exception e) {
+      System.out.println(e);
+    };
+
+
+  }
+}
diff --git a/tools/do_test.sh b/tools/do_test.sh
new file mode 100755 (executable)
index 0000000..05163b8
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+TESTPROG="../main.native"
+
+usage() {
+    echo "error: missing input, query file, xml_diff or $TESTPROG"
+    echo "usage: $0 file.xml"
+}
+
+FILE="$1"
+RESULTS="$FILE".results
+QUERIES="$FILE".queries
+
+if test ! -f "$FILE" -o ! -f "$QUERIES" -o ! -f "$TESTPROG" -o ! -f xml_diff
+then
+   usage;
+   exit 1
+fi
+
+
+mkdir -p "$RESULTS"
+
+cat "$QUERIES" | grep -v '^#' | ( TOTAL=0; TESTS=0;
+while read qname q
+do
+    TOTAL=$(($TOTAL + 1))
+    echo -n "Testing $qname: $q ... "
+    "$TESTROG" "$FILE" "$q" > "$RESULTS"/"$qname"_test.xml 2> "$RESULTS"/"$qname"_test.log
+    ./xml_diff "$RESULTS"/"$qname"_test.xml "$RESULTS"/"$qname".xml 2>> "$RESULTS"/"$qname"_test.log
+    case "$?" in
+        0)
+            TESTS=$(($TESTS + 1))
+            echo ok
+            ;;
+        *)
+            echo failed
+            echo ------- output -----------
+            cat "$RESULTS"/"$qname"_test.log
+            echo --------------------------
+            ;;
+    esac
+done;
+echo "Summary: $TESTS/$TOTAL tests successfull"
+)
diff --git a/tools/gen_mlpack.sh b/tools/gen_mlpack.sh
new file mode 100755 (executable)
index 0000000..95f78ec
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+cd src
+echo `pwd`
+for dir in *
+do
+    if [ -d "$dir" ]
+    then
+        echo "$dir"
+        rm -f "$dir".mlpack
+        cd "$dir"
+        for i in *.ml *.mly *.mll
+        do
+            if [ ! -f "$i" ]
+            then
+                continue
+            fi
+            echo "$i"
+            f=`echo "$i" | cut -b1`
+            l=`basename "$i" .mll`
+            l=`basename "$l" .mly`
+            l=`basename "$l" .ml | cut -b2-`
+            o=`echo "$f" | tr a-z A-Z`
+            echo "$dir"/"$o""$l" >> ../"$dir".mlpack
+        done
+        cd ..
+    fi
+done
+cd ..
\ No newline at end of file
diff --git a/tools/gen_test.sh b/tools/gen_test.sh
new file mode 100755 (executable)
index 0000000..3e5a34f
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+usage() {
+    echo "error: missing input, query file or XPathEval class"
+    echo "usage: $0 file.xml"
+}
+
+FILE="$1"
+RESULTS="$FILE".results
+QUERIES="$FILE".queries
+
+if test ! -f "$FILE" -o ! -f "$QUERIES" -o ! -f XPathEval.class
+then
+   usage;
+   exit 1
+fi
+
+
+mkdir -p "$RESULTS"
+
+cat "$QUERIES" | grep -v '^#' | while read qname q
+do
+    echo "Computing $q"
+    java XPathEval "$FILE" "$q" > "$RESULTS"/"$qname".xml
+done
diff --git a/tools/xml_diff.ml b/tools/xml_diff.ml
new file mode 100644 (file)
index 0000000..1d85743
--- /dev/null
@@ -0,0 +1,112 @@
+type event = Open of string * (string * string) list | Close of string | Text of string
+
+let eq_event e1 e2 =
+  match e1, e2 with
+  | Open(t1, l1), Open(t2, l2) -> t1 = t2 &&
+  let sl1 = List.sort compare l1
+  and sl2 = List.sort compare l2 in sl1 = sl2
+  | Close t1, Close t2 -> t1 = t2
+  | Text s1 , Text s2 -> s1 = s2
+  | _ -> false
+
+
+type position = {
+  byte_index : int;
+  column_num : int;
+  line_num : int;
+  byte_count : int;
+}
+
+type ctx = {
+  text_buffer : Buffer.t;
+  events : (event*position) Queue.t;
+}
+let get_position p =
+  { byte_index = Expat.get_current_byte_index p;
+    column_num = Expat.get_current_column_number p;
+    line_num =  Expat.get_current_line_number p;
+    byte_count =  Expat.get_current_byte_count p;
+  }
+
+
+let rec start_element_handler parser_ ctx tag attr_list =
+  do_text parser_ ctx;
+  Queue.add (Open(tag, attr_list), get_position parser_) ctx.events
+
+and end_element_handler parser_ ctx tag =
+  do_text parser_ ctx;
+  Queue.add (Close(tag), get_position parser_) ctx.events
+
+and do_text parser_ ctx =
+  if Buffer.length ctx.text_buffer != 0 then
+    let s = Buffer.contents ctx.text_buffer in
+    Buffer.clear  ctx.text_buffer;
+    Queue.add (Text s, get_position parser_) ctx.events
+
+let character_data_handler parser_ ctx text =
+  Buffer.add_string ctx.text_buffer text
+
+let create_parser () =
+  let ctx1 = { text_buffer = Buffer.create 512; events = Queue.create (); } in
+  let parser1 = Expat.parser_create ~encoding:None in
+  Expat.set_start_element_handler parser1 (start_element_handler parser1 ctx1);
+  Expat.set_end_element_handler parser1 (end_element_handler parser1 ctx1);
+  Expat.set_character_data_handler
+    parser1 (character_data_handler parser1 ctx1);
+  parser1, ctx1
+
+exception Diff of position
+
+let common_prefix ctx1 ctx2 len =
+  for i = 1 to len do
+    let e1,p1 = Queue.pop ctx1.events in
+    let e2,_ = Queue.pop ctx2.events in
+    if not (eq_event e1 e2) then raise (Diff p1)
+  done
+
+
+let diffs fd1 fd2 =
+  let buffer1 = String.create 4096 in
+  let buffer2 = String.create 4096 in
+  let parser1,ctx1 = create_parser () in
+  let parser2,ctx2 = create_parser () in
+  let rec loop () =
+    let read1 = input fd1 buffer1 0 4096 in
+    let read2 = input fd2 buffer2 0 4096 in
+    if read1 == 0 && read2 == 0 then begin
+      let l1 = Queue.length ctx1.events in
+      let l2 = Queue.length ctx2.events in
+      if l1 > l2 then let _, p1 = Queue.pop ctx1.events in raise (Diff p1)
+      else if l2 > l1 then let _, p2 = Queue.pop ctx2.events in raise (Diff p2)
+      else common_prefix ctx1 ctx2 l1
+    end else begin
+      let () = Expat.parse_sub parser1 buffer1 0 read1 in
+      let () = Expat.parse_sub parser2 buffer2 0 read2 in
+      common_prefix ctx1 ctx2 (min (Queue.length ctx1.events) (Queue.length ctx2.events));
+      loop ()
+    end
+  in
+  loop ()
+
+let main () =
+  if Array.length Sys.argv != 3 then begin
+    Printf.eprintf "usage: %s file1.xml file2.xml\n%!" Sys.argv.(0);
+    exit 2
+  end else
+    let fn1 = Sys.argv.(1) in
+    let fn2 = Sys.argv.(2) in
+    try
+      let fd1 = open_in fn1 in
+      let fd2 = open_in fn2 in
+      let code =
+        try
+          diffs fd1 fd2; 0
+        with
+          Diff p -> Printf.eprintf "File %s and %s differ at line %i, column %i\n%!"
+            fn1 fn2 p.line_num p.column_num;1
+      in close_in fd1; close_in fd2; exit code
+    with
+      e -> Printf.eprintf "Error %s\n%!" (Printexc.to_string e); exit 3
+
+let () = main ()
+