burrito-1.0.1.0: src/test/Main.hs
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeFamilies #-}
module Main
( main
)
where
import qualified Burrito
import qualified Control.Monad as Monad
import qualified Control.Monad.Trans.Writer as Writer
import qualified Data.Either as Either
import qualified Data.String as String
import qualified GHC.Exts as Exts
import qualified GHC.Stack as Stack
import qualified System.Exit as Exit
import qualified Test.HUnit as Test
main :: IO ()
main = runTests $ do
label "accepts empty templates" $ do
test "" [] ""
label "ignores extra variables" $ do
test "" ["extra" =: "ignored"] ""
label "accepts ascii literals" $ do
test "!" [] "!"
test "#" [] "#"
test "$" [] "$"
test "&" [] "&"
test "(" [] "("
test ")" [] ")"
test "*" [] "*"
test "+" [] "+"
test "," [] ","
test "-" [] "-"
test "." [] "."
test "/" [] "/"
test "0" [] "0"
test "9" [] "9"
test ":" [] ":"
test ";" [] ";"
test "=" [] "="
test "?" [] "?"
test "@" [] "@"
test "A" [] "A"
test "Z" [] "Z"
test "[" [] "["
test "]" [] "]"
test "_" [] "_"
test "a" [] "a"
test "z" [] "z"
test "~" [] "~"
label "accepts unicode literals" $ do
test "\xa0" [] "%C2%A0"
test "\xd7ff" [] "%ED%9F%BF"
test "\xf900" [] "%EF%A4%80"
test "\xfdcf" [] "%EF%B7%8F"
test "\xfdf0" [] "%EF%B7%B0"
test "\xffef" [] "%EF%BF%AF"
test "\x10000" [] "%F0%90%80%80"
test "\x1fffd" [] "%F0%9F%BF%BD"
test "\x20000" [] "%F0%A0%80%80"
test "\x2fffd" [] "%F0%AF%BF%BD"
test "\x30000" [] "%F0%B0%80%80"
test "\x3fffd" [] "%F0%BF%BF%BD"
test "\x40000" [] "%F1%80%80%80"
test "\x4fffd" [] "%F1%8F%BF%BD"
test "\x50000" [] "%F1%90%80%80"
test "\x5fffd" [] "%F1%9F%BF%BD"
test "\x60000" [] "%F1%A0%80%80"
test "\x6fffd" [] "%F1%AF%BF%BD"
test "\x70000" [] "%F1%B0%80%80"
test "\x7fffd" [] "%F1%BF%BF%BD"
test "\x80000" [] "%F2%80%80%80"
test "\x8fffd" [] "%F2%8F%BF%BD"
test "\x90000" [] "%F2%90%80%80"
test "\x9fffd" [] "%F2%9F%BF%BD"
test "\xa0000" [] "%F2%A0%80%80"
test "\xafffd" [] "%F2%AF%BF%BD"
test "\xb0000" [] "%F2%B0%80%80"
test "\xbfffd" [] "%F2%BF%BF%BD"
test "\xc0000" [] "%F3%80%80%80"
test "\xcfffd" [] "%F3%8F%BF%BD"
test "\xd0000" [] "%F3%90%80%80"
test "\xdfffd" [] "%F3%9F%BF%BD"
test "\xe1000" [] "%F3%A1%80%80"
test "\xefffd" [] "%F3%AF%BF%BD"
label "accepts private literals" $ do
test "\xe000" [] "%EE%80%80"
test "\xf8ff" [] "%EF%A3%BF"
test "\xf0000" [] "%F3%B0%80%80"
test "\xffffd" [] "%F3%BF%BF%BD"
test "\x100000" [] "%F4%80%80%80"
test "\x10fffd" [] "%F4%8F%BF%BD"
label "passes percent encoded literals through" $ do
test "%00" [] "%00"
label "normalizes percent encodings to uppercase" $ do
test "%AA" [] "%AA"
test "%Aa" [] "%AA"
test "%aA" [] "%AA"
test "%aa" [] "%AA"
label "rejects invalid percent encodings" $ do
test "%" [] Failure
test "%0" [] Failure
test "%0z" [] Failure
test "%z" [] Failure
label "does not decode percent encoded literals" $ do
test "%30" [] "%30"
label "rejects invalid literals" $ do
test " " [] Failure
test "\"" [] Failure
test "'" [] Failure
test "%" [] Failure
test "<" [] Failure
test ">" [] Failure
test "\\" [] Failure
test "^" [] Failure
test "`" [] Failure
test "{" [] Failure
test "|" [] Failure
test "}" [] Failure
label "rejects empty variable names" $ do
test "{}" [] Failure
test "{,}" [] Failure
test "{a,,b}" [] Failure
test "{+}" [] Failure
test "{:1}" [] Failure
test "{*}" [] Failure
label "accepts uppercase variable names" $ do
test "{AZ}" [] ""
label "accepts lowercase variable names" $ do
test "{az}" [] ""
label "accepts decimal variable names" $ do
test "{09}" [] ""
label "accepts underscores in variable names" $ do
test "{_a}" [] ""
test "{a_}" [] ""
test "{_}" [] ""
label "accepts dots in variable names" $ do
test "{A.A}" [] ""
test "{a.a}" [] ""
test "{0.0}" [] ""
test "{_._}" [] ""
test "{%aa.%aa}" [] ""
label "rejects invalid dots in variable names" $ do
test "{.}" [] Failure
test "{a.}" [] Failure
test "{+.a}" [] Failure
test "{a..b}" [] Failure
label "accepts percent encoded variable names" $ do
test "{%00}" [] ""
-- It's unclear if percent encoded triplets in variable names should be case
-- sensitive or not. Section 2.3 says: "Variable names are case-sensitive
-- because the name might be expanded within a case-sensitive URI component."
-- But the HEXDIG rule in section 1.5 says: "; case-insensitive". I think
-- it's safe to assume they are case sensitive because they can appear in an
-- expansion. For example:
--
-- render "{;%aa}" [("%aa", "A")] ==> ";%aa=A"
--
-- However percent encoded triplets that only differ by case would be decoded
-- into the same octet anyway.
label "does not normalize percent encoded variable names" $ do
test "{%AA}" ["%AA" =: "upper-upper"] "upper-upper"
test "{%Aa}" ["%Aa" =: "upper-lower"] "upper-lower"
test "{%aA}" ["%aA" =: "lower-upper"] "lower-upper"
test "{%aa}" ["%aa" =: "lower-lower"] "lower-lower"
label "rejects invalid percent encoded variable names" $ do
test "{%}" [] Failure
test "{%0}" [] Failure
test "{%0z}" [] Failure
test "{%z}" [] Failure
label "rejects invalid variable names" $ do
test "{!}" [] Failure
label "rejects invalid expressions" $ do
test "{" [] Failure
test "{{}" [] Failure
test "}" [] Failure
test "{}}" [] Failure
label "accepts multiple variables in one expression" $ do
test "{a,b}" [] ""
test "{a,b,c,d}" [] ""
test "{a,a}" [] ""
label "accepts prefix modifiers" $ do
test "{a:5}" [] ""
test "{a:67}" [] ""
test "{a:801}" [] ""
test "{a:234}" [] ""
test "{a:9999}" [] ""
label "applies prefix modifiers" $ do
let values = ["a" =: "abcdefghijklmnopqrstuvwxyz"] :: Values
test "{a:1}" values "a"
test "{a:5}" values "abcde"
test "{a:10}" values "abcdefghij"
test "{a:15}" values "abcdefghijklmno"
test "{a:20}" values "abcdefghijklmnopqrst"
test "{a:25}" values "abcdefghijklmnopqrstuvwxy"
test "{a:30}" values "abcdefghijklmnopqrstuvwxyz"
label "rejects invalid prefix modifiers" $ do
test "{a:}" [] Failure
test "{a:0}" [] Failure
test "{a:10000}" [] Failure
test "{a:-1}" [] Failure
label "accepts explode modifiers" $ do
test "{a*}" [] ""
label "rejects both prefix and explode modifiers" $ do
test "{a:1*}" [] Failure
test "{a*:1}" [] Failure
label "accepts different modifiers on different variables" $ do
test "{a,b:1,c*}" [] ""
label "accepts allowed operators" $ do
test "{+a}" [] ""
test "{#a}" [] ""
test "{.a}" [] ""
test "{/a}" [] ""
test "{;a}" [] ""
test "{?a}" [] ""
test "{&a}" [] ""
label "rejects reserved operators" $ do
test "{=a}" [] Failure
test "{,a}" [] Failure
test "{!a}" [] Failure
test "{@a}" [] Failure
test "{|a}" [] Failure
label "rejects multiple operators" $ do
test "{+#a}" [] Failure
label "rejects different operators for different variables" $ do
test "{+a,#b}" [] Failure
label "accepts operators and modifiers" $ do
test "{+a:1}" [] ""
test "{#a*}" [] ""
label "accepts multiple variables with an operator" $ do
test "{+a,b}" [] ""
test "{#a,b}" [] ""
test "{.a,b}" [] ""
test "{/a,b}" [] ""
test "{;a,b}" [] ""
test "{?a,b}" [] ""
test "{&a,b}" [] ""
label "accepts multiple expressions" $ do
test "{a}{b}" [] ""
test "{a}{b}{c}{d}" [] ""
test "{a}{a}" [] ""
label "rejects nested expressions" $ do
test "{{}}" [] Failure
test "{a{b}}" [] Failure
test "{{a}b}" [] Failure
test "{a{b}c}" [] Failure
label "accepts literals and expressions together" $ do
test "a{b}" [] "a"
test "{a}b" [] "b"
test "a{b}c" [] "ac"
test "{a}b{c}" [] "b"
label "handles missing values" $ do
test "{a}" [] ""
test "{+a}" [] ""
test "{#a}" [] ""
test "{.a}" [] ""
test "{/a}" [] ""
test "{;a}" [] ""
test "{?a}" [] ""
test "{&a}" [] ""
label "handles empty list values" $ do
let values = ["a" =: emptyList] :: Values
test "{a}" values ""
test "{+a}" values ""
test "{#a}" values ""
test "{.a}" values ""
test "{/a}" values ""
test "{;a}" values ""
test "{?a}" values ""
test "{&a}" values ""
label "handles empty dictionary values" $ do
let values = ["a" =: emptyDictionary] :: Values
test "{a}" values ""
test "{+a}" values ""
test "{#a}" values ""
test "{.a}" values ""
test "{/a}" values ""
test "{;a}" values ""
test "{?a}" values ""
test "{&a}" values ""
label "handles empty string values" $ do
let values = ["a" =: ""] :: Values
test "{a}" values ""
test "{+a}" values ""
test "{#a}" values "#"
test "{.a}" values "."
test "{/a}" values "/"
test "{;a}" values ";a"
test "{?a}" values "?a="
test "{&a}" values "&a="
label "handles nonempty string values" $ do
let values = ["a" =: "A"] :: Values
test "{a}" values "A"
test "{+a}" values "A"
test "{#a}" values "#A"
test "{.a}" values ".A"
test "{/a}" values "/A"
test "{;a}" values ";a=A"
test "{?a}" values "?a=A"
test "{&a}" values "&a=A"
label "handles a mix of defined and undefined values" $ do
let values = ["b" =: "B"] :: Values
test "{a,b}" values "B"
test "{+a,b}" values "B"
test "{#a,b}" values "#B"
test "{.a,b}" values ".B"
test "{/a,b}" values "/B"
test "{;a,b}" values ";b=B"
test "{?a,b}" values "?b=B"
test "{&a,b}" values "&b=B"
label "handles multiple empty string values" $ do
let values = ["a" =: ""] :: Values
test "{a,a}" values ","
test "{+a,a}" values ","
test "{#a,a}" values "#,"
test "{.a,a}" values ".."
test "{/a,a}" values "//"
test "{;a,a}" values ";a;a"
test "{?a,a}" values "?a=&a="
test "{&a,a}" values "&a=&a="
label "handles multiple non-empty string values" $ do
let values = ["a" =: "A", "b" =: "B"] :: Values
test "{a,b}" values "A,B"
test "{+a,b}" values "A,B"
test "{#a,b}" values "#A,B"
test "{.a,b}" values ".A.B"
test "{/a,b}" values "/A/B"
test "{;a,b}" values ";a=A;b=B"
test "{?a,b}" values "?a=A&b=B"
test "{&a,b}" values "&a=A&b=B"
label "escapes characters in composite dictionaries" $ do
let values = ["a" =: ["K! \xa0\xd7ff\x10000" =: "V! \xa0\xd7ff\x10000"]] :: Values
test "{a*}" values "K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"
test "{+a*}" values "K!%20%C2%A0%ED%9F%BF%F0%90%80%80=V!%20%C2%A0%ED%9F%BF%F0%90%80%80"
test "{#a*}" values "#K!%20%C2%A0%ED%9F%BF%F0%90%80%80=V!%20%C2%A0%ED%9F%BF%F0%90%80%80"
test "{.a*}" values ".K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"
test "{/a*}" values "/K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"
test "{;a*}" values ";K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"
test "{?a*}" values "?K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"
test "{&a*}" values "&K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"
label "prefers the first variable" $ do
test "{a}" ["a" =: "A", "a" =: "B"] "A"
test "{a}" ["a" =: "B", "a" =: "A"] "B"
label "passes test from rfc" $ do
label "section 1.1" $ do
test "http://example.com/~{username}/" ["username" =: "fred"] "http://example.com/~fred/"
test "http://example.com/~{username}/" ["username" =: "mark"] "http://example.com/~mark/"
test "http://example.com/dictionary/{term:1}/{term}" ["term" =: "cat"] "http://example.com/dictionary/c/cat"
test "http://example.com/dictionary/{term:1}/{term}" ["term" =: "dog"] "http://example.com/dictionary/d/dog"
test "http://example.com/search{?q,lang}" ["q" =: "cat", "lang" =: "en"] "http://example.com/search?q=cat&lang=en"
test "http://example.com/search{?q,lang}" ["q" =: "chien", "lang" =: "fr"] "http://example.com/search?q=chien&lang=fr"
test "http://www.example.com/foo{?query,number}" ["query" =: "mycelium", "number" =: "100"] "http://www.example.com/foo?query=mycelium&number=100"
test "http://www.example.com/foo{?query,number}" ["number" =: "100"] "http://www.example.com/foo?number=100"
test "http://www.example.com/foo{?query,number}" [] "http://www.example.com/foo"
label "section 1.2" $ do
let
values =
[ "empty" =: ""
, "hello" =: "Hello World!"
, "keys" =: ["semi" =: ";", "dot" =: ".", "comma" =: ","]
, "list" =: ["red", "green", "blue"]
, "path" =: "/foo/bar"
, "var" =: "value"
, "x" =: "1024"
, "y" =: "768"
] :: Values
label "level 1" $ do
test "{var}" values "value"
test "{hello}" values "Hello%20World%21"
label "level 2" $ do
test "{+var}" values "value"
test "{+hello}" values "Hello%20World!"
test "{+path}/here" values "/foo/bar/here"
test "here?ref={+path}" values "here?ref=/foo/bar"
test "X{#var}" values "X#value"
test "X{#hello}" values "X#Hello%20World!"
label "level 3" $ do
test "map?{x,y}" values "map?1024,768"
test "{x,hello,y}" values "1024,Hello%20World%21,768"
test "{+x,hello,y}" values "1024,Hello%20World!,768"
test "{+path,x}/here" values "/foo/bar,1024/here"
test "{#x,hello,y}" values "#1024,Hello%20World!,768"
test "{#path,x}/here" values "#/foo/bar,1024/here"
test "X{.var}" values "X.value"
test "X{.x,y}" values "X.1024.768"
test "{/var}" values "/value"
test "{/var,x}/here" values "/value/1024/here"
test "{;x,y}" values ";x=1024;y=768"
test "{;x,y,empty}" values ";x=1024;y=768;empty"
test "{?x,y}" values "?x=1024&y=768"
test "{?x,y,empty}" values "?x=1024&y=768&empty="
test "?fixed=yes{&x}" values "?fixed=yes&x=1024"
test "{&x,y,empty}" values "&x=1024&y=768&empty="
label "level 4" $ do
test "{var:3}" values "val"
test "{var:30}" values "value"
test "{list}" values "red,green,blue"
test "{list*}" values "red,green,blue"
test "{keys}" values "semi,%3B,dot,.,comma,%2C"
test "{keys*}" values "semi=%3B,dot=.,comma=%2C"
test "{+path:6}/here" values "/foo/b/here"
test "{+list}" values "red,green,blue"
test "{+list*}" values "red,green,blue"
test "{+keys}" values "semi,;,dot,.,comma,,"
test "{+keys*}" values "semi=;,dot=.,comma=,"
test "{#path:6}/here" values "#/foo/b/here"
test "{#list}" values "#red,green,blue"
test "{#list*}" values "#red,green,blue"
test "{#keys}" values "#semi,;,dot,.,comma,,"
test "{#keys*}" values "#semi=;,dot=.,comma=,"
test "X{.var:3}" values "X.val"
test "X{.list}" values "X.red,green,blue"
test "X{.list*}" values "X.red.green.blue"
test "X{.keys}" values "X.semi,%3B,dot,.,comma,%2C"
test "X{.keys*}" values "X.semi=%3B.dot=..comma=%2C"
test "{/var:1,var}" values "/v/value"
test "{/list}" values "/red,green,blue"
test "{/list*}" values "/red/green/blue"
test "{/list*,path:4}" values "/red/green/blue/%2Ffoo"
test "{/keys}" values "/semi,%3B,dot,.,comma,%2C"
test "{/keys*}" values "/semi=%3B/dot=./comma=%2C"
test "{;hello:5}" values ";hello=Hello"
test "{;list}" values ";list=red,green,blue"
test "{;list*}" values ";list=red;list=green;list=blue"
test "{;keys}" values ";keys=semi,%3B,dot,.,comma,%2C"
test "{;keys*}" values ";semi=%3B;dot=.;comma=%2C"
test "{?var:3}" values "?var=val"
test "{?list}" values "?list=red,green,blue"
test "{?list*}" values "?list=red&list=green&list=blue"
test "{?keys}" values "?keys=semi,%3B,dot,.,comma,%2C"
test "{?keys*}" values "?semi=%3B&dot=.&comma=%2C"
test "{&var:3}" values "&var=val"
test "{&list}" values "&list=red,green,blue"
test "{&list*}" values "&list=red&list=green&list=blue"
test "{&keys}" values "&keys=semi,%3B,dot,.,comma,%2C"
test "{&keys*}" values "&semi=%3B&dot=.&comma=%2C"
label "section 2.4.1" $ do
let values = ["var" =: "value", "semi" =: ";"] :: Values
test "{var}" values "value"
test "{var:20}" values "value"
test "{var:3}" values "val"
test "{semi}" values "%3B"
test "{semi:2}" values "%3B"
label "section 2.4.2" $ do
let values = ["year" =: ["1965", "2000", "2012"], "dom" =: ["example", "com"]] :: Values
test "find{?year*}" values "find?year=1965&year=2000&year=2012"
test "www{.dom*}" values "www.example.com"
label "section 3.1" $ do
let
values =
[ "base" =: "http://example.com/home/"
, "count" =: ["one", "two", "three"]
, "dom" =: ["example", "com"]
, "dub" =: "me/too"
, "empty_keys" =: emptyDictionary
, "empty" =: ""
, "half" =: "50%"
, "hello" =: "Hello World!"
, "keys" =: ["semi" =: ";", "dot" =: ".", "comma" =: ","]
, "list" =: ["red", "green", "blue"]
, "path" =: "/foo/bar"
, "v" =: "6"
, "var" =: "value"
, "who" =: "fred"
, "x" =: "1024"
, "y" =: "768"
] :: Values
label "subsection 1" $ do
test "{count}" values "one,two,three"
test "{count*}" values "one,two,three"
test "{/count}" values "/one,two,three"
test "{/count*}" values "/one/two/three"
test "{;count}" values ";count=one,two,three"
test "{;count*}" values ";count=one;count=two;count=three"
test "{?count}" values "?count=one,two,three"
test "{?count*}" values "?count=one&count=two&count=three"
test "{&count*}" values "&count=one&count=two&count=three"
label "subsection 2" $ do
test "{var}" values "value"
test "{hello}" values "Hello%20World%21"
test "{half}" values "50%25"
test "O{empty}X" values "OX"
test "O{undef}X" values "OX"
test "{x,y}" values "1024,768"
test "{x,hello,y}" values "1024,Hello%20World%21,768"
test "?{x,empty}" values "?1024,"
test "?{x,undef}" values "?1024"
test "?{undef,y}" values "?768"
test "{var:3}" values "val"
test "{var:30}" values "value"
test "{list}" values "red,green,blue"
test "{list*}" values "red,green,blue"
test "{keys}" values "semi,%3B,dot,.,comma,%2C"
test "{keys*}" values "semi=%3B,dot=.,comma=%2C"
label "subsection 3" $ do
test "{+var}" values "value"
test "{+hello}" values "Hello%20World!"
test "{+half}" values "50%25"
test "{base}index" values "http%3A%2F%2Fexample.com%2Fhome%2Findex"
test "{+base}index" values "http://example.com/home/index"
test "O{+empty}X" values "OX"
test "O{+undef}X" values "OX"
test "{+path}/here" values "/foo/bar/here"
test "here?ref={+path}" values "here?ref=/foo/bar"
test "up{+path}{var}/here" values "up/foo/barvalue/here"
test "{+x,hello,y}" values "1024,Hello%20World!,768"
test "{+path,x}/here" values "/foo/bar,1024/here"
test "{+path:6}/here" values "/foo/b/here"
test "{+list}" values "red,green,blue"
test "{+list*}" values "red,green,blue"
test "{+keys}" values "semi,;,dot,.,comma,,"
test "{+keys*}" values "semi=;,dot=.,comma=,"
label "subsection 4" $ do
test "{#var}" values "#value"
test "{#hello}" values "#Hello%20World!"
test "{#half}" values "#50%25"
test "foo{#empty}" values "foo#"
test "foo{#undef}" values "foo"
test "{#x,hello,y}" values "#1024,Hello%20World!,768"
test "{#path,x}/here" values "#/foo/bar,1024/here"
test "{#path:6}/here" values "#/foo/b/here"
test "{#list}" values "#red,green,blue"
test "{#list*}" values "#red,green,blue"
test "{#keys}" values "#semi,;,dot,.,comma,,"
test "{#keys*}" values "#semi=;,dot=.,comma=,"
label "subsection 5" $ do
test "{.who}" values ".fred"
test "{.who,who}" values ".fred.fred"
test "{.half,who}" values ".50%25.fred"
test "www{.dom*}" values "www.example.com"
test "X{.var}" values "X.value"
test "X{.empty}" values "X."
test "X{.undef}" values "X"
test "X{.var:3}" values "X.val"
test "X{.list}" values "X.red,green,blue"
test "X{.list*}" values "X.red.green.blue"
test "X{.keys}" values "X.semi,%3B,dot,.,comma,%2C"
test "X{.keys*}" values "X.semi=%3B.dot=..comma=%2C"
test "X{.empty_keys}" values "X"
test "X{.empty_keys*}" values "X"
label "subsection 6" $ do
test "{/who}" values "/fred"
test "{/who,who}" values "/fred/fred"
test "{/half,who}" values "/50%25/fred"
test "{/who,dub}" values "/fred/me%2Ftoo"
test "{/var}" values "/value"
test "{/var,empty}" values "/value/"
test "{/var,undef}" values "/value"
test "{/var,x}/here" values "/value/1024/here"
test "{/var:1,var}" values "/v/value"
test "{/list}" values "/red,green,blue"
test "{/list*}" values "/red/green/blue"
test "{/list*,path:4}" values "/red/green/blue/%2Ffoo"
test "{/keys}" values "/semi,%3B,dot,.,comma,%2C"
test "{/keys*}" values "/semi=%3B/dot=./comma=%2C"
label "subsection 7" $ do
test "{;who}" values ";who=fred"
test "{;half}" values ";half=50%25"
test "{;empty}" values ";empty"
test "{;v,empty,who}" values ";v=6;empty;who=fred"
test "{;v,bar,who}" values ";v=6;who=fred"
test "{;x,y}" values ";x=1024;y=768"
test "{;x,y,empty}" values ";x=1024;y=768;empty"
test "{;x,y,undef}" values ";x=1024;y=768"
test "{;hello:5}" values ";hello=Hello"
test "{;list}" values ";list=red,green,blue"
test "{;list*}" values ";list=red;list=green;list=blue"
test "{;keys}" values ";keys=semi,%3B,dot,.,comma,%2C"
test "{;keys*}" values ";semi=%3B;dot=.;comma=%2C"
label "subsection 8" $ do
test "{?who}" values "?who=fred"
test "{?half}" values "?half=50%25"
test "{?x,y}" values "?x=1024&y=768"
test "{?x,y,empty}" values "?x=1024&y=768&empty="
test "{?x,y,undef}" values "?x=1024&y=768"
test "{?var:3}" values "?var=val"
test "{?list}" values "?list=red,green,blue"
test "{?list*}" values "?list=red&list=green&list=blue"
test "{?keys}" values "?keys=semi,%3B,dot,.,comma,%2C"
test "{?keys*}" values "?semi=%3B&dot=.&comma=%2C"
label "subsection 9" $ do
test "{&who}" values "&who=fred"
test "{&half}" values "&half=50%25"
test "?fixed=yes{&x}" values "?fixed=yes&x=1024"
test "{&x,y,empty}" values "&x=1024&y=768&empty="
test "{&x,y,undef}" values "&x=1024&y=768"
test "{&var:3}" values "&var=val"
test "{&list}" values "&list=red,green,blue"
test "{&list*}" values "&list=red&list=green&list=blue"
test "{&keys}" values "&keys=semi,%3B,dot,.,comma,%2C"
test "{&keys*}" values "&semi=%3B&dot=.&comma=%2C"
label "handles simple expansion" $ do
test "{a}" [] ""
test "{a}" ["a" =: emptyList] ""
test "{a}" ["a" =: emptyDictionary] ""
test "{a}" ["a" =: ""] ""
test "{a}" ["a" =: "A"] "A"
test "{a}" ["a" =: "~"] "~"
test "{a}" ["a" =: "%"] "%25"
test "{a}" ["a" =: "?"] "%3F"
test "{a}" ["a" =: "&"] "%26"
test "{a}" ["a" =: "\xa0"] "%C2%A0"
test "{a}" ["a" =: "\xd7ff"] "%ED%9F%BF"
test "{a}" ["a" =: "\x10000"] "%F0%90%80%80"
test "{a}" ["a" =: ["A"]] "A"
test "{a}" ["a" =: ["A", "B"]] "A,B"
test "{a}" ["a" =: ["%"]] "%25"
test "{a}" ["a" =: ["\xa0"]] "%C2%A0"
test "{a}" ["a" =: ["A" =: "1"]] "A,1"
test "{a}" ["a" =: ["A" =: "1", "B" =: "2"]] "A,1,B,2"
test "{a}" ["a" =: ["A" =: "%"]] "A,%25"
test "{a}" ["a" =: ["A" =: "\xa0"]] "A,%C2%A0"
test "{a}" ["a" =: ["%" =: "1"]] "%25,1"
test "{a*}" [] ""
test "{a*}" ["a" =: ""] ""
test "{a*}" ["a" =: "A"] "A"
test "{a*}" ["a" =: emptyList] ""
test "{a*}" ["a" =: ["A"]] "A"
test "{a*}" ["a" =: ["A", "B"]] "A,B"
test "{a*}" ["a" =: emptyDictionary] ""
test "{a*}" ["a" =: ["A" =: "1"]] "A=1"
test "{a*}" ["a" =: ["A" =: "1", "B" =: "2"]] "A=1,B=2"
test "{a:1}" [] ""
test "{a:1}" ["a" =: ""] ""
test "{a:1}" ["a" =: "A"] "A"
test "{a:1}" ["a" =: "AB"] "A"
test "{a:1}" ["a" =: "%B"] "%25"
test "{a:1}" ["a" =: "\xa0\&B"] "%C2%A0"
test "{a:1}" ["a" =: "\xd7ff\&B"] "%ED%9F%BF"
test "{a:1}" ["a" =: "\x10000\&B"] "%F0%90%80%80"
test "{a:1}" ["a" =: emptyList] ""
test "{a:1}" ["a" =: ["AB"]] "AB"
test "{a:1}" ["a" =: ["AB", "CD"]] "AB,CD"
test "{a:1}" ["a" =: emptyDictionary] ""
test "{a:1}" ["a" =: ["AB" =: "12"]] "AB,12"
test "{a:1}" ["a" =: ["AB" =: "12", "CD" =: "34"]] "AB,12,CD,34"
test "{a,a}" [] ""
test "{a,a}" ["a" =: emptyList] ""
test "{a,a}" ["a" =: emptyDictionary] ""
test "{a,a}" ["a" =: ""] ","
test "{a,b}" ["a" =: ""] ""
test "{a,b}" ["b" =: ""] ""
test "{%aa}" ["%aa" =: "A"] "A"
test "{%aa}" ["%aa" =: ["A", "B"]] "A,B"
test "{%aa}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "A,1,B,2"
test "{%aa*}" ["%aa" =: "A"] "A"
test "{%aa*}" ["%aa" =: ["A", "B"]] "A,B"
test "{%aa*}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "A=1,B=2"
label "handles reserved expansion" $ do
test "{+a}" [] ""
test "{+a}" ["a" =: emptyList] ""
test "{+a}" ["a" =: emptyDictionary] ""
test "{+a}" ["a" =: ""] ""
test "{+a}" ["a" =: "A"] "A"
test "{+a}" ["a" =: "~"] "~"
test "{+a}" ["a" =: "%"] "%25"
test "{+a}" ["a" =: "?"] "?"
test "{+a}" ["a" =: "&"] "&"
test "{+a}" ["a" =: "\xa0"] "%C2%A0"
test "{+a}" ["a" =: "\xd7ff"] "%ED%9F%BF"
test "{+a}" ["a" =: "\x10000"] "%F0%90%80%80"
test "{+a}" ["a" =: ["A"]] "A"
test "{+a}" ["a" =: ["A", "B"]] "A,B"
test "{+a}" ["a" =: ["%"]] "%25"
test "{+a}" ["a" =: ["\xa0"]] "%C2%A0"
test "{+a}" ["a" =: ["A" =: "1"]] "A,1"
test "{+a}" ["a" =: ["A" =: "1", "B" =: "2"]] "A,1,B,2"
test "{+a}" ["a" =: ["A" =: "%"]] "A,%25"
test "{+a}" ["a" =: ["A" =: "\xa0"]] "A,%C2%A0"
test "{+a}" ["a" =: ["%" =: "1"]] "%25,1"
test "{+a*}" [] ""
test "{+a*}" ["a" =: ""] ""
test "{+a*}" ["a" =: "A"] "A"
test "{+a*}" ["a" =: emptyList] ""
test "{+a*}" ["a" =: ["A"]] "A"
test "{+a*}" ["a" =: ["A", "B"]] "A,B"
test "{+a*}" ["a" =: emptyDictionary] ""
test "{+a*}" ["a" =: ["A" =: "1"]] "A=1"
test "{+a*}" ["a" =: ["A" =: "1", "B" =: "2"]] "A=1,B=2"
test "{+a:1}" [] ""
test "{+a:1}" ["a" =: ""] ""
test "{+a:1}" ["a" =: "A"] "A"
test "{+a:1}" ["a" =: "AB"] "A"
test "{+a:1}" ["a" =: "%B"] "%25"
test "{+a:1}" ["a" =: "\xa0\&B"] "%C2%A0"
test "{+a:1}" ["a" =: "\xd7ff\&B"] "%ED%9F%BF"
test "{+a:1}" ["a" =: "\x10000\&B"] "%F0%90%80%80"
test "{+a:1}" ["a" =: emptyList] ""
test "{+a:1}" ["a" =: ["AB"]] "AB"
test "{+a:1}" ["a" =: ["AB", "CD"]] "AB,CD"
test "{+a:1}" ["a" =: emptyDictionary] ""
test "{+a:1}" ["a" =: ["AB" =: "12"]] "AB,12"
test "{+a:1}" ["a" =: ["AB" =: "12", "CD" =: "34"]] "AB,12,CD,34"
test "{+a,a}" [] ""
test "{+a,a}" ["a" =: emptyList] ""
test "{+a,a}" ["a" =: emptyDictionary] ""
test "{+a,a}" ["a" =: ""] ","
test "{+a,b}" ["a" =: ""] ""
test "{+a,b}" ["b" =: ""] ""
test "{+%aa}" ["%aa" =: "A"] "A"
test "{+%aa}" ["%aa" =: ["A", "B"]] "A,B"
test "{+%aa}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "A,1,B,2"
test "{+%aa*}" ["%aa" =: "A"] "A"
test "{+%aa*}" ["%aa" =: ["A", "B"]] "A,B"
test "{+%aa*}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "A=1,B=2"
label "handles fragment expansion" $ do
test "{#a}" [] ""
test "{#a}" ["a" =: emptyList] ""
test "{#a}" ["a" =: emptyDictionary] ""
test "{#a}" ["a" =: ""] "#"
test "{#a}" ["a" =: "A"] "#A"
test "{#a}" ["a" =: "~"] "#~"
test "{#a}" ["a" =: "%"] "#%25"
test "{#a}" ["a" =: "?"] "#?"
test "{#a}" ["a" =: "&"] "#&"
test "{#a}" ["a" =: "\xa0"] "#%C2%A0"
test "{#a}" ["a" =: "\xd7ff"] "#%ED%9F%BF"
test "{#a}" ["a" =: "\x10000"] "#%F0%90%80%80"
test "{#a}" ["a" =: ["A"]] "#A"
test "{#a}" ["a" =: ["A", "B"]] "#A,B"
test "{#a}" ["a" =: ["%"]] "#%25"
test "{#a}" ["a" =: ["\xa0"]] "#%C2%A0"
test "{#a}" ["a" =: ["A" =: "1"]] "#A,1"
test "{#a}" ["a" =: ["A" =: "1", "B" =: "2"]] "#A,1,B,2"
test "{#a}" ["a" =: ["A" =: "%"]] "#A,%25"
test "{#a}" ["a" =: ["A" =: "\xa0"]] "#A,%C2%A0"
test "{#a}" ["a" =: ["%" =: "1"]] "#%25,1"
test "{#a*}" [] ""
test "{#a*}" ["a" =: ""] "#"
test "{#a*}" ["a" =: "A"] "#A"
test "{#a*}" ["a" =: emptyList] ""
test "{#a*}" ["a" =: ["A"]] "#A"
test "{#a*}" ["a" =: ["A", "B"]] "#A,B"
test "{#a*}" ["a" =: emptyDictionary] ""
test "{#a*}" ["a" =: ["A" =: "1"]] "#A=1"
test "{#a*}" ["a" =: ["A" =: "1", "B" =: "2"]] "#A=1,B=2"
test "{#a:1}" [] ""
test "{#a:1}" ["a" =: ""] "#"
test "{#a:1}" ["a" =: "A"] "#A"
test "{#a:1}" ["a" =: "AB"] "#A"
test "{#a:1}" ["a" =: "%B"] "#%25"
test "{#a:1}" ["a" =: "\xa0\&B"] "#%C2%A0"
test "{#a:1}" ["a" =: "\xd7ff\&B"] "#%ED%9F%BF"
test "{#a:1}" ["a" =: "\x10000\&B"] "#%F0%90%80%80"
test "{#a:1}" ["a" =: emptyList] ""
test "{#a:1}" ["a" =: ["AB"]] "#AB"
test "{#a:1}" ["a" =: ["AB", "CD"]] "#AB,CD"
test "{#a:1}" ["a" =: emptyDictionary] ""
test "{#a:1}" ["a" =: ["AB" =: "12"]] "#AB,12"
test "{#a:1}" ["a" =: ["AB" =: "12", "CD" =: "34"]] "#AB,12,CD,34"
test "{#a,a}" [] ""
test "{#a,a}" ["a" =: emptyList] ""
test "{#a,a}" ["a" =: emptyDictionary] ""
test "{#a,a}" ["a" =: ""] "#,"
test "{#a,b}" ["a" =: ""] "#"
test "{#a,b}" ["b" =: ""] "#"
test "{#%aa}" ["%aa" =: "A"] "#A"
test "{#%aa}" ["%aa" =: ["A", "B"]] "#A,B"
test "{#%aa}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "#A,1,B,2"
test "{#%aa*}" ["%aa" =: "A"] "#A"
test "{#%aa*}" ["%aa" =: ["A", "B"]] "#A,B"
test "{#%aa*}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "#A=1,B=2"
label "handles label expansion" $ do
test "{.a}" [] ""
test "{.a}" ["a" =: emptyList] ""
test "{.a}" ["a" =: emptyDictionary] ""
test "{.a}" ["a" =: ""] "."
test "{.a}" ["a" =: "A"] ".A"
test "{.a}" ["a" =: "~"] ".~"
test "{.a}" ["a" =: "%"] ".%25"
test "{.a}" ["a" =: "?"] ".%3F"
test "{.a}" ["a" =: "&"] ".%26"
test "{.a}" ["a" =: "\xa0"] ".%C2%A0"
test "{.a}" ["a" =: "\xd7ff"] ".%ED%9F%BF"
test "{.a}" ["a" =: "\x10000"] ".%F0%90%80%80"
test "{.a}" ["a" =: ["A"]] ".A"
test "{.a}" ["a" =: ["A", "B"]] ".A,B"
test "{.a}" ["a" =: ["%"]] ".%25"
test "{.a}" ["a" =: ["\xa0"]] ".%C2%A0"
test "{.a}" ["a" =: ["A" =: "1"]] ".A,1"
test "{.a}" ["a" =: ["A" =: "1", "B" =: "2"]] ".A,1,B,2"
test "{.a}" ["a" =: ["A" =: "%"]] ".A,%25"
test "{.a}" ["a" =: ["A" =: "\xa0"]] ".A,%C2%A0"
test "{.a}" ["a" =: ["%" =: "1"]] ".%25,1"
test "{.a*}" [] ""
test "{.a*}" ["a" =: ""] "."
test "{.a*}" ["a" =: "A"] ".A"
test "{.a*}" ["a" =: emptyList] ""
test "{.a*}" ["a" =: ["A"]] ".A"
test "{.a*}" ["a" =: ["A", "B"]] ".A.B"
test "{.a*}" ["a" =: emptyDictionary] ""
test "{.a*}" ["a" =: ["A" =: "1"]] ".A=1"
test "{.a*}" ["a" =: ["A" =: "1", "B" =: "2"]] ".A=1.B=2"
test "{.a:1}" [] ""
test "{.a:1}" ["a" =: ""] "."
test "{.a:1}" ["a" =: "A"] ".A"
test "{.a:1}" ["a" =: "AB"] ".A"
test "{.a:1}" ["a" =: "%B"] ".%25"
test "{.a:1}" ["a" =: "\xa0\&B"] ".%C2%A0"
test "{.a:1}" ["a" =: "\xd7ff\&B"] ".%ED%9F%BF"
test "{.a:1}" ["a" =: "\x10000\&B"] ".%F0%90%80%80"
test "{.a:1}" ["a" =: emptyList] ""
test "{.a:1}" ["a" =: ["AB"]] ".AB"
test "{.a:1}" ["a" =: ["AB", "CD"]] ".AB,CD"
test "{.a:1}" ["a" =: emptyDictionary] ""
test "{.a:1}" ["a" =: ["AB" =: "12"]] ".AB,12"
test "{.a:1}" ["a" =: ["AB" =: "12", "CD" =: "34"]] ".AB,12,CD,34"
test "{.a,a}" [] ""
test "{.a,a}" ["a" =: emptyList] ""
test "{.a,a}" ["a" =: emptyDictionary] ""
test "{.a,a}" ["a" =: ""] ".."
test "{.a,b}" ["a" =: ""] "."
test "{.a,b}" ["b" =: ""] "."
test "{.%aa}" ["%aa" =: "A"] ".A"
test "{.%aa}" ["%aa" =: ["A", "B"]] ".A,B"
test "{.%aa}" ["%aa" =: ["A" =: "1", "B" =: "2"]] ".A,1,B,2"
test "{.%aa*}" ["%aa" =: "A"] ".A"
test "{.%aa*}" ["%aa" =: ["A", "B"]] ".A.B"
test "{.%aa*}" ["%aa" =: ["A" =: "1", "B" =: "2"]] ".A=1.B=2"
label "handles segment expansion" $ do
test "{/a}" [] ""
test "{/a}" ["a" =: emptyList] ""
test "{/a}" ["a" =: emptyDictionary] ""
test "{/a}" ["a" =: ""] "/"
test "{/a}" ["a" =: "A"] "/A"
test "{/a}" ["a" =: "~"] "/~"
test "{/a}" ["a" =: "%"] "/%25"
test "{/a}" ["a" =: "?"] "/%3F"
test "{/a}" ["a" =: "&"] "/%26"
test "{/a}" ["a" =: "\xa0"] "/%C2%A0"
test "{/a}" ["a" =: "\xd7ff"] "/%ED%9F%BF"
test "{/a}" ["a" =: "\x10000"] "/%F0%90%80%80"
test "{/a}" ["a" =: ["A"]] "/A"
test "{/a}" ["a" =: ["A", "B"]] "/A,B"
test "{/a}" ["a" =: ["%"]] "/%25"
test "{/a}" ["a" =: ["\xa0"]] "/%C2%A0"
test "{/a}" ["a" =: ["A" =: "1"]] "/A,1"
test "{/a}" ["a" =: ["A" =: "1", "B" =: "2"]] "/A,1,B,2"
test "{/a}" ["a" =: ["A" =: "%"]] "/A,%25"
test "{/a}" ["a" =: ["A" =: "\xa0"]] "/A,%C2%A0"
test "{/a}" ["a" =: ["%" =: "1"]] "/%25,1"
test "{/a*}" [] ""
test "{/a*}" ["a" =: ""] "/"
test "{/a*}" ["a" =: "A"] "/A"
test "{/a*}" ["a" =: emptyList] ""
test "{/a*}" ["a" =: ["A"]] "/A"
test "{/a*}" ["a" =: ["A", "B"]] "/A/B"
test "{/a*}" ["a" =: emptyDictionary] ""
test "{/a*}" ["a" =: ["A" =: "1"]] "/A=1"
test "{/a*}" ["a" =: ["A" =: "1", "B" =: "2"]] "/A=1/B=2"
test "{/a:1}" [] ""
test "{/a:1}" ["a" =: ""] "/"
test "{/a:1}" ["a" =: "A"] "/A"
test "{/a:1}" ["a" =: "AB"] "/A"
test "{/a:1}" ["a" =: "%B"] "/%25"
test "{/a:1}" ["a" =: "\xa0\&B"] "/%C2%A0"
test "{/a:1}" ["a" =: "\xd7ff\&B"] "/%ED%9F%BF"
test "{/a:1}" ["a" =: "\x10000\&B"] "/%F0%90%80%80"
test "{/a:1}" ["a" =: emptyList] ""
test "{/a:1}" ["a" =: ["AB"]] "/AB"
test "{/a:1}" ["a" =: ["AB", "CD"]] "/AB,CD"
test "{/a:1}" ["a" =: emptyDictionary] ""
test "{/a:1}" ["a" =: ["AB" =: "12"]] "/AB,12"
test "{/a:1}" ["a" =: ["AB" =: "12", "CD" =: "34"]] "/AB,12,CD,34"
test "{/a,a}" [] ""
test "{/a,a}" ["a" =: emptyList] ""
test "{/a,a}" ["a" =: emptyDictionary] ""
test "{/a,a}" ["a" =: ""] "//"
test "{/a,b}" ["a" =: ""] "/"
test "{/a,b}" ["b" =: ""] "/"
test "{/%aa}" ["%aa" =: "A"] "/A"
test "{/%aa}" ["%aa" =: ["A", "B"]] "/A,B"
test "{/%aa}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "/A,1,B,2"
test "{/%aa*}" ["%aa" =: "A"] "/A"
test "{/%aa*}" ["%aa" =: ["A", "B"]] "/A/B"
test "{/%aa*}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "/A=1/B=2"
label "handles parameter expansion" $ do
test "{;a}" [] ""
test "{;a}" ["a" =: emptyList] ""
test "{;a}" ["a" =: emptyDictionary] ""
test "{;a}" ["a" =: ""] ";a"
test "{;a}" ["a" =: "A"] ";a=A"
test "{;a}" ["a" =: "~"] ";a=~"
test "{;a}" ["a" =: "%"] ";a=%25"
test "{;a}" ["a" =: "?"] ";a=%3F"
test "{;a}" ["a" =: "&"] ";a=%26"
test "{;a}" ["a" =: "\xa0"] ";a=%C2%A0"
test "{;a}" ["a" =: "\xd7ff"] ";a=%ED%9F%BF"
test "{;a}" ["a" =: "\x10000"] ";a=%F0%90%80%80"
test "{;a}" ["a" =: ["A"]] ";a=A"
test "{;a}" ["a" =: ["A", "B"]] ";a=A,B"
test "{;a}" ["a" =: ["%"]] ";a=%25"
test "{;a}" ["a" =: ["\xa0"]] ";a=%C2%A0"
test "{;a}" ["a" =: ["A" =: "1"]] ";a=A,1"
test "{;a}" ["a" =: ["A" =: "1", "B" =: "2"]] ";a=A,1,B,2"
test "{;a}" ["a" =: ["A" =: "%"]] ";a=A,%25"
test "{;a}" ["a" =: ["A" =: "\xa0"]] ";a=A,%C2%A0"
test "{;a}" ["a" =: ["%" =: "1"]] ";a=%25,1"
test "{;a*}" [] ""
test "{;a*}" ["a" =: ""] ";a"
test "{;a*}" ["a" =: "A"] ";a=A"
test "{;a*}" ["a" =: emptyList] ""
test "{;a*}" ["a" =: ["A"]] ";a=A"
test "{;a*}" ["a" =: ["A", "B"]] ";a=A;a=B"
test "{;a*}" ["a" =: emptyDictionary] ""
test "{;a*}" ["a" =: ["A" =: "1"]] ";A=1"
test "{;a*}" ["a" =: ["A" =: "1", "B" =: "2"]] ";A=1;B=2"
test "{;a:1}" [] ""
test "{;a:1}" ["a" =: ""] ";a"
test "{;a:1}" ["a" =: "A"] ";a=A"
test "{;a:1}" ["a" =: "AB"] ";a=A"
test "{;a:1}" ["a" =: "%B"] ";a=%25"
test "{;a:1}" ["a" =: "\xa0\&B"] ";a=%C2%A0"
test "{;a:1}" ["a" =: "\xd7ff\&B"] ";a=%ED%9F%BF"
test "{;a:1}" ["a" =: "\x10000\&B"] ";a=%F0%90%80%80"
test "{;a:1}" ["a" =: emptyList] ""
test "{;a:1}" ["a" =: ["AB"]] ";a=AB"
test "{;a:1}" ["a" =: ["AB", "CD"]] ";a=AB,CD"
test "{;a:1}" ["a" =: emptyDictionary] ""
test "{;a:1}" ["a" =: ["AB" =: "12"]] ";a=AB,12"
test "{;a:1}" ["a" =: ["AB" =: "12", "CD" =: "34"]] ";a=AB,12,CD,34"
test "{;a,a}" [] ""
test "{;a,a}" ["a" =: emptyList] ""
test "{;a,a}" ["a" =: emptyDictionary] ""
test "{;a,a}" ["a" =: ""] ";a;a"
test "{;a,b}" ["a" =: ""] ";a"
test "{;a,b}" ["b" =: ""] ";b"
test "{;%aa}" ["%aa" =: "A"] ";%aa=A"
test "{;%aa}" ["%aa" =: ["A", "B"]] ";%aa=A,B"
test "{;%aa}" ["%aa" =: ["A" =: "1", "B" =: "2"]] ";%aa=A,1,B,2"
test "{;%aa*}" ["%aa" =: "A"] ";%aa=A"
test "{;%aa*}" ["%aa" =: ["A", "B"]] ";%aa=A;%aa=B"
test "{;%aa*}" ["%aa" =: ["A" =: "1", "B" =: "2"]] ";A=1;B=2"
label "handles query expansion" $ do
test "{?a}" [] ""
test "{?a}" ["a" =: emptyList] ""
test "{?a}" ["a" =: emptyDictionary] ""
test "{?a}" ["a" =: ""] "?a="
test "{?a}" ["a" =: "A"] "?a=A"
test "{?a}" ["a" =: "~"] "?a=~"
test "{?a}" ["a" =: "%"] "?a=%25"
test "{?a}" ["a" =: "?"] "?a=%3F"
test "{?a}" ["a" =: "&"] "?a=%26"
test "{?a}" ["a" =: "\xa0"] "?a=%C2%A0"
test "{?a}" ["a" =: "\xd7ff"] "?a=%ED%9F%BF"
test "{?a}" ["a" =: "\x10000"] "?a=%F0%90%80%80"
test "{?a}" ["a" =: ["A"]] "?a=A"
test "{?a}" ["a" =: ["A", "B"]] "?a=A,B"
test "{?a}" ["a" =: ["%"]] "?a=%25"
test "{?a}" ["a" =: ["\xa0"]] "?a=%C2%A0"
test "{?a}" ["a" =: ["A" =: "1"]] "?a=A,1"
test "{?a}" ["a" =: ["A" =: "1", "B" =: "2"]] "?a=A,1,B,2"
test "{?a}" ["a" =: ["A" =: "%"]] "?a=A,%25"
test "{?a}" ["a" =: ["A" =: "\xa0"]] "?a=A,%C2%A0"
test "{?a}" ["a" =: ["%" =: "1"]] "?a=%25,1"
test "{?a*}" [] ""
test "{?a*}" ["a" =: ""] "?a="
test "{?a*}" ["a" =: "A"] "?a=A"
test "{?a*}" ["a" =: emptyList] ""
test "{?a*}" ["a" =: ["A"]] "?a=A"
test "{?a*}" ["a" =: ["A", "B"]] "?a=A&a=B"
test "{?a*}" ["a" =: emptyDictionary] ""
test "{?a*}" ["a" =: ["A" =: "1"]] "?A=1"
test "{?a*}" ["a" =: ["A" =: "1", "B" =: "2"]] "?A=1&B=2"
test "{?a:1}" [] ""
test "{?a:1}" ["a" =: ""] "?a="
test "{?a:1}" ["a" =: "A"] "?a=A"
test "{?a:1}" ["a" =: "AB"] "?a=A"
test "{?a:1}" ["a" =: "%B"] "?a=%25"
test "{?a:1}" ["a" =: "\xa0\&B"] "?a=%C2%A0"
test "{?a:1}" ["a" =: "\xd7ff\&B"] "?a=%ED%9F%BF"
test "{?a:1}" ["a" =: "\x10000\&B"] "?a=%F0%90%80%80"
test "{?a:1}" ["a" =: emptyList] ""
test "{?a:1}" ["a" =: ["AB"]] "?a=AB"
test "{?a:1}" ["a" =: ["AB", "CD"]] "?a=AB,CD"
test "{?a:1}" ["a" =: emptyDictionary] ""
test "{?a:1}" ["a" =: ["AB" =: "12"]] "?a=AB,12"
test "{?a:1}" ["a" =: ["AB" =: "12", "CD" =: "34"]] "?a=AB,12,CD,34"
test "{?a,a}" [] ""
test "{?a,a}" ["a" =: emptyList] ""
test "{?a,a}" ["a" =: emptyDictionary] ""
test "{?a,a}" ["a" =: ""] "?a=&a="
test "{?a,b}" ["a" =: ""] "?a="
test "{?a,b}" ["b" =: ""] "?b="
test "{?%aa}" ["%aa" =: "A"] "?%aa=A"
test "{?%aa}" ["%aa" =: ["A", "B"]] "?%aa=A,B"
test "{?%aa}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "?%aa=A,1,B,2"
test "{?%aa*}" ["%aa" =: "A"] "?%aa=A"
test "{?%aa*}" ["%aa" =: ["A", "B"]] "?%aa=A&%aa=B"
test "{?%aa*}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "?A=1&B=2"
label "handles continuation expansion" $ do
test "{&a}" [] ""
test "{&a}" ["a" =: emptyList] ""
test "{&a}" ["a" =: emptyDictionary] ""
test "{&a}" ["a" =: ""] "&a="
test "{&a}" ["a" =: "A"] "&a=A"
test "{&a}" ["a" =: "~"] "&a=~"
test "{&a}" ["a" =: "%"] "&a=%25"
test "{&a}" ["a" =: "?"] "&a=%3F"
test "{&a}" ["a" =: "&"] "&a=%26"
test "{&a}" ["a" =: "\xa0"] "&a=%C2%A0"
test "{&a}" ["a" =: "\xd7ff"] "&a=%ED%9F%BF"
test "{&a}" ["a" =: "\x10000"] "&a=%F0%90%80%80"
test "{&a}" ["a" =: ["A"]] "&a=A"
test "{&a}" ["a" =: ["A", "B"]] "&a=A,B"
test "{&a}" ["a" =: ["%"]] "&a=%25"
test "{&a}" ["a" =: ["\xa0"]] "&a=%C2%A0"
test "{&a}" ["a" =: ["A" =: "1"]] "&a=A,1"
test "{&a}" ["a" =: ["A" =: "1", "B" =: "2"]] "&a=A,1,B,2"
test "{&a}" ["a" =: ["A" =: "%"]] "&a=A,%25"
test "{&a}" ["a" =: ["A" =: "\xa0"]] "&a=A,%C2%A0"
test "{&a}" ["a" =: ["%" =: "1"]] "&a=%25,1"
test "{&a*}" [] ""
test "{&a*}" ["a" =: ""] "&a="
test "{&a*}" ["a" =: "A"] "&a=A"
test "{&a*}" ["a" =: emptyList] ""
test "{&a*}" ["a" =: ["A"]] "&a=A"
test "{&a*}" ["a" =: ["A", "B"]] "&a=A&a=B"
test "{&a*}" ["a" =: emptyDictionary] ""
test "{&a*}" ["a" =: ["A" =: "1"]] "&A=1"
test "{&a*}" ["a" =: ["A" =: "1", "B" =: "2"]] "&A=1&B=2"
test "{&a:1}" [] ""
test "{&a:1}" ["a" =: ""] "&a="
test "{&a:1}" ["a" =: "A"] "&a=A"
test "{&a:1}" ["a" =: "AB"] "&a=A"
test "{&a:1}" ["a" =: "%B"] "&a=%25"
test "{&a:1}" ["a" =: "\xa0\&B"] "&a=%C2%A0"
test "{&a:1}" ["a" =: "\xd7ff\&B"] "&a=%ED%9F%BF"
test "{&a:1}" ["a" =: "\x10000\&B"] "&a=%F0%90%80%80"
test "{&a:1}" ["a" =: emptyList] ""
test "{&a:1}" ["a" =: ["AB"]] "&a=AB"
test "{&a:1}" ["a" =: ["AB", "CD"]] "&a=AB,CD"
test "{&a:1}" ["a" =: emptyDictionary] ""
test "{&a:1}" ["a" =: ["AB" =: "12"]] "&a=AB,12"
test "{&a:1}" ["a" =: ["AB" =: "12", "CD" =: "34"]] "&a=AB,12,CD,34"
test "{&a,a}" [] ""
test "{&a,a}" ["a" =: emptyList] ""
test "{&a,a}" ["a" =: emptyDictionary] ""
test "{&a,a}" ["a" =: ""] "&a=&a="
test "{&a,b}" ["a" =: ""] "&a="
test "{&a,b}" ["b" =: ""] "&b="
test "{&%aa}" ["%aa" =: "A"] "&%aa=A"
test "{&%aa}" ["%aa" =: ["A", "B"]] "&%aa=A,B"
test "{&%aa}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "&%aa=A,1,B,2"
test "{&%aa*}" ["%aa" =: "A"] "&%aa=A"
test "{&%aa*}" ["%aa" =: ["A", "B"]] "&%aa=A&%aa=B"
test "{&%aa*}" ["%aa" =: ["A" =: "1", "B" =: "2"]] "&A=1&B=2"
label "supports quasi quotes" $ do
label "as expressions" $ do
let qq x y = Writer.tell . Test $ Just x Test.~?= Burrito.parse y
qq [Burrito.uriTemplate||] ""
qq [Burrito.uriTemplate|a|] "a"
qq [Burrito.uriTemplate|%00|] "%00"
qq [Burrito.uriTemplate|{a}|] "{a}"
qq [Burrito.uriTemplate|{a,b}|] "{a,b}"
qq [Burrito.uriTemplate|{a}{b}|] "{a}{b}"
qq [Burrito.uriTemplate|{a*}|] "{a*}"
qq [Burrito.uriTemplate|{a:1}|] "{a:1}"
qq [Burrito.uriTemplate|{+a}|] "{+a}"
qq [Burrito.uriTemplate|{#a}|] "{#a}"
qq [Burrito.uriTemplate|{.a}|] "{.a}"
qq [Burrito.uriTemplate|{/a}|] "{/a}"
qq [Burrito.uriTemplate|{;a}|] "{;a}"
qq [Burrito.uriTemplate|{?a}|] "{?a}"
qq [Burrito.uriTemplate|{&a}|] "{&a}"
runTests :: Writer.WriterT Test IO a -> IO ()
runTests writer = do
tests <- Writer.execWriterT writer
counts <- Test.runTestTT $ unwrapTest tests
let
hasErrors = Test.errors counts /= 0
hasFailures = Test.failures counts /= 0
Monad.when (hasErrors || hasFailures) Exit.exitFailure
label
:: Monad m
=> String
-> Writer.WriterT Test m a
-> Writer.WriterT Test m a
label string = Writer.censor $ \tests -> Test $ string Test.~: tests
test
:: (Stack.HasCallStack, Monad m)
=> String -- ^ A string to parse as a URI template ('Burrito.parse')
-> Values -- ^ Values to feed into the template ('Burrito.expand')
-> Expected -- ^ The URI that should result
-> Writer.WriterT Test m ()
test input values output = do
let expand = Burrito.expand $ fmap (fmap unwrapValue) values
Writer.tell
. Test
$ input
Test.~: fmap expand (Burrito.parse input)
Test.~?= expectedToMaybe output
newtype Test = Test
{ unwrapTest :: Test.Test
} deriving (Show)
instance Monoid Test where
mempty = Test $ Test.TestList []
instance Semigroup Test where
x <> y = Test . Test.TestList $ case (unwrapTest x, unwrapTest y) of
(Test.TestList t, Test.TestList u) -> t <> u
(Test.TestList t, u) -> t <> [u]
(t, Test.TestList u) -> t : u
(t, u) -> [t, u]
instance Test.Testable Test where
test = unwrapTest
data Expected
= Failure
| Success String
deriving (Eq, Show)
instance String.IsString Expected where
fromString = Success
expectedToMaybe :: Expected -> Maybe String
expectedToMaybe expected = case expected of
Failure -> Nothing
Success string -> Just string
type Values = [(String, Value)]
newtype Value = Value
{ unwrapValue :: Burrito.Value
} deriving (Eq, Show)
instance Exts.IsList Value where
type Item Value = Item
fromList items =
Value $ case Either.partitionEithers $ fmap unwrapItem items of
(strings, []) -> Burrito.listValue strings
([], tuples) -> Burrito.dictionaryValue tuples
(_, _) -> error $ "fromList " <> show items <> " :: Value"
toList value = error $ "toList " <> show value
instance String.IsString Value where
fromString = Value . Burrito.stringValue
emptyList :: Value
emptyList = Value $ Burrito.listValue []
emptyDictionary :: Value
emptyDictionary = Value $ Burrito.dictionaryValue []
newtype Item = Item
{ unwrapItem :: Either String (String, String)
} deriving (Eq, Show)
instance String.IsString Item where
fromString = Item . Left
class Pair a where
type K a
type V a
(=:) :: K a -> V a -> a
instance Pair (a, b) where
type K (a, b) = a
type V (a, b) = b
(=:) = (,)
instance Pair Item where
type K Item = String
type V Item = String
key =: value = Item $ Right (key, value)