# -*- tcl -*- Include file for cmdr.test.
# # ## ### ##### ######## ############# #####################

## Runtime parsing of a command line based on the argument spec.
## Cases to look at:
##
## arguments only
## - required only
## - optional only
## - required + optional
## - splat
##   - especially with optional arguments coming before.
## options only
## - scalar
## - list
## args + options
## - especially splat with options
## state
##
## Take examples from the intended target app
## NOTE: interaction not testable!! linenoise - mockup ?!

# # ## ### ##### ######## ############# #####################
## Group I: 5 parameters, required and optional alternating. Called
## with zero to six arguments.

test cmdr-runtime-1.0 {required + optional arguments, not enough} -body {
    Parse {
	input A - { validate integer }
	input B - { validate integer; optional ; default 0 }
	input C - { validate integer }
	input D - { validate integer; optional ; default 0 }
	input E - { validate integer }
    }
} -returnCodes error \
    -result "wrong\#args, not enough"

test cmdr-runtime-1.1 {required + optional arguments, not enough} -body {
    Parse {
	input A - { validate integer }
	input B - { validate integer; optional ; default 0 }
	input C - { validate integer }
	input D - { validate integer; optional ; default 0 }
	input E - { validate integer }
    } 1
} -returnCodes error \
    -result "wrong\#args, not enough"

test cmdr-runtime-1.2 {required + optional arguments, not enough} -body {
    Parse {
	input A - { validate integer }
	input B - { validate integer; optional ; default 0 }
	input C - { validate integer }
	input D - { validate integer; optional ; default 0 }
	input E - { validate integer }
    } 1 2
} -returnCodes error \
    -result "wrong\#args, not enough"

test cmdr-runtime-1.3 {required + optional arguments, assignment under thresholding} -body {
    Parse {
	input A - { validate integer }
	input B - { validate integer; optional ; default 0 }
	input C - { validate integer }
	input D - { validate integer; optional ; default 0 }
	input E - { validate integer }
    } 1 2 3
} -result {
    A    = '1'         v'1'
    B    = <undefined> v'0'
    C    = '2'         v'2'
    D    = <undefined> v'0'
    E    = '3'         v'3'
    help = <undefined> v'no'
}

test cmdr-runtime-1.4 {required + optional arguments, assignment under thresholding} -body {
    Parse {
	input A - { validate integer }
	input B - { validate integer; optional ; default 0 }
	input C - { validate integer }
	input D - { validate integer; optional ; default 0 }
	input E - { validate integer }
    } 1 2 3 4
} -result {
    A    = '1'         v'1'
    B    = '2'         v'2'
    C    = '3'         v'3'
    D    = <undefined> v'0'
    E    = '4'         v'4'
    help = <undefined> v'no'
}

test cmdr-runtime-1.5 {required + optional arguments, assignment under thresholding} -body {
    Parse {
	input A - { validate integer }
	input B - { validate integer; optional ; default 0 }
	input C - { validate integer }
	input D - { validate integer; optional ; default 0 }
	input E - { validate integer }
    } 1 2 3 4 5
} -result {
    A    = '1'         v'1'
    B    = '2'         v'2'
    C    = '3'         v'3'
    D    = '4'         v'4'
    E    = '5'         v'5'
    help = <undefined> v'no'
}

test cmdr-runtime-1.6 {required + optional arguments, too many} -body {
    Parse {
	input A - { validate integer }
	input B - { validate integer; optional ; default 0 }
	input C - { validate integer }
	input D - { validate integer; optional ; default 0 }
	input E - { validate integer }
    } 1 2 3 4 5 6
} -returnCodes error \
    -result "wrong\#args, too many"

# # ## ### ##### ######## ############# #####################
## Group II: Splat arguments, required and optional

test cmdr-runtime-2.0 {required + required splat, not enough} -body {
    Parse {
	input A - { validate integer }
	input B - { list ; validate integer }
    }
} -returnCodes error \
    -result "wrong\#args, not enough"

test cmdr-runtime-2.1 {required + required splat, not enough} -body {
    Parse {
	input A - { validate integer }
	input B - { list ; validate integer }
    } 1
} -returnCodes error \
    -result "wrong\#args, not enough"

test cmdr-runtime-2.2 {required + required splat} -body {
    Parse {
	input A - { validate integer }
	input B - { list ; validate integer }
    } 1 2
} -result {
    A    = '1'         v'1'
    B    = '2'         v'2'
    help = <undefined> v'no'
}

test cmdr-runtime-2.3 {required + required splat} -body {
    Parse {
	input A - { validate integer }
	input B - { list ; validate integer }
    } 1 2 3
} -result {
    A    = '1'         v'1'
    B    = '2 3'       v'2 3'
    help = <undefined> v'no'
}

test cmdr-runtime-2.4 {required + required splat} -body {
    Parse {
	input A - { validate integer }
	input B - { list ; validate integer }
    } 1 2 3 4
} -result {
    A    = '1'         v'1'
    B    = '2 3 4'     v'2 3 4'
    help = <undefined> v'no'
}

# # ## ### ##### ######## ############# #####################

test cmdr-runtime-2.5 {required + optional splat, not enough} -body {
    Parse {
	input A - { validate integer }
	input B - { list ; optional ; validate integer }
    }
} -returnCodes error \
    -result "wrong\#args, not enough"

test cmdr-runtime-2.6 {required + optional splat, empty} -body {
    Parse {
	input A - { validate integer }
	input B - { list ; optional ; validate integer }
    } 1
} -result {
    A    = '1'         v'1'
    B    = <undefined> v''
    help = <undefined> v'no'
}

test cmdr-runtime-2.7 {required + optional splat} -body {
    Parse {
	input A - { validate integer }
	input B - { list ; optional ; validate integer }
    } 1 2
} -result {
    A    = '1'         v'1'
    B    = '2'         v'2'
    help = <undefined> v'no'
}

test cmdr-runtime-2.8 {required + optional splat} -body {
    Parse {
	input A - { validate integer }
	input B - { list ; optional ; validate integer }
    } 1 2 3
} -result {
    A    = '1'         v'1'
    B    = '2 3'       v'2 3'
    help = <undefined> v'no'
}

test cmdr-runtime-2.9 {required + optional splat} -body {
    Parse {
	input A - { validate integer }
	input B - { list ; optional ; validate integer }
    } 1 2 3 4
} -result {
    A    = '1'         v'1'
    B    = '2 3 4'     v'2 3 4'
    help = <undefined> v'no'
}

# # ## ### ##### ######## ############# #####################

test cmdr-runtime-2.10 {optional + required splat, not enough} -body {
    Parse {
	input A - { optional ; validate integer }
	input B - { list ; validate integer }
    }
} -returnCodes error \
    -result "wrong\#args, not enough"

test cmdr-runtime-2.11 {optional + required splat, not enough} -body {
    Parse {
	input A - { optional ; validate integer }
	input B - { list ; validate integer }
    } 1
} -result {
    A    = <undefined> v'0'
    B    = '1'         v'1'
    help = <undefined> v'no'
}
test cmdr-runtime-2.12 {optional + required splat} -body {
    Parse {
	input A - { optional ; validate integer }
	input B - { list ; validate integer }
    } 1 2
} -result {
    A    = '1'         v'1'
    B    = '2'         v'2'
    help = <undefined> v'no'
}

test cmdr-runtime-2.13 {optional + required splat} -body {
    Parse {
	input A - { optional ; validate integer }
	input B - { list ; validate integer }
    } 1 2 3
} -result {
    A    = '1'         v'1'
    B    = '2 3'       v'2 3'
    help = <undefined> v'no'
}

test cmdr-runtime-2.14 {optional + required splat} -body {
    Parse {
	input A - { optional ; validate integer }
	input B - { list ; validate integer }
    } 1 2 3 4
} -result {
    A    = '1'         v'1'
    B    = '2 3 4'     v'2 3 4'
    help = <undefined> v'no'
}

# # ## ### ##### ######## ############# #####################

test cmdr-runtime-2.15 {optional + optional splat, not enough} -body {
    Parse {
	input A - { optional ; validate integer }
	input B - { list ; optional ; validate integer }
    }
} -result {
    A    = <undefined> v'0'
    B    = <undefined> v''
    help = <undefined> v'no'
}

test cmdr-runtime-2.16 {optional + optional splat, empty} -body {
    Parse {
	input A - { optional ; validate integer }
	input B - { list ; optional ; validate integer }
    } 1
} -result {
    A    = '1'         v'1'
    B    = <undefined> v''
    help = <undefined> v'no'
}

test cmdr-runtime-2.17 {optional + optional splat} -body {
    Parse {
	input A - { optional ; validate integer }
	input B - { list ; optional ; validate integer }
    } 1 2
} -result {
    A    = '1'         v'1'
    B    = '2'         v'2'
    help = <undefined> v'no'
}

test cmdr-runtime-2.18 {optional + optional splat} -body {
    Parse {
	input A - { optional ; validate integer }
	input B - { list ; optional ; validate integer }
    } 1 2 3
} -result {
    A    = '1'         v'1'
    B    = '2 3'       v'2 3'
    help = <undefined> v'no'
}

test cmdr-runtime-2.19 {optional + optional splat} -body {
    Parse {
	input A - { optional ; validate integer }
	input B - { list ; optional ; validate integer }
    } 1 2 3 4
} -result {
    A    = '1'         v'1'
    B    = '2 3 4'     v'2 3 4'
    help = <undefined> v'no'
}

# # ## ### ##### ######## ############# #####################
## Group III: Options.

test cmdr-runtime-3.0 {options, simple, boolean} -body {
    Parse {
	option A -
    }
} -result {
    A    = <undefined> v'no'
    help = <undefined> v'no'
}

test cmdr-runtime-3.1 {options, simple, boolean} -body {
    Parse {
	option A -
    } -A 1
} -result {
    A    = '1'         v'1'
    help = <undefined> v'no'
}

test cmdr-runtime-3.2 {options, simple, boolean} -body {
    Parse {
	option A -
    } -A 0
} -result {
    A    = '0'         v'0'
    help = <undefined> v'no'
}

test cmdr-runtime-3.3 {options, simple, boolean, multiple use} -body {
    Parse {
	option A -
    } -A 0 -A 1
} -result {
    A    = '1'         v'1'
    help = <undefined> v'no'
}

test cmdr-runtime-3.4 {options, simple, boolean, special forms} -body {
    Parse {
	option A -
	option B -
	option C -
	option D -
	option E -
	option F -
	option G - { validate identity }
    } -A -G X --no-B -G X -C 1 -D 0 --no-E 1 --no-F 0
} -result {
    A    = 'yes'       v'1'
    B    = '0'         v'0'
    C    = '1'         v'1'
    D    = '0'         v'0'
    E    = '0'         v'0'
    F    = '1'         v'1'
    G    = 'X'         v'X'
    help = <undefined> v'no'
}

# # ## ### ##### ######## ############# #####################

test cmdr-runtime-3.5 {options, list, string} -body {
    Parse {
	option A - { list ; validate identity }
    }
} -result {
    A    = <undefined> v''
    help = <undefined> v'no'
}

test cmdr-runtime-3.6 {options, list, string, missing value} -body {
    Parse {
	option A - { list ; validate identity }
    } -A
} -returnCodes error \
    -result "wrong\#args, missing value for option '-A'"

test cmdr-runtime-3.7 {options, list, string} -body {
    Parse {
	option A - { list ; validate identity }
    } -A X
} -result {
    A    = 'X'         v'X'
    help = <undefined> v'no'
}

test cmdr-runtime-3.8 {options, list, string} -body {
    Parse {
	option A - { list ; validate identity }
    } -A X -A Y
} -result {
    A    = 'X Y'       v'X Y'
    help = <undefined> v'no'
}

# # ## ### ##### ######## ############# #####################

test cmdr-runtime-3.9 {options, aliases} -body {
    Parse {
	option A - { validate identity ; alias Z }
    } -A X -Z Y
} -result {
    A    = 'Y'         v'Y'
    help = <undefined> v'no'
}

test cmdr-runtime-3.10 {options, aliases, list} -body {
    Parse {
	option A - { list ; validate identity ; alias Z }
    } -A X -Z Y
} -result {
    A    = 'X Y'       v'X Y'
    help = <undefined> v'no'
}

# # ## ### ##### ######## ############# #####################

test cmdr-runtime-3.11 {options, long names} -body {
    Parse {
	option ALPHA - { list ; validate identity }
    } --ALPHA Z
} -result {
    ALPHA = 'Z'         v'Z'
    help  = <undefined> v'no'
}

test cmdr-runtime-3.12 {options, unique prefix expansion} -body {
    Parse {
	option ALPHA - { list ; validate identity }
    } --AL Z
} -result {
    ALPHA = 'Z'         v'Z'
    help  = <undefined> v'no'
}

# # ## ### ##### ######## ############# #####################

test cmdr-runtime-3.13 {options, unknown option} -body {
    Parse {
	option A - { validate identity }
    } -B Z
} -returnCodes error \
    -result {Unknown option -B}

test cmdr-runtime-3.14 {options, ambiguous prefix} -body {
    Parse {
	option ALPHA - { validate identity }
	option ALNUM - { validate identity }
    } --AL Z
} -returnCodes error \
    -result {Ambiguous option prefix --AL, matching --ALNUM, --ALPHA}

# NOTE: Test is brittle, dependent on the internal order in which the
# options are processed by UniquePrefixes.

test cmdr-runtime-3.15 {options, semi-ambiguous prefix (other option)} -body {
    Parse {
	option ALPHA - { validate identity }
	option AL    - { validate identity }
    } --AL Z
} -result {
    AL    = 'Z'         v'Z'
    ALPHA = <undefined> v''
    help  = <undefined> v'no'
}

# NOTE: Test is brittle, dependent on the internal order in which the
# options are processed by UniquePrefixes.

test cmdr-runtime-3.16 {options, semi-ambiguous prefix (other option)} -body {
    Parse {
	option AL-PHA - { validate identity }
	option AL     - { validate identity }
    } --AL Z --AL-PHA Y
} -result {
    AL     = 'Z'         v'Z'
    AL-PHA = 'Y'         v'Y'
    help   = <undefined> v'no'
}

# # ## ### ##### ######## ############# #####################
## Group IV: Mix options and arguments.

test cmdr-runtime-4.1 {options, arguments, and splat} -body {
    Parse {
	input  A - { optional ; validate identity }
	input  C - { optional ; validate identity ; list }
	option T - { validate identity }
    } A -T X C C C -T Y
    # The threshold method for optional arguments processes options
    # before arguments, handling the 2nd -T before splat collection.
    # Compare to 5.1.
} -result {
    A    = 'A'         v'A'
    C    = 'C C C'     v'C C C'
    help = <undefined> v'no'
    T    = 'Y'         v'Y'
}

# # ## ### ##### ######## ############# #####################
## Group V: Optional arguments, via peek+validate

test cmdr-runtime-5.1 {options, arguments, and splat, peek} -body {
    Parse {
	input  A - { optional ; validate identity ; test }
	input  C - { optional ; validate identity ; test ; list }
	option T - { validate identity }
    } A -T X C C C -T Y
    # The peek+validate method for optional arguments does not look
    # ahead beyond that, thus the 2nd -T becomes part of the splat
    # collection. Compare to 4.1. Note that the splat is _also_ put
    # into peek+validate mode, otherwise it would force the options to
    # be processed first.
} -result {
    A    = 'A'          v'A'
    C    = 'C C C -T Y' v'C C C -T Y'
    help = <undefined>  v'no'
    T    = 'X'          v'X'
}

# # ## ### ##### ######## ############# #####################
## Group VI: Introspect on the context of the generate, validate, and
##           when-complete command prefixes. Ensure that they are
##           called and work.

test cmdr-runtime-6.1 {generator command} -setup {
    StartNotes
    set ons A ; # Trigger parse to save the parameter object namespace.
} -body {
    Note {*}[split [Parse {
	input A - {
	    optional
	    generate {apply {{p} {
		Note generate  [uplevel 1 [list namespace current]]
		return G
	    }}}
	}
    }] \n]
    string map [list $::ons MATCH] [Notes]
} -cleanup {
    unset ons
    StopNotes
} -result {
    generate MATCH
    {} {    A    = <undefined> v'G'} {    help = <undefined> v'no'} {}
}

test cmdr-runtime-6.2 {validation, default supplication} -setup {
    StartNotes
    set ons A ; # Trigger parse to save the object namespace.
} -body {
    Note {*}[split [Parse {
	input A - {
	    optional
	    validate {apply {{kind p args} {
		Note names = [$p config names]
		Note validate $kind [uplevel 1 [list namespace current]]
		switch -glob -- $kind {
		    validate { return [lindex $args 0] }
		    default  { return zZzZz }
		    * { error $kind }
		}
	    }}}
	}
    }] \n]
    string map [list $::ons MATCH] [Notes]
} -cleanup {
    unset ons
    StopNotes
} -result {
    names = help
    validate default MATCH
    {} {    A    = <undefined> v'zZzZz'} {    help = <undefined> v'no'} {}
}

test cmdr-runtime-6.3 {generator command} -setup {
    StartNotes
    set ons A ; # Trigger parse to save the parameter object namespace.
} -body {
    Note {*}[split [Parse {
	input A - {
	    optional
	    when-complete {apply {{p v} {
		Note names = [$p config names]
		Note defined = $v
		Note defined [uplevel 1 [list namespace current]]
		return
	    }}}
	}
    }] \n]
    string map [list $::ons MATCH] [Notes]
} -cleanup {
    unset ons
    StopNotes
} -result {
    names = {help A}
    defined = {}
    defined MATCH
    {} {    A    = <undefined> v''} {    help = <undefined> v'no'} {}
}

# # ## ### ##### ######## ############# #####################

test cmdr-runtime-7.1 {not-quite options, arguments} -body {
    Parse {
	input  A - { optional ; validate identity }
	option T - { validate identity }
    } -1 -T -3
    # The -1 looks like an option, but is for input A.
} -result {
    A    = '-1'        v'-1'
    help = <undefined> v'no'
    T    = '-3'        v'-3'
}

test cmdr-runtime-7.2 {validation errors} -body {
    ParseFailParse {
	input A - { validate integer }
	input B - { validate integer }
    } 1 X
} -returnCodes error \
    -result {Expected an integer for input "B", got "X"}

# # ## ### ##### ######## ############# #####################
## Options, negative aliases

test cmdr-runtime-8.0 {options, simple, boolean, special forms II} -body {
    Parse {
	option A -
	option B -
	option C -
	option D -
	option E -
	option F -
	option G - { validate identity }
    } -A -G=X --no-B -G=X -C=1 -D=0 --no-E=1 --no-F=0
} -result {
    A    = 'yes'       v'1'
    B    = '0'         v'0'
    C    = '1'         v'1'
    D    = '0'         v'0'
    E    = '0'         v'0'
    F    = '1'         v'1'
    G    = 'X'         v'X'
    help = <undefined> v'no'
}

test cmdr-runtime-8.1 {options, boolean, aliases} -body {
    Parse {
	option A - { alias     X }
	option B - { neg-alias Y }
    } -X -Y
} -result {
    A    = 'yes'       v'1'
    B    = '0'         v'0'
    help = <undefined> v'no'
}

test cmdr-runtime-8.2 {options, boolean, aliases, special forms} -body {
    Parse {
	option A - { alias     X }
	option B - { neg-alias Y }
    } -X=0 -Y=0
} -result {
    A    = '0'         v'0'
    B    = '1'         v'1'
    help = <undefined> v'no'
}

# # ## ### ##### ######## ############# #####################

return
