|
#!/usr/bin/env ocamlscript |
|
|
|
(* Compile and link with Batteries *) |
|
Ocaml.packs := ["batteries"];; |
|
-- |
|
open Array |
|
open Batteries |
|
open List |
|
open Printf |
|
|
|
let read_file filename = |
|
(let lines = File.lines_of filename in |
|
(Enum.reduce (fun a b -> a ^ "\n" ^ b) lines));; |
|
|
|
let take_until sep str = |
|
try |
|
let sep_start = String.find str sep in |
|
(String.slice ~last: sep_start str) |
|
with |
|
| _ -> str;; |
|
|
|
let drop_until sep str = |
|
try |
|
let sep_start = String.find str sep in |
|
(String.slice ~first: (sep_start + (String.length sep)) str) |
|
with |
|
| _ -> "";; |
|
|
|
let rec extract_comments contents = |
|
match contents with |
|
| "" -> [] |
|
| _ -> |
|
(let comment_start = drop_until "(*" contents in |
|
(let comment = take_until "*)" comment_start in |
|
(let comment_end = drop_until "*)" comment_start in |
|
(comment :: (extract_comments comment_end)))));; |
|
|
|
let is_test_comment str = String.starts_with str "\n#";; |
|
|
|
let rec extract_tests comment = |
|
match comment with |
|
| "" -> [] |
|
| _ -> |
|
(let test = drop_until "# " comment in |
|
(let test_cmd = take_until ";;" test in |
|
(let rest_of_test = drop_until ";;" test in |
|
(let expected = drop_until "=" (drop_until "- :" rest_of_test) in |
|
(let expected_value = take_until "\n" expected in |
|
(let rest = drop_until "\n" expected in |
|
((test_cmd, expected_value) :: (extract_tests rest))))))));; |
|
|
|
(* TODO: Fragile assumption that first word in test is the name *) |
|
let get_test_name cmd = take_until " " cmd;; |
|
|
|
let single_test (cmd, expected) = |
|
(let name = get_test_name cmd in |
|
(sprintf "if ((%s) <> (%s)) |
|
then (print_string \"[FAILED!] %s\\n\") |
|
else (print_string \"[PASS] %s\\n\");" cmd expected name name));; |
|
|
|
let all_tests filename = |
|
(let file = read_file filename in |
|
(let test_comments = filter is_test_comment (extract_comments file) in |
|
(let tests = flatten (map extract_tests test_comments) in |
|
(rev (map single_test tests)))));; |
|
|
|
let test_harness filename = |
|
(let test_strs = String.concat "\n" (all_tests filename) in |
|
(sprintf "#use \"%s\";; |
|
let run_tests = [ |
|
%s |
|
];;" filename test_strs));; |
|
|
|
let run_tests code = |
|
(let input_chan, output_chan = Unix.open_process "/usr/bin/env ocaml" in |
|
(let obuf = Buffer.create 4096 in |
|
(try |
|
output_string output_chan code; |
|
while true do |
|
Buffer.add_channel obuf input_chan 1 |
|
done |
|
with End_of_file -> ()); |
|
let _ = Unix.close_process (input_chan, output_chan) in |
|
(Buffer.contents obuf)));; |
|
|
|
let main = |
|
if ((Array.length Sys.argv) <> 2) |
|
then (print_string "Usage: ./doctest.ml [file]\n") |
|
else (let test_code = test_harness (Array.get Sys.argv 1) in |
|
(print_string (run_tests test_code)));; |