Index: Makefile.in =================================================================== diff -u -rbd4aa15282abe386464ef276b78f2708f249f333 -ra0d6bbfb8166411fcf148f2f77e7905b62e6b9db --- Makefile.in (.../Makefile.in) (revision bd4aa15282abe386464ef276b78f2708f249f333) +++ Makefile.in (.../Makefile.in) (revision a0d6bbfb8166411fcf148f2f77e7905b62e6b9db) @@ -403,6 +403,7 @@ $(TCLSH) $(src_test_dir_native)/varresolution.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/info-method.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/submethods.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) + $(TCLSH) $(src_test_dir_native)/disposition.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/parameters.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/returns.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/method-require.test -libdir $(PLATFORM_DIR) $(TESTFLAGS) Index: TODO =================================================================== diff -u -rceb5634acd12db91d50b16bcec1bda5906922ced -ra0d6bbfb8166411fcf148f2f77e7905b62e6b9db --- TODO (.../TODO) (revision ceb5634acd12db91d50b16bcec1bda5906922ced) +++ TODO (.../TODO) (revision a0d6bbfb8166411fcf148f2f77e7905b62e6b9db) @@ -2924,6 +2924,9 @@ * switched parameter logic from default for nrargs from 0 to 1 * simplified logic to detect additional arguments in argument parser * improved error message for missing required argument +- regression tests: + * added disposition.test + * extended regression test TODO: Index: tests/disposition.test =================================================================== diff -u --- tests/disposition.test (revision 0) +++ tests/disposition.test (revision a0d6bbfb8166411fcf148f2f77e7905b62e6b9db) @@ -0,0 +1,451 @@ +# -*- Tcl -*- +package req nx +package require nx::test + +# +# test cases for disposition "alias" and "forward" +# + +nx::Test case basics { + + Class create C { + :class attribute [list inst "::__%&singleton"] + :method foo {x} { + #puts stderr [current method] + set :[current method] $x + } + :method bar {} {;} + :protected method baz {y} { + #puts stderr [current method] + set :my[current method] $y + } + + # + # some testing helpers + # + :public class method setObjectParams {spec} { + set :objectparams $spec + ::nsf::invalidateobjectparameter [current] + } + :class method objectparameter {} { + return ${:objectparams} + } + :setObjectParams "" + + :public class method new args { + return [:create ${:inst} {*}$args] + } + } + + # + # Restricted to object parameters only? + # + foreach paramType {forward alias} { + set msg "Parameter option '$paramType' not allowed" + ? [list C method m1 -foo:$paramType {;}] $msg + ? [list C method m1 foo:$paramType {;}] $msg + } + + + # + # Do aliases and forwarder set instance variables? They should not. + # + C setObjectParams -baz:alias + ? {[C new -baz BAZ] eval {info exists :baz}} 0 + + C setObjectParams {{{-baz:forward,method=%self %method}}} + ? {[C new -baz BAZ] eval {info exists :baz}} 0 + + # + # Note, currently alias/forward disposition operate on public and + # protected target methods alike. Is this intended? For example, + # providing access through the parameter interface to protected + # methods etc. (at the instantiation site only) ? Or, are they + # expected to be public ... + # + ### objectparameter are from the intentions public: the typical + ### use-case is that someone wants to configure an object to be + ### created, before the object exists.... + + # + # 1) Positional object parameters + alias/forward disposition? + # + + # + # Passing a single argument to a positional alias + # + C setObjectParams foo:alias + ? {C new FOO} "::__%&singleton" + ? {C new {FOO FAA}} "::__%&singleton" + + ### + ### Whenever a value is provided (default value or actual value) the + ### parameter is evaluated. + ### + C setObjectParams {{foo:alias ""}} + ? {C new} "::__%&singleton" + + C setObjectParams {{-foo:alias "fooDefault"}} + ? {[C new] eval {set :foo}} "fooDefault" + + # + # What about multi-argument vectors? + # + + C eval { + :method multi-2 {x y} { + set :[current method] [current args] + } + :method multi-escape {x} { + set :[current method] $x + } + :method multi-args {args} { + set :[current method] $args + } + } + + # + # Parameters are limited to a single value by the object parameter. + # + + C setObjectParams {{-multi-2:alias}} + ? {[C new -multi-2 {X Y}] eval {set :multi-2}} \ + "wrong # args: should be \"multi-2 x y\"" + # + # Passing multiple arguments as a list + # + C setObjectParams {{-multi-escape:alias}} + ? {[C new -multi-escape [list X Y]] eval {set :multi-escape}} \ + [list X Y] + + # + # Passing multiple arguments as a list, passed to a args argument + # list. + # + C setObjectParams {{-multi-args:alias}} + ? {[C new -multi-args [list X Y]] eval {set :multi-args}} \ + [list [list X Y]] + + # + # By design, all parameters are currently limited to 0 or 1 + # argument. The same is true for disposition "alias" an + # "forward". One could consider to unbox a parameter list via a + # parameter option "expand" (like {*}) for alias|forward parameter + # specs, e.g.: + # {-multi-2:alias,expand} + # {-multi-2:forward,method=...,expand} + # + # Without the sketched extension, one could use eval in a forwarder. + # + + C setObjectParams {{{-multi-2:forward,method=eval %self %method}}} + ? {[C new -multi-2 {X Y}] eval {set :multi-2}} \ + "X Y" + + # + # In the positional case, why is FOO not passed on as arg value to + # the target method? + # + C setObjectParams {{{foo:forward,method=%self %method}}} + ? {C new FOO} "::__%&singleton" + ? {[C new FOO] eval {set :foo}} "FOO" + + # + # Naming of the parameter spec element "method": It fits the alias + # disposition, but is a little irritating in the context of a + # forward. One would expect forwardspec or simply "spec" (as this is + # used in the docs, the error messages etc.), e.g.: + # + # {foo:forward,spec=%self %method} + # + # 'spec' would also work for 'alias' as it is more general (the spec + # of an alias is the method name ...) + # + #### well, "spec" is not nice for alias, and potentially confusing + #### with the parameter spec (the full parameter definition). + + # + # Passing non-positional arguments to target methods (at least + # forwarder ones)? + # + + C method multi-mix {-x y args} { + set :[current method] --x-$x--y-$y--args-$args + } + + C setObjectParams {{{-multi-mix:forward,method=eval %self %method}}} + ? {[C new -multi-mix [list -x X Y Z 1 2]] eval {set :multi-mix}} \ + "--x-X--y-Y--args-Z 1 2" + + # + # Aliased methods with nonpos arguments are rendered entirely + # useless by the single-value limitation (see also above): + # + + C method single-np {-x:required} { + set :[current method] --x-$x + } + + C setObjectParams {{-single-np:alias}} + ? {[C new -single-np [list -x]] eval {set :single-np}} \ + "Argument for parameter '-x' expected" + ? {[C new -single-np [list -x X]] eval {set :single-np}} \ + "Invalid argument '-x X', maybe too many arguments; should be \"[C inst] single-np -x\"" + + # + # INTERACTIONS with other parameter types + # + # There are two validation points: + # 1) the object parameter validation on the initial argument set + # 2) the target method validation on the (mangled) argument set + # + # ... they can deviate from each other, to a point of direct + # conflict + # + + # + # Allowed built-in value types (according to feature matrix in + # parameters.test) + # + + set msg {expected $mtype but got \"$testvalue\" for parameter \"x\"} + dict set types boolean [list testvalue f mtype object msg $msg] + dict set types integer [list testvalue 81 mtype punct msg $msg] + dict set types object [list testvalue ::C mtype integer msg $msg ] + + dict set types class [list testvalue ::C mtype boolean msg $msg] + dict set types object,type=::Class \ + [list testvalue ::C mtype object,type=::C \ + msg "expected object of type ::C but got \"::C\"\ + for parameter \"x\""] + + # for aliases ... + + dict for {t tdict} $types { + dict with tdict { + ::C public method foo [list x:$t] { + set :[current method] $x + } + ::C setObjectParams [list [list -foo:alias,$t]] + ? "::nsf::is $t \[\[::C new -foo $testvalue\] eval {set :foo}\]" 1 + } + } + + dict for {t tdict} $types { + dict with tdict { + ::C public method foo [list x:$mtype] { + set :[current method] $x + } + ::C setObjectParams [list [list -foo:alias,$t]] + ? "::nsf::is $t \[\[::C new -foo $testvalue\] eval {set :foo}\]" \ + [subst $msg] + } + } + + # + # TODO: update the matrix in parameters.test (feature matrix) + # + ### + ### The question is, what happens with the matrix. The matrix is in + ### some respects not complete (no disposition) and contains old + ### namings (e.g. allowempty, multiple) and contains types removed + ### some time ago (such as e.g. "relation"). + ### + + foreach disposition [list alias forward] { + C setObjectParams [list [list -foo:$disposition,switch]] + ? {C new} "Parameter option 'switch' not allowed" + + C setObjectParams [list [list -foo:$disposition,xxx]] + ? {C new} "Parameter option 'xxx' unknown for parameter type $disposition" + + C setObjectParams [list [list -foo:$disposition,relation]] + ? {C new} "Parameter option 'relation' unknown for parameter type $disposition" + } + + C setObjectParams [list [list -foo:alias,forward]] + ? {C new} "Parameter options alias and forward can be not used together" + + C setObjectParams [list [list -foo:forward,alias]] + ? {C new} "Parameter options alias and forward can be not used together" + + C setObjectParams [list [list -foo:alias,initcmd]] + ? {C new} "Parameter options alias and initcmd can be not used together" + + C setObjectParams [list [list -foo:forward,initcmd]] + ? {C new} "Parameter options forward and initcmd can be not used together" + + C setObjectParams [list [list -foo:forward,initcmd]] + + # Remains to be done ... + # INTERACTIONS with the return value checker + # + + # + # uplevel, upvar (with forward, with different target method types) + # + + # + # allowempty + # +} + +nx::Test case alias-noarg { + Class create C { + :public class method setObjectParams {spec} { + set :objectparams $spec + ::nsf::invalidateobjectparameter [current] + } + :class method objectparameter {} { + return ${:objectparams} + } + :public method foo {args} { + set :foo $args + #puts stderr FOO + return $args + } + :public method bar {args} { + set :bar $args + #puts stderr BAR + return $args + } + } + + # + # nopos arg with noargs, given + # + C setObjectParams {-bar:alias,noarg} + C create c1 -bar + ? {c1 eval {info exists :bar}} 1 + ? {c1 eval {info exists :x}} 0 + + # + # nopos arg with noargs, not given + # + C setObjectParams {-bar:alias,noarg} + C create c1 + ? {c1 eval {info exists :bar}} 0 + + # + # pos arg with noargs + # + C setObjectParams {foo:alias,noarg} + C create c1 + ? {c1 eval {info exists :foo}} 1 + + # + # initcmd with default + # + C setObjectParams {{__init:initcmd :foo}} + C create c1 + ? {c1 eval {info exists :foo}} 1 + + # + # pos arg with noargs and nonposarg with noargs, given + # + C setObjectParams {foo:alias,noarg -bar:alias,noarg} + C create c1 -bar + ? {c1 eval {info exists :bar}} 1 + ? {c1 eval {info exists :foo}} 1 + ? {c1 eval {info exists :x}} 0 + + # + # optional initcmd, like in nx + # + C setObjectParams {initcmd:initcmd,optional} + C create c1 {set :x 1} + ? {c1 eval {info exists :x}} 1 + + # + # using a default value for initcmd + # + C setObjectParams {{initcmd:initcmd ""}} + C create c1 {set :x 1} + C create c2 + ? {c1 eval {info exists :x}} 1 + ? {c2 eval {info exists :x}} 0 + + # + # optional initcmd + non-consuming (nrargs==0) posarg, provided + # initcmd + # + C setObjectParams {foo:alias,noarg initcmd:initcmd,optional} + C create c1 {set :x 1} + ? {c1 eval {info exists :x}} 1 + ? {c1 eval {info exists :foo}} 1 + ? {c1 eval {info exists :bar}} 0 + + # + # optional initcmd + non-consuming (nrargs==0) posarg, no value for + # initcmd + # + C setObjectParams {foo:alias,noarg initcmd:initcmd,optional} + C create c1 + ? {c1 eval {info exists :x}} 0 + ? {c1 eval {info exists :foo}} 1 + ? {c1 eval {info exists :bar}} 0 + + # + # initcmd with default + non-consuming (nrargs==0) posarg, no value + # for initcmd + # + C setObjectParams {foo:alias,noarg {initcmd:initcmd ""}} + C create c1 + ? {c1 eval {info exists :x}} 0 + ? {c1 eval {info exists :foo}} 1 + ? {c1 eval {info exists :bar}} 0 + + # + # non-consuming alias, nonpos alias with noarg, initcmd provided + # + C setObjectParams {foo:alias,noarg -bar:alias,noarg initcmd:initcmd,optional} + C create c1 {set :x 1} + ? {c1 eval {info exists :foo}} 1 + ? {c1 eval {info exists :bar}} 0 + ? {c1 eval {info exists :x}} 1 + + # + # non-consuming alias, nonpos alias with noarg, nonpos called, initcmd provided + # + C setObjectParams {foo:alias,noarg -bar:alias,noarg initcmd:initcmd,optional} + C create c1 -bar {set :x 1} + ? {c1 eval {info exists :foo}} 1 + ? {c1 eval {info exists :bar}} 1 + ? {c1 eval {info exists :x}} 1 + + # + # non-consuming alias, nonpos alias with noarg, no initcmd provided + # + C setObjectParams {foo:alias,noarg -bar:alias,noarg initcmd:initcmd,optional} + C create c1 + ? {c1 eval {info exists :foo}} 1 + ? {c1 eval {info exists :bar}} 0 + ? {c1 eval {info exists :x}} 0 + + # + # non-consuming alias, nonpos alias with noarg, nonpos called, no + # initcmd provided + # + C setObjectParams {foo:alias,noarg -bar:alias,noarg initcmd:initcmd,optional} + C create c1 -bar + ? {c1 eval {info exists :foo}} 1 + ? {c1 eval {info exists :bar}} 1 + ? {c1 eval {info exists :x}} 0 +} + +nx::Test case alias-noarg { + Class create C { + :public class method setObjectParams {spec} { + set :objectparams $spec + ::nsf::invalidateobjectparameter [current] + } + :class method objectparameter {} { + return ${:objectparams} + } + } + + C setObjectParams {initcmd:initcmd,noarg} + ? {C create c1} {option noarg only allowed for parameter type "alias"} + +} +#### initcmd noarg \ No newline at end of file Index: tests/parameters.test =================================================================== diff -u -rceb5634acd12db91d50b16bcec1bda5906922ced -ra0d6bbfb8166411fcf148f2f77e7905b62e6b9db --- tests/parameters.test (.../parameters.test) (revision ceb5634acd12db91d50b16bcec1bda5906922ced) +++ tests/parameters.test (.../parameters.test) (revision a0d6bbfb8166411fcf148f2f77e7905b62e6b9db) @@ -12,6 +12,19 @@ } ? {::nsf::object::exists ::o} 0 +# +# simple test case for parameter passing +# +nx::Test case syntax { + ::nx::Class create C + ? {::nsf::method::alias C} \ + {required argument 'methodName' is missing, should be: + ::nsf::method::alias object ?-per-object? methodName ?-frame method|object|default? cmdName} + ? {::nsf::method::alias C foo ::set} "::nsf::classes::C::foo" + ? {::nsf::method::alias C foo ::set 1} \ + {Invalid argument '1', maybe too many arguments; should be "::nsf::method::alias object ?-per-object? methodName ?-frame method|object|default? cmdName"} +} + ####################################################### # parametercheck ####################################################### @@ -154,10 +167,10 @@ } ####################################################### -# parametercheck +# param manager ####################################################### Test parameter count 10000 -Test case parametercheck { +Test case param-manager { Object create ::paramManager { :method type=sex {name value} {