(* $Id: test-charEncoding.ml,v 1.8 2006/08/13 21:23:08 yori Exp $ *)
(* Copyright 2002, 2010 Yamagata Yoriyuki *)

open Printf
open Blender
open Camomile
open UPervasives
open CharEncoding
open OOChannel
module UL = ULine.Make (UTF8)

let print_uchar u = sprintf "\\u%08x" (int_of_uchar u)
let enc_filename file = input_filename (Filename.concat "data/enc" file)

let test_decoding enc_name file utf8_file =
  let file = enc_filename file in
  let utf8_file = enc_filename utf8_file in
  let enc = of_name enc_name in
  let src = new in_channel enc (open_in_bin file) in
  let dst = new in_channel utf8 (open_in_bin utf8_file) in
  test ~desc:("Decoding: " ^ enc_name) ~body:(fun () ->
      expect_pass ~body:(fun () ->
          let c = ref 0 in
          (try
             while true do
               incr c;
               let u = dst#get () in
               expect_equal_app
                 ~msg:(lazy (sprintf "location %d" !c))
                 ~printer:print_uchar
                 (fun () -> u)
                 ()
                 (fun () -> src#get ())
                 ()
             done
           with End_of_file -> ());
          expect_equal_app
            ~msg:(lazy "EOF")
            ~printer:print_uchar
            (fun () -> dst#get ())
            ()
            (fun () -> src#get ())
            ()))

let test_encoding enc_name utf8_file =
  let enc = of_name enc_name in
  let utf8_file = enc_filename utf8_file in
  let us = stream_of_channel (new in_channel utf8 (open_in_bin utf8_file)) in
  let us0 = stream_of_channel (new in_channel utf8 (open_in_bin utf8_file)) in
  let cs = char_stream_of enc us0 in
  let us' = ustream_of enc cs in
  test ~desc:("Encoding: " ^ enc_name) ~body:(fun () ->
      expect_pass ~body:(fun () ->
          Stream.iter
            (fun u ->
              expect_equal_app
                ~msg:(lazy (sprintf "location %d" (Stream.count us)))
                ~printer:print_uchar
                (fun () -> u)
                () Stream.next us')
            us;
          expect_equal_app
            ~msg:(lazy "EOF")
            ~printer:print_uchar Stream.next us Stream.next us'))

let test_enc file =
  let enc_name =
    let s = Filename.basename file in
    try
      let i = String.index s '#' in
      String.sub s 0 i
    with Not_found -> s
  in
  try
    let utf8_file = file ^ "..UTF8" in
    test_decoding enc_name file utf8_file;
    test_encoding enc_name utf8_file
  with
    | Not_found ->
        prerr_string ("Warning: " ^ enc_name ^ " is not supported");
        prerr_newline ()
    | Sys_error _ as exn ->
        prerr_string ("Exception: " ^ Printexc.to_string exn);
        prerr_newline ()

let filter name = not (Str.string_match (Str.regexp ".*\\.\\..*") name 0)
let _ = foreach_file (input_filename "data/enc") ~filter test_enc

(* test for Japanese auto detection *)

let _ = test_decoding "jauto" "ISO-2022-JP" "ISO-2022-JP..UTF8"
let _ = test_decoding "jauto" "ISO-2022-JP-2" "ISO-2022-JP-2..UTF8"
let _ = test_decoding "jauto" "EUC-JP" "EUC-JP..UTF8"

class buffer_io b =
  object
    method flush () = ()
    method close_out () = ()

    method output s pos len =
      Buffer.add_subbytes b s pos len;
      len
  end

let () =
  test ~desc:"Output Test" ~body:(fun () ->
      expect_pass ~body:(fun () ->
          let b = Buffer.create 0 in
          let oooch = new buffer_io b in
          let ceuoc =
            new CharEncoding.uchar_output_channel_of CharEncoding.utf8 oooch
          in
          let outln = new UL.output_line ceuoc in
          outln#put "---------------";
          outln#flush ();
          outln#put "1 first line  1";
          outln#flush ();
          outln#put "2 second line 2";
          outln#flush ();
          outln#put "3 third line  3";
          outln#flush ();
          outln#flush ();
          outln#flush ();
          expect_equal
            ~msg:(lazy (sprintf "output %s\n" (Buffer.contents b)))
            (Buffer.contents b)
            "---------------\n\
             1 first line  1\n\
             2 second line 2\n\
             3 third line  3\n"))

let () = Blender.main ()
