let of_log_events conf events =
  let global_conf =
    List.fold_left
      (fun acc log_ev ->
         match log_ev.event with
           | GlobalEvent (GConf (k, v)) -> (k, v) :: acc
           | _ -> acc)
      []
      (List.rev events)
  in
  let running_time, global_results, test_case_count =
    let rec find_results =
      function
        | {event =
             GlobalEvent
               (GResults (running_time, results, test_case_count))} :: _ ->
            running_time, results, test_case_count
        | _ :: tl ->
            find_results tl
        | [] ->
            failwith "Cannot find results in OUnitResult.of_log_events."
    in
      find_results events
  in
  let tests =
    let rec split_raw tmstp str lst =
      try
        let idx = String.index str '\n' in
          split_raw tmstp
            (String.sub str (idx + 1) (String.length str - idx - 1))
            ((tmstp, NoneString.sub str 0 idx) :: lst)
      with Not_found ->
        (tmstp, None, str) :: lst
    in

    let finalize t =
      let log_entries =
        List.sort
          (fun (f1, _, _) (f2, _, _) -> Pervasives.compare f2 f1)
          t.log_entries
      in
      let log_entries =
        List.rev_map
          (fun (f, a, b) -> f -. t.timestamp_start, a, b)
          log_entries
      in
        {t with log_entries = log_entries}
    in

    let default_timestamp = 0.0 in
    let rec process_log_event tests log_event =
      let timestamp = log_event.timestamp in
        match log_event.event with
          | GlobalEvent _ ->
              tests
          | TestEvent (path, ev)  ->
              begin
                let t =
                  try
                    MapPath.find path tests
                  with Not_found ->
                    {
                      test_name = string_of_path path;
                      timestamp_start = default_timestamp;
                      timestamp_end = default_timestamp;
                      log_entries = [];
                      test_result = RFailure ("Not finished"NoneNone);
                    }
                in
                let alt0 t1 t2 =
                  if t1 = default_timestamp then
                    t2
                  else
                    t1
                in
                let t' =
                  match ev with
                    | EStart ->
                        {t with
                             timestamp_start = timestamp;
                             timestamp_end = alt0 t.timestamp_end timestamp}
                    | EEnd ->
                        {t with
                             timestamp_end = timestamp;
                             timestamp_start = alt0 t.timestamp_start timestamp}
                    | EResult rslt ->
                        {t with test_result = rslt}
                    | ELog (svrt, str) ->
                        {t with log_entries = (timestamp, Some svrt, str)
                         :: t.log_entries}
                    | ELogRaw str ->
                        {t with log_entries =
                           split_raw timestamp str t.log_entries}
                in
                  MapPath.add path t' tests
              end
    and group_test tests =
      function
        | hd :: tl ->
            group_test
              (process_log_event tests hd)
              tl
        | [] ->
            let lst =
              MapPath.fold
                (fun _ test lst ->
                   finalize test :: lst)
                tests []
            in
              List.sort
                (fun t1 t2 ->
                   Pervasives.compare t1.timestamp_start t2.timestamp_start)
                lst
    in
      group_test MapPath.empty events
  in
  let start_at =
    List.fold_left
      (fun start_at log_ev ->
         min start_at log_ev.timestamp)
      (now ())
      events
  in
  let suite_name =
    match global_results with
      | (path, _, _) :: _ ->
          List.fold_left
            (fun acc nd ->
               match nd with
                 | ListItem _ -> acc
                 | Label str -> str)
            "noname"
            path
      | [] ->
          "noname"
  in
  let count f =
    List.length
      (List.filter (fun (_, test_result, _) -> f test_result)
         global_results)
  in
  let charset = encoding conf in
    {
      suite_name      = suite_name;
      start_at        = start_at;
      charset         = charset;
      conf            = global_conf;
      running_time    = running_time;
      global_results  = global_results;
      test_case_count = test_case_count;
      tests           = tests;
      errors          = count is_error;
      failures        = count is_failure;
      skips           = count is_skip;
      todos           = count is_todo;
      timeouts        = count is_timeout;
      successes       = count is_success;
    }