test/unixpath.ss
#lang planet chongkai/sml

(* Auxiliary functions for test cases *)

infix 1 seq
fun e1 seq e2 = e2;
fun check b = if b then "OK" else "WRONG";
fun check' f = (if f () then "OK" else "WRONG") handle _ => "EXN";

fun range (from, to) p = 
    let open Int 
    in
	(from > to) orelse (p from) andalso (range (from+1, to) p)
    end;

fun checkrange bounds = check o range bounds;

fun tst0 s s' = print (s ^ "    \t" ^ s' ^ "\n");
fun tst  s b = tst0 s (check  b);
fun tst' s f = tst0 s (check' f);

fun tstrange s bounds = (tst s) o range bounds  

(* test/unixpath.sml 6 -- for Unix, 1995-05-23 *)

val _ = print "\nFile unixpath.sml: Testing structure Path...\n"

local 
    open Path
    
val test1a = 
    tst' "test1a" (fn _ => fromString "" = {isAbs=false, vol = "", arcs = []});
val test1b = 
    tst' "test1b" (fn _ => fromString "/" = {isAbs=true, vol="", arcs=[""]});
val test1c = 
    tst' "test1c" (fn _ => fromString "//" = {isAbs=true, vol="", arcs=["", ""]});
val test1d = 
    tst' "test1d" (fn _ => fromString "a" = {isAbs=false, vol = "", arcs = ["a"]});
val test1e = 
    tst' "test1e" (fn _ => fromString "/a" = {isAbs=true, vol="", arcs=["a"]});
val test1f = 
    tst' "test1f" (fn _ => fromString "//a" = {isAbs=true, vol="", arcs=["","a"]});
val test1g = 
    tst' "test1g" (fn _ => fromString "a/" = {isAbs=false, vol = "", arcs = ["a", ""]});
val test1h = 
    tst' "test1h" (fn _ => fromString "a//" = {isAbs=false, vol = "", arcs = ["a", "", ""]});
val test1i = 
    tst' "test1i" (fn _ => fromString "a/b" = {isAbs=false, vol = "", arcs = ["a", "b"]});
val test1j = 
    tst' "test1j" (fn _ => fromString "a.b/c" = {isAbs=false, vol = "", arcs = ["a.b", "c"]});
val test1k = 
    tst' "test1k" (fn _ => fromString "a.b/c/" = {isAbs=false, vol = "", arcs = ["a.b", "c", ""]});
val test1l = 
    tst' "test1l" (fn _ => fromString "a/./c" = {isAbs=false, vol = "", arcs = ["a", ".", "c"]});
val test1m = 
    tst' "test1m" (fn _ => fromString "a/../c" = {isAbs=false, vol = "", arcs = ["a", "..", "c"]});
val test1n = 
    tst' "test1n" (fn _ => fromString "." = {isAbs=false, vol = "", arcs = ["."]});

val test2a =
    tst' "test2a" (fn _ => toString {isAbs=false, vol = "", arcs = []} = "");
val test2b = 
    tst' "test2b" (fn _ => toString {isAbs=true, vol="", arcs=[]} = "/");
val test2c = 
    tst' "test2c" (fn _ => toString {isAbs=true, vol="", arcs=["", ""]} = "//");
val test2d = 
    tst' "test2d" (fn _ => toString {isAbs=false, vol = "", arcs = ["a"]} = "a");
val test2e = 
    tst' "test2e" (fn _ => toString {isAbs=true, vol="", arcs=["a"]} = "/a");
val test2f = 
    tst' "test2f" (fn _ => toString {isAbs=true, vol="", arcs=["","a"]} = "//a");
val test2g = 
    tst' "test2g" (fn _ => toString {isAbs=false, vol = "", arcs = ["a", ""]} = "a/");
val test2h = 
    tst' "test2h" (fn _ => toString {isAbs=false, vol = "", arcs = ["a", "", ""]} = "a//");
val test2i = 
    tst' "test2i" (fn _ => toString {isAbs=false, vol = "", arcs = ["a", "b"]} = "a/b");
val test2j = 
    tst' "test2j" (fn _ => toString {isAbs=false, vol = "", arcs = ["a.b", "c"]} = "a.b/c");
val test2k = 
    tst' "test2k" (fn _ => toString {isAbs=false, vol = "", arcs = ["a.b", "c", ""]} = "a.b/c/");
val test2l = 
    tst' "test2l" (fn _ => toString {isAbs=false, vol = "", arcs = ["a", ".", "c"]} = "a/./c");
val test2m = 
    tst' "test2m" (fn _ => toString {isAbs=false, vol = "", arcs = ["a", "..", "c"]} = "a/../c");
val test2n = 
    tst' "test2n" (fn _ => toString {isAbs=true, vol="", arcs=["a", "..", "c"]} = "/a/../c");
val test2o = tst0 "test2o" ((toString {isAbs=false, vol = "", arcs =  ["", "a"]} seq "WRONG")
			    handle Path => "OK" | _ => "WRONG")
val test2p = tst0 "test2p" ((toString {isAbs=true, vol = "C:", arcs =  ["windows"]} seq "WRONG")
			    handle Path => "OK" | _ => "WRONG")

val test3b = 
    tst' "test3b" (fn _ => getVolume "/" = "");
val test3c = 
    tst' "test3c" (fn _ => getVolume "//" = "");
val test3d = 
    tst' "test3d" (fn _ => getVolume "a//b/c/" = "");
val test3e = 
    tst' "test3e" (fn _ => getVolume "./" = "");
val test3f = 
    tst' "test3f" (fn _ => getVolume "../" = "");
val test3g = 
    tst' "test3g" (fn _ => getVolume "" = "");
val test3h = 
    tst' "test3h" (fn _ => getVolume "C:" = "");

val test4a = 
    tst' "test4a" (fn _ => 
	   List.all isRelative ["", ".", "..", "a//"]
	   andalso not (List.exists isRelative ["/", "/a", "//"]));
val test4b = 
    tst' "test4b" (fn _ => 
	   List.all isAbsolute ["/", "/a", "//", "/.", "/.."]
	   andalso not (List.exists isAbsolute ["", ".", "..", "a//"]));

val test5a = 
    tst' "test5a" (fn _ => 
	   getParent "/" = "/"
	   andalso getParent "a" = "."
	   andalso getParent "a/" = "a/.."
	   andalso getParent "a///" = "a///.."
	   andalso getParent "a/b" = "a"
	   andalso getParent "a/b/" = "a/b/.."
	   andalso getParent "/a/b" = "/a"
	   andalso getParent "/a/b/" = "/a/b/.."
	   andalso getParent ".." = "../.."
	   andalso getParent "." = ".."
	   andalso getParent "../" = "../.."
	   andalso getParent "./" = "./.."
	   andalso getParent "" = "..");

val test6a = 
    tst' "test6a" (fn _ => 
	   concat("a", "b") = "a/b"
	   andalso concat("a", "b/c") = "a/b/c"
	   andalso concat("/", "b/c") = "/b/c"
	   andalso concat("", "b/c") = "b/c"
	   andalso concat("/a", "b/c") = "/a/b/c"
	   andalso concat("a/", "b/c") = "a/b/c"
	   andalso concat("a//", "b/c") = "a//b/c"
	   andalso concat(".", "b/c") = "./b/c"
	   andalso concat("a/b", "..") = "a/b/.."
	   andalso concat("a/b", "../c") = "a/b/../c");
val test6b = tst0 "test6b" ((concat ("a", "/b") seq "WRONG")
			    handle Path => "OK" | _ => "WRONG")

val test7a = 
    tst' "test7a" (fn _ => 
	   mkAbsolute{path="/a/b", relativeTo="/c/d"} = "/a/b"
	   andalso mkAbsolute{path="/", relativeTo="/c/d"} = "/"
	   andalso mkAbsolute{path="a/b", relativeTo="/c/d"} = "/c/d/a/b");
val test7b = tst0 "test7b" ((mkAbsolute{path="a", relativeTo="c/d"} seq "WRONG")
			    handle Path => "OK" | _ => "WRONG")
val test7c = tst0 "test7c" ((mkAbsolute{path="/a", relativeTo="c/d"} seq "WRONG")
			    handle Path => "OK" | _ => "WRONG")

val test8a = 
    tst' "test8a" (fn _ => 
	   mkRelative{path="a/b", relativeTo="/c/d"} = "a/b"
	   andalso mkRelative{path="/", relativeTo="/a/b/c"}	   = "../../.." 
	   andalso mkRelative{path="/a/", relativeTo="/a/b/c"}	   = "../../" 
	   andalso mkRelative{path="/a/b/", relativeTo="/a/c"}	   = "../b/"     
	   andalso mkRelative{path="/a/b", relativeTo="/a/c/"}	   = "../b"      
	   andalso mkRelative{path="/a/b/", relativeTo="/a/c/"}	   = "../b/"     
	   andalso mkRelative{path="/", relativeTo="/"}		   = "."	      
	   andalso mkRelative{path="/", relativeTo="/."}	   = "."	      
	   andalso mkRelative{path="/", relativeTo="/.."}	   = "."	      
	   andalso mkRelative{path="/", relativeTo="/a"}	   = ".."	      
	   andalso mkRelative{path="/a/b/../c", relativeTo="/a/d"} = "../b/../c" 
	   andalso mkRelative{path="/a/b", relativeTo="/c/d"}      = "../../a/b"
	   andalso mkRelative{path="/c/a/b", relativeTo="/c/d"}    = "../a/b"
	   andalso mkRelative{path="/c/d/a/b", relativeTo="/c/d"}  = "a/b");
val test8b = tst0 "test8b" ((mkRelative{path="/a", relativeTo="c/d"} seq "WRONG")
			    handle Path => "OK" | _ => "WRONG")
val test8c = tst0 "test8c" ((mkRelative{path="a", relativeTo="c/d"} seq "WRONG")
			    handle Path => "OK" | _ => "WRONG")

val test9a = let
    fun chkCanon (a, b) =
	  (mkCanonical a = b) 
	  andalso (mkCanonical b = b)
	  andalso (isCanonical b)
    in
      tst' "test9a" (fn _ => 
	   chkCanon("", ".")
	   andalso chkCanon(".", ".")
	   andalso chkCanon("./.", ".")
	   andalso chkCanon("/.", "/")
	   andalso chkCanon("..", "..")
	   andalso chkCanon("../..", "../..")
	   andalso chkCanon("b", "b")
	   andalso chkCanon("a/b", "a/b")
	   andalso chkCanon("/a/b", "/a/b")
	   andalso chkCanon("a/b/", "a/b")
	   andalso chkCanon("a/b//", "a/b")
	   andalso chkCanon("a/../b", "b")
	   andalso chkCanon("a/..", ".")
	   andalso chkCanon("a/.", "a")
	   andalso chkCanon("a/", "a")
	   andalso chkCanon("/a/../b/", "/b")
	   andalso chkCanon("/..", "/")
	   andalso chkCanon("/../../a/b", "/a/b")
	   andalso chkCanon("/./../../a/b", "/a/b")
	   andalso chkCanon("/./../..", "/")
	   andalso chkCanon("a/../b", "b")
	   andalso chkCanon("a/./b", "a/b")
	   andalso chkCanon("a////b", "a/b")
           andalso chkCanon("a////b", "a/b"))
    end

val test10a = 
    tst' "test10a" (fn _ => 
	   not (isCanonical "./."
		orelse isCanonical "/.."
		orelse isCanonical "/."
		orelse isCanonical "//"
		orelse isCanonical "a/.."
		orelse isCanonical "a//b"
		orelse isCanonical "a/."
	        orelse isCanonical "a/b/"
		orelse isCanonical "a/.."))
		
val test11a = 
    tst' "test11a" (fn _ => 
	   splitDirFile "" = {dir = "", file = ""}
	   andalso splitDirFile "." = {dir = "", file = "."}
	   andalso splitDirFile ".." = {dir = "", file = ".."}
	   andalso splitDirFile "b" = {dir = "", file = "b"}
	   andalso splitDirFile "b/" = {dir = "b", file = ""}
	   andalso splitDirFile "a/b" = {dir = "a", file = "b"}
	   andalso splitDirFile "/a" = {dir = "/", file = "a"}
	   andalso splitDirFile "/a/b" = {dir = "/a", file = "b"}
	   andalso splitDirFile "/c/a/b" = {dir = "/c/a", file = "b"}
	   andalso splitDirFile "/c/a/b/" = {dir = "/c/a/b", file = ""}
	   andalso splitDirFile "/c/a/b.foo.bar" = {dir = "/c/a", file="b.foo.bar"}
	   andalso splitDirFile "/c/a/b.foo" = {dir = "/c/a", file = "b.foo"});

(*    
val test11b = (splitDirFile "" seq "WRONG") 
              handle Path => "OK" | _ => "WRONG";
*)

val test12 = 
    tst' "test12" (fn _ => 
	   "" = joinDirFile {dir = "", file = ""}
	   andalso "b" = joinDirFile {dir = "", file = "b"}
	   andalso "/" = joinDirFile {dir = "/", file = ""}
	   andalso "/b" = joinDirFile {dir = "/", file = "b"}
	   andalso "a/b" = joinDirFile {dir = "a", file = "b"}
	   andalso "/a/b" = joinDirFile {dir = "/a", file = "b"}
	   andalso "/c/a/b" = joinDirFile {dir = "/c/a", file = "b"}
	   andalso "/c/a/b/" = joinDirFile {dir = "/c/a/b", file = ""}
	   andalso "/c/a/b.foo.bar" = joinDirFile {dir = "/c/a", file="b.foo.bar"}
	   andalso "/c/a/b.foo" = joinDirFile {dir = "/c/a", file = "b.foo"});

val test13 = 
    tst' "test13" (fn _ => 
	   dir "b" = ""
	   andalso dir "a/b" = "a"
	   andalso dir "/" = "/"
	   andalso dir "/b" = "/"
	   andalso dir "/a/b" = "/a"
	   andalso dir "/c/a/b" = "/c/a"
	   andalso dir "/c/a/b/" = "/c/a/b"
	   andalso dir "/c/a/b.foo.bar" = "/c/a"
	   andalso dir "/c/a/b.foo" = "/c/a");

val test14 = 
    tst' "test14" (fn _ => 
	   file "b" = "b"
	   andalso file "a/b" = "b"
	   andalso file "/" = ""
	   andalso file "/b" = "b"
	   andalso file "/a/b" = "b"
	   andalso file "/c/a/b" = "b"
	   andalso file "/c/a/b/" = ""
	   andalso file "/c/a/b.foo.bar" = "b.foo.bar"
	   andalso file "/c/a/b.foo" = "b.foo");

val test15 = 
    tst' "test15" (fn _ => 
	   splitBaseExt "" = {base = "", ext = NONE}
	   andalso splitBaseExt ".login" = {base = ".login", ext = NONE}
	   andalso splitBaseExt "/.login" = {base = "/.login", ext = NONE}
	   andalso splitBaseExt "a" = {base = "a", ext = NONE}
	   andalso splitBaseExt "a." = {base = "a.", ext = NONE}
	   andalso splitBaseExt "a.b" = {base = "a", ext = SOME "b"}
	   andalso splitBaseExt "a.b.c" = {base = "a.b", ext = SOME "c"}
	   andalso splitBaseExt "/a.b" = {base = "/a", ext = SOME "b"}
	   andalso splitBaseExt "/c/a.b" = {base = "/c/a", ext = SOME "b"}
	   andalso splitBaseExt "/c/a/b/.d" = {base = "/c/a/b/.d", ext = NONE}
	   andalso splitBaseExt "/c.a/b.d" = {base = "/c.a/b", ext = SOME "d"}
	   andalso splitBaseExt "/c.a/bd" = {base = "/c.a/bd", ext = NONE}
	   andalso splitBaseExt "/c/a/b.foo.bar" = {base="/c/a/b.foo",ext=SOME "bar"}
	   andalso splitBaseExt "/c/a/b.foo" = {base = "/c/a/b", ext = SOME "foo"});

val test16 = 
    tst' "test16" (fn _ => 
	   "" = joinBaseExt {base = "", ext = NONE}
	   andalso ".login" = joinBaseExt {base = ".login", ext = NONE}
	   andalso "a" = joinBaseExt {base = "a", ext = NONE}
	   andalso "a" = joinBaseExt {base = "a", ext = SOME ""}
	   andalso "a.b" = joinBaseExt {base = "a", ext = SOME "b"}
	   andalso "a.b.c" = joinBaseExt {base = "a.b", ext = SOME "c"}
	   andalso "a.b.c.d" = joinBaseExt {base = "a.b", ext = SOME "c.d"}
	   andalso "/a.b" = joinBaseExt {base = "/a", ext = SOME "b"}
	   andalso "/c/a.b" = joinBaseExt {base = "/c/a", ext = SOME "b"}
	   andalso "/c/a/b/.d" = joinBaseExt {base = "/c/a/b/", ext = SOME "d"}
	   andalso "/c/a/b.foo.bar" = joinBaseExt {base="/c/a/b",ext=SOME "foo.bar"}
	   andalso "/c/a/b.foo" = joinBaseExt {base = "/c/a/b", ext = SOME "foo"});

val test17 = 
    tst' "test17" (fn _ => 
	   ext "" = NONE
	   andalso ext ".login" = NONE
	   andalso ext "/.login" = NONE
	   andalso ext "a" = NONE
	   andalso ext "a." = NONE
	   andalso ext "a.b" = SOME "b"
	   andalso ext "a.b.c" = SOME "c"
	   andalso ext "a.b.c.d" = SOME "d"
	   andalso ext "/a.b" = SOME "b"
	   andalso ext "/c/a.b" = SOME "b"
	   andalso ext "/c/a/b/.d" = NONE
	   andalso ext "/c.a/b.d" = SOME "d"
	   andalso ext "/c.a/bd" = NONE
	   andalso ext "/c/a/b.foo.bar" = SOME "bar"
	   andalso ext "/c/a/b.foo" = SOME "foo");

val test18 = 
    tst' "test18" (fn _ => 
	   base "" = ""
	   andalso base ".d" = ".d"
	   andalso base ".login" = ".login"
	   andalso base "/.login" = "/.login"
	   andalso base "a" = "a"
	   andalso base "a." = "a."
	   andalso base "a.b" = "a"
	   andalso base "a.b.c" = "a.b" 
	   andalso base "a.b.c.d" = "a.b.c"
	   andalso base "/a.b" = "/a"
	   andalso base "/c/a.b" = "/c/a"
	   andalso base "/c/a/b/.d" = "/c/a/b/.d"
	   andalso base "/c.a/b.d" = "/c.a/b"
	   andalso base "/c.a/bd" = "/c.a/bd"
	   andalso base "/c/a/b.foo.bar" = "/c/a/b.foo"
	   andalso base "/c/a/b.foo" = "/c/a/b");

val test19 = 
    tst' "test19" (fn () => validVolume{isAbs=false, vol=""}
	   andalso validVolume{isAbs=true, vol=""}
	   andalso not (validVolume{isAbs=true, vol="/"}
			orelse validVolume{isAbs=false, vol="/"} 
			orelse validVolume{isAbs=true, vol="C:"}
			orelse validVolume{isAbs=false, vol="C:"}
			orelse validVolume{isAbs=true, vol=" "}
			orelse validVolume{isAbs=false, vol=" "}));
in
end