###############################################################################
#
# interp-exited.eagle --
#
# Extensible Adaptable Generalized Logic Engine (Eagle)
#
# Copyright (c) 2007-2012 by Joe Mistachkin.  All rights reserved.
#
# See the file "license.terms" for information on usage and redistribution of
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# RCS: @(#) $Id: $
#
###############################################################################

source [file join [file normalize [file dirname [info script]]] prologue.eagle]

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

runTest {test exit-1.1 {eval in exited interpreter} -body {
  exit 1; return "hello world"
} -constraints {eagle shell} -noExit true -exitCode 1 -result {}}

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

runTest {test interp-1.1 {create and use interpreter} -body {
  set error ""

  list [set i [interp create]] \
      [expr {int([interp exists {}])}] \
      [expr {int([interp exists $i])}] \
      [expr {int([interp issafe {}])}] \
      [expr {int([interp issafe $i])}] \
      [llength [interp slaves]] \
      [catch {interp eval $i {pwd; return OK}} error] \
      $error \
      [catch {expr {int([file exists [interp invokehidden $i pwd]])}} error] \
      $error \
      [interp delete $i] \
      [expr {int([interp exists {}])}] \
      [expr {int([interp exists $i])}]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain error i
} -match regexp -result \
{^(?:interp)?\d+ 1 1 0 0 1 0 OK 1 \{invalid hidden command name "pwd"\} \{\} 1\
0$}}

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

runTest {test interp-1.2 {create and use safe interpreter} -body {
  set error ""

  list [set i [interp create -safe]] \
      [expr {int([interp exists {}])}] \
      [expr {int([interp exists $i])}] \
      [expr {int([interp issafe {}])}] \
      [expr {int([interp issafe $i])}] \
      [llength [interp slaves]] \
      [catch {interp eval $i {pwd; return OK}} error] $error \
      [catch {expr {int([file exists [interp invokehidden $i pwd]])}} error] \
      $error \
      [interp delete $i] \
      [expr {int([interp exists {}])}] \
      [expr {int([interp exists $i])}]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain error i
} -match regexp -result \
{^(?:interp)?\d+ 1 1 0 1 1 1 \{invalid command name "pwd"\} 0 1 \{\} 1 0$}}

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

runTest {test interp-1.3 {use of hide/invokehidden/expose} -body {
  set error ""

  list [set i [interp create -safe]] \
      [catch {expr {int([file exists [interp invokehidden $i pwd]])}} error] \
      $error \
      [catch {interp expose $i pwd} error] $error \
      [catch {interp expose $i pwd} error] $error \
      [catch {expr {int([file exists [interp invokehidden $i pwd]])}} error] \
      $error \
      [catch {interp hide $i pwd} error] $error \
      [catch {interp hide $i pwd} error] $error \
      [catch {expr {int([file exists [interp invokehidden $i pwd]])}} error] \
      $error \
      [interp delete $i]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain error i
} -match regexp -result \
{^(?:interp)?\d+ 0 1 0 \{\} 1 \{unknown hidden command "pwd"\} 1 \{invalid\
hidden command name "pwd"\} 0 \{\} 1 \{unknown command "pwd"\} 0 1 \{\}$}}

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

runTest {test interp-1.4.1 {create and use interpreter alias} -setup {
  proc newPwd {} {
    return [expr {int([file exists [pwd]])}]
  }
} -body {
  set error ""

  list [set i [interp create -safe]] \
      [set token [interp alias $i safePwd {} newPwd]] \
      [interp aliases] \
      [interp aliases $i] \
      [interp eval $i safePwd] \
      [interp alias $i $token {}] \
      [interp aliases] \
      [interp aliases $i] \
      [interp delete $i]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain token error i

  rename newPwd ""
} -constraints {tcl tcl84} -match regexp -result \
{^(?:interp)?\d+ safePwd \{\} safePwd 1 \{\} \{\} \{\} \{\}$}}

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

runTest {test interp-1.4.2 {create and use interpreter alias} -setup {
  proc newPwd {} {
    return [expr {int([file exists [pwd]])}]
  }
} -body {
  set error ""

  list [set i [interp create -safe]] \
      [set token [interp alias $i safePwd {} newPwd]] \
      [lsort [interp aliases]] \
      [lsort [interp aliases $i]] \
      [interp eval $i safePwd] \
      [interp alias $i $token {}] \
      [lsort [interp aliases]] \
      [lsort [interp aliases $i]] \
      [interp delete $i]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain token error i

  rename newPwd ""
} -constraints {eagle} -match regexp -result {^(?:interp)?\d+ safePwd\
\{::tcltest::testConstraint testConstraint\} safePwd 1 \{\}\
\{::tcltest::testConstraint testConstraint\} \{\} \{\}$}}

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

runTest {test interp-1.5 {scripted policies} -setup {
  proc nopPolicy {} {}

  proc okPolicy {} {
    return -code ok
  }

  proc errorPolicy {} {
    error "test: error"
  }

  proc returnPolicy {} {
    return -code return "test: return"
  }

  proc breakPolicy {} {
    return -code break "test: break"
  }

  proc continuePolicy {} {
    return -code continue "test: continue"
  }

  proc policyWithArgs { args } {
    if {[llength $args] % 2 == 0} then {
      return "allow: even number of args"
    } else {
      error "deny: odd number of args"
    }
  }

  set i [interp create -safe]
} -body {
  set scripts [list "" \
      nopPolicy okPolicy errorPolicy \
      returnPolicy breakPolicy continuePolicy \
      policyWithArgs [list policyWithArgs foo] \
      "policyWithArgs \{ foo bar"]

  set result [list]

  foreach script $scripts {
    #
    # NOTE: Try the policy script with the [info] command.
    #
    set policy [interp policy -flags \
        {+SplitList Arguments} -type \
        Eagle._Commands.Info $i $script]

    lappend result [catch {interp eval $i {
      info level
    }} error]
    lappend result $error
    lappend result [catch {interp eval $i {
      proc foo { a b c d } {
        return [info level 0]
      }
      foo 1 2 3 4
    }} error]
    lappend result $error

    interp nopolicy $i $policy

    #
    # NOTE: Try the policy script with the [pwd] command.
    #
    set policy [interp policy \
        -type Eagle._Commands.Pwd \
        $i $script]

    lappend result [catch {interp eval $i {
      expr {[string length [pwd]] > 0}
    }} error]
    lappend result $error
    lappend result [catch {interp eval $i {
      expr {[string length [pwd 1]] > 0}
    }} error]
    lappend result $error

    interp nopolicy $i $policy
  }

  set result
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain error policy script result scripts i

  rename policyWithArgs ""
  rename continuePolicy ""
  rename breakPolicy ""
  rename returnPolicy ""
  rename errorPolicy ""
  rename okPolicy ""
  rename nopPolicy ""
} -constraints {eagle} -result \
{0 0 1 {invalid command name "info"} 0 True 1 {wrong # args: should be "pwd"} 1\
{invalid command name "info"} 1 {invalid command name "info"} 0 True 1 {wrong #\
args: should be "pwd"} 1 {invalid command name "info"} 1 {invalid command name\
"info"} 0 True 1 {wrong # args: should be "pwd"} 1 {invalid command name\
"info"} 1 {invalid command name "info"} 1 {invalid command name "pwd"} 1\
{invalid command name "pwd"} 1 {invalid command name "info"} 1 {invalid command\
name "info"} 1 {invalid command name "pwd"} 1 {invalid command name "pwd"} 1\
{invalid command name "info"} 1 {invalid command name "info"} 1 {invalid\
command name "pwd"} 1 {invalid command name "pwd"} 1 {invalid command name\
"info"} 1 {invalid command name "info"} 1 {invalid command name "pwd"} 1\
{invalid command name "pwd"} 0 0 1 {invalid command name "info"} 0 True 1\
{wrong # args: should be "pwd"} 1 {invalid command name "info"} 0 {foo 1 2 3 4}\
1 {invalid command name "pwd"} 1 {invalid command name "pwd"} 1 {invalid\
command name "info"} 1 {invalid command name "info"} 1 {invalid command name\
"pwd"} 1 {invalid command name "pwd"}}}

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

runTest {test interp-1.6 {default [object] policy} -setup {
  #
  # NOTE: Create an "unsafe" slave interpreter.
  #
  set i [interp create]
} -body {
  #
  # NOTE: Evaluate the test script in the new interpreter.
  #
  interp eval $i {
    #
    # NOTE: Create some objects and command aliases.
    #
    set safeVar [object invoke -create -alias Int32 Parse 12345]
    set unsafeVar [object invoke -create -alias Int32 Parse 12345]

    #
    # NOTE: We need to flag the variables and command aliases for both the
    #       "safe" and "unsafe" objects as "safe" so they will survive the
    #       call to [interp makesafe].  This test is only really concerned
    #       about the flags on the IObject itself (i.e. only the "safe"
    #       IObject will be marked as "safe" prior to the test).
    #
    foreach varName [list safeVar unsafeVar] {
      #
      # NOTE: Grab the command alias we just created.
      #
      set execute null; set error null
      set code [object invoke -alias -flags +NonPublic Interpreter.GetActive \
          GetIExecuteViaResolvers UseAll [object invoke -flags +NonPublic \
          Eagle._Components.Private.MarshalOps WrapHandle "" [set $varName]] \
          null Default execute error]

      if {$code ne "Ok"} then {
        error [object invoke $error ToString]
      }

      #
      # NOTE: Make sure we do not dispose this object as we do not actually
      #       own it.
      #
      object flags $execute +NoDispose

      #
      # NOTE: Flag the command as "safe".
      #
      set cmdFlags [$execute -type Eagle._Interfaces.Public.ICommandData Flags]
      lappend cmdFlags Safe
      $execute -type Eagle._Interfaces.Public.ICommandData Flags $cmdFlags

      #
      # NOTE: Grab the variable we just created.
      #
      set flags GlobalOnly; set variable null; set error null
      set code [object invoke -alias -flags +NonPublic Interpreter.GetActive \
          GetVariableViaResolversWithSplit $varName flags variable error]

      if {$code ne "Ok"} then {
        error [object invoke $error ToString]
      }

      #
      # NOTE: Flag the variable as "safe".
      #
      set varFlags [$variable Flags]
      lappend varFlags Safe
      $variable Flags $varFlags
    }

    #
    # NOTE: Grab the "safe" object we just created.
    #
    set object null; set error null
    set code [object invoke -alias -flags +NonPublic Interpreter.GetActive \
        GetObject [object invoke -flags +NonPublic \
        Eagle._Components.Private.MarshalOps WrapHandle "" $safeVar] Default \
        object error]

    if {$code ne "Ok"} then {
      error [object invoke $error ToString]
    }

    #
    # NOTE: Make sure we do not dispose this object as we do not actually
    #       own it.
    #
    object flags $object +NoDispose

    #
    # NOTE: Flag the object as "safe".
    #
    set objFlags [$object Flags]
    lappend objFlags Safe
    $object Flags $objFlags

    #
    # NOTE: Make the interpreter "safe" now.  This will remove all "unsafe"
    #       commands, procedures, and variables.
    #
    interp makesafe

    #
    # NOTE: Try to use [object invoke] on both "safe" and "unsafe" objects.
    #
    list [catch {$safeVar ToString} msg1] $msg1 \
        [catch {$unsafeVar ToString} msg2] $msg2
  }
} -cleanup {
  catch {interp delete $i}
  unset -nocomplain i
} -constraints {eagle} -match regexp -result {^0 12345 1 \{permission denied:\
safe interpreter cannot use object from "System#Int32#\d+"\}$}}

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

runTest {test interp-1.7 {hidden command with non-hidden alias} -setup {
  proc safeProc { interp name args body } {
    if {[llength [interp eval $interp [list info commands $name]]] != 0} then {
      error [appendArgs "can't add \"" $name "\": command already exists"]
    }

    return [interp invokehidden $interp proc $name $args $body]
  }

  set i [interp create -safe]
  set error ""
} -body {
  interp hide $i proc
  interp alias $i proc {} safeProc $i

  set script {interp eval $i [list proc foo { a } { return test }]}

  list [catch $script error] $error [catch $script error] $error \
      [catch {interp eval $i foo 1}] $error
} -cleanup {
  catch {interp delete $i}
  unset -nocomplain script error i

  rename safeProc ""
} -result {0 {} 1 {can't add "foo": command already exists} 0\
{can't add "foo": command already exists}}}

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

runTest {test interp-1.8 {no disposal of slave interpreters} -setup {
  set i [interp create -safe]
} -body {
  set interps [object invoke -flags +NonPublic Interpreter.GetActive \
      slaveInterpreters.Values]

  object foreach interp $interps {
    object dispose $interp
  }

  object foreach interp $interps {
    object invoke $interp Id; # disposed?
  }
} -cleanup {
  catch {interp delete $i}
  unset -nocomplain interps interp i
} -constraints {eagle} -result {}}

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

runTest {test interp-1.9 {WrapHandle returns input on not found} -body {
  object invoke -flags +NonPublic Eagle._Components.Private.MarshalOps \
      WrapHandle "" some_value
} -constraints {eagle} -result {some_value}}

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

runTest {test interp-1.10 {load/save interpreter settings} -setup {
  set fileName [file join [getTemporaryPath] settings.xml]
} -body {
  #
  # NOTE: Load the known test settings.  These will later be compared with the
  #       "live" settings created by this test.
  #
  set is(1) null
  set error null
  set code [object invoke -alias InterpreterSettings LoadFrom \
      [file join $test_data_path settings.xml] false is(1) error]

  if {$code ne "Ok"} then {
    error [object invoke $error ToString]
  }

  #
  # NOTE: Create some "live" settings to be saved, loaded, and then compared
  #       with the known test settings.
  #
  set is(2) [object invoke -alias InterpreterSettings Create]

  $is(2) Args [set args [object create StringList a b c d]]
  $is(2) Culture en-US
  $is(2) CreateFlags Default
  $is(2) ScriptFlags Default
  $is(2) AppDomain null; # ignored, not serialized.
  $is(2) Host null; # ignored, not serialized.
  $is(2) Profile Color
  $is(2) ApplicationObject [set obj(1) [object create Object]]

  $is(2) PolicyObject [set obj(2) \
      [object create String "this is a test."]]

  $is(2) ResolverObject [set obj(3) \
      [object invoke -create Int32 Parse -559038737]]

  $is(2) UserObject [set obj(4) \
      [object invoke -create System.Boolean Parse True]]

  $is(2) Text {puts stdout "this is the pre-init script."}

  $is(2) LibraryPath [appendArgs %EAGLE%\\lib\\ [info engine] \
      [info engine version]]

  $is(2) AutoPathList [set list [object create StringList \
      %EAGLE%\\bin\\Debug\\lib does_not_exist]]

  #
  # NOTE: Save the "live" settings we just created in the above step.
  #
  set error null
  set code [object invoke InterpreterSettings SaveTo \
      $fileName false $is(2) error]

  if {$code ne "Ok"} then {
    error [object invoke $error ToString]
  }

  #
  # NOTE: Load the "live" settings we just saved in the above step.
  #
  set is(3) null
  set error null
  set code [object invoke -alias InterpreterSettings LoadFrom \
      $fileName false is(3) error]

  if {$code ne "Ok"} then {
    error [object invoke $error ToString]
  }

  #
  # NOTE: Make sure the round-tripping process resulted in matching properties
  #       on both InterpreterSettings objects.  This also verifies that the
  #       properties marked to be ignored were not serialized/deserialized.
  #
  list [string equal [$is(1) Culture] [$is(3) Culture]] \
      [string equal [$is(1) Profile] [$is(3) Profile]] \
      [string equal [$is(1) ApplicationObject] [$is(3) ApplicationObject]] \
      [string equal [$is(1) PolicyObject] [$is(3) PolicyObject]] \
      [string equal [$is(1) ResolverObject] [$is(3) ResolverObject]] \
      [string equal [$is(1) UserObject] [$is(3) UserObject]] \
      [string equal [$is(1) Text] [$is(3) Text]] \
      [string equal [$is(1) LibraryPath] [$is(3) LibraryPath]] \
      [string equal [$is(1) Args] [$is(3) Args]] \
      [string equal [$is(1) CreateFlags] [$is(3) CreateFlags]] \
      [string equal [$is(1) ScriptFlags] [$is(3) ScriptFlags]] \
      [string equal [$is(1) AutoPathList] [$is(3) AutoPathList]]
} -cleanup {
  catch {file delete $fileName}

  unset -nocomplain error code obj args is list fileName
} -constraints {eagle compile.XML compile.SERIALIZATION file_settings.xml} \
-result {True True True True True True True True True True True True}}

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

runTest {test interp-1.11 {load/use interpreter settings} -body {
  set settings null
  set error null
  set code [object invoke -alias InterpreterSettings LoadFrom \
      [file join $test_data_path settings.xml] true settings error]

  if {$code ne "Ok"} then {
    error [object invoke $error ToString]
  }

  set result null
  set interp [object invoke -alias Interpreter Create $settings true result]

  if {[string length $interp] == 0} then {
    error [object invoke $result ToString]
  }

  set result null
  set code [$interp EvaluateScript {set argv} result]

  if {$code eq "Ok"} then {
    set args [object invoke $result ToString]
  } else {
    error [object invoke $result ToString]
  }

  list [string equal [$settings Culture] [$interp CultureInfo.ToString]] \
      [string equal [$settings Profile] [$interp Host.Profile]] \
      [string equal [$settings Text] \
          [$interp -flags +NonPublic preInitializeText]] \
      [string equal [$settings LibraryPath] [$interp LibraryPath]] \
      [string equal [$settings Args] $args] \
      [string equal [$settings CreateFlags] \
          [$interp -flags +NonPublic CreateFlags]] \
      [string equal [$settings ScriptFlags] \
          [$interp -flags +NonPublic ScriptFlags]] \
      [string equal [$settings AutoPathList] [$interp AutoPathList]]
} -cleanup {
  unset -nocomplain args result interp error code settings
} -constraints {eagle compile.XML compile.SERIALIZATION file_settings.xml} \
-result {True True True True True True True True}}

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

#
# NOTE: This test will cause an unhandled exception in all Eagle builds from
#       beta 19 to beta 21.
#
runTest {test interp-1.12 {interpreter disposal via destructor} -body {
  #
  # NOTE: Create a new interpreter that we can play with and then dispose.
  #
  set result null
  set interp [object invoke Interpreter Create result]

  if {[string length $interp] == 0} then {
    error [object invoke $result ToString]
  }

  #
  # NOTE: Prevent the message boxes from popping up when the inevitable object
  #       already disposed exceptions occur later on.
  #
  object invoke $interp Quiet true

  #
  # NOTE: First, remove the interpreter from the global list containing all
  #       interpreters created in this application domain.
  #
  object invoke Eagle._Components.Private.GlobalState RemoveInterpreter $interp

  #
  # NOTE: Next, clear out any reference to the interpreter from the core host
  #       (just in case the derived host class, if any, has external references
  #       that may keep it alive, which would indirectly keep the interpreter
  #       we are trying to dispose of via the GC alive as well).
  #
  catch {
    set host [object invoke $interp Host]; object invoke -flags +NonPublic \
        -type Eagle._Hosts.Core $host interpreter null; object dispose \
        -nodispose $host
  }

  #
  # NOTE: Next, if necessary, make sure that all thread-local data that refers
  #       to the interpreter is disposed and/or cleared.
  #
  if {[haveConstraint compile.THREADING]} then {
    object invoke -flags +NonPublic $interp contextManager.Dispose
  }

  #
  # NOTE: Next, remove the interpreter from the internal object list without
  #       actually disposing of it.
  #
  object dispose -nodispose $interp

  #
  # NOTE: Finally, force the previously removed interpreter to be disposed via
  #       the finalizer, trigged by the GC.
  #
  object invoke GC GetTotalMemory true
} -cleanup {
  unset -nocomplain newObjects host interp result
} -constraints {eagle} -match regexp -result {^\d+$}}

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

runTest {test interp-1.13 {sandbox variable context} -setup {
  unset -nocomplain msg x i

  proc setDataProc {} {
    foreach d [list 1 7 3 4 6 7 3 2 1 4 7 6 C \
                    3 2 7 8 9 7 7 7 6 4 3 T 7 \
                    3 2 V 7 3 1 1 7 8 8 8 7 3 \
                    2 4 7 6 7 8 9 7 6 4 3 7 6] {
      append ::x $d
    }
  }

  set i [interp create -safe]
} -body {
  list [interp alias $i setDataProc {} setDataProc] \
      [interp eval $i setDataProc] \
      [catch {interp eval $i [list set x]} msg] $msg \
      [interp eval $i [list set x 1]] \
      [expr {[info exists x] ? [string length $x] : -1}] \
      [setDataProc] \
      [expr {[info exists x] ? [string length $x] : -1}]
} -cleanup {
  catch {interp delete $i}

  rename setDataProc ""

  unset -nocomplain msg x i
} -result {setDataProc {} 1 {can't read "x": no such variable} 1 52 {} 104}}

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

runTest {test interp-1.14 {alias chaining with nested interps} -setup {
  proc getLevel0 { args } {
    return [info level [info level]]
  }

  set i(1) [interp create]
  set i(2) [interp eval $i(1) [list interp create]]
  set i(3) [interp eval [list $i(1) $i(2)] [list interp create]]
} -body {
  list [interp alias $i(1) getLevel1 {} getLevel0] \
      [interp alias [list $i(1) $i(2)] getLevel2 $i(1) getLevel1] \
      [interp alias [list $i(1) $i(2) $i(3)] getLevel3 \
          [list $i(1) $i(2)] getLevel2] \
      [interp eval $i(1) getLevel1] \
      [interp eval [list $i(1) $i(2)] getLevel2] \
      [interp eval [list $i(1) $i(2) $i(3)] getLevel3]
} -cleanup {
  catch {interp delete [list $i(1) $i(2) $i(3)]}
  catch {interp delete [list $i(1) $i(2)]}
  catch {interp delete $i(1)}

  rename getLevel0 ""

  unset -nocomplain i
} -result {getLevel1 getLevel2 getLevel3 getLevel0 getLevel0 getLevel0}}

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

#
# HACK: Full test interpreter isolation is required for this test; otherwise,
#       the interpreter running the test suite could be corrupted if the test
#       fails.
#
runTest {test interp-1.15 {disposal during command execution} -setup {
  set i [interp create]
} -body {
  interp eval $i {
    object invoke Interpreter.GetActive Dispose
  }
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle} -isolationLevel Interpreter -returnCodes 1 -result \
{interpreter is unusable (it may have been disposed)}}

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

#
# HACK: Full test interpreter isolation is required for this test; otherwise,
#       the interpreter running the test suite could be corrupted if the test
#       fails.
#
runTest {test interp-1.16 {disposal during procedure execution} -setup {
  set i [interp create]
} -body {
  interp eval $i {
    proc doDispose {} {
      object invoke Interpreter.GetActive Dispose
    }
    doDispose
  }
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle} -isolationLevel Interpreter -returnCodes 1 -result \
{interpreter is unusable (it may have been disposed)}}

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

#
# HACK: Full test interpreter isolation is required for this test; otherwise,
#       the interpreter running the test suite could be corrupted if the test
#       fails.
#
runTest {test interp-1.17 {disposal during lambda execution} -setup {
  set i [interp create]
} -body {
  interp eval $i {
    apply [list [list] { object invoke Interpreter.GetActive Dispose }]
  }
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle} -isolationLevel Interpreter -returnCodes 1 -result \
{interpreter is unusable (it may have been disposed)}}

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

#
# HACK: Full test interpreter isolation is required for this test; otherwise,
#       the interpreter running the test suite could be corrupted if the test
#       fails.
#
runTest {test interp-1.18 {disposal during function #1 execution} -setup {
  set i [interp create]
} -body {
  interp eval $i {
    set result null
    set code [object invoke -alias Eagle._Tests.Default TestAddNamedFunction \
        "" testFunc null result]

    if {$code ne "Ok"} then {
      error [object invoke $result ToString]
    }

    expr {testFunc("0 + \[object invoke Interpreter.GetActive Dispose\]")}
  }
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle compile.TEST Eagle._Tests.Default.TestAddNamedFunction\
Eagle._Tests.Default.TestRemoveNamedFunction} -isolationLevel Interpreter \
-returnCodes 1 -result {interpreter is unusable (it may have been disposed)}}

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

runTest {test interp-1.19 {disposal during function #2 execution} -setup {
  set i [interp create]
} -body {
  set result null
  set code [object invoke -alias Eagle._Tests.Default TestAddNamedFunction2 \
      $i testFunc null result]

  if {$code ne "Ok"} then {
    error [object invoke $result ToString]
  }

  set result null
  set code [object invoke -alias Engine EvaluateExpression $i \
      {testFunc(1, "")} result]

  list $code [expr {[string length $result] > 0 ? [$result ToString] : ""}]
} -cleanup {
  catch {
    set result null
    set code [object invoke -alias Eagle._Tests.Default \
        TestRemoveNamedFunction $i testFunc null result]

    if {$code ne "Ok"} then {
      tputs $test_channel [appendArgs \
          "---- failed to remove math function \"testFunc\": " \
          [object invoke $result ToString] \n]
    }
  }

  catch {interp delete $i}

  unset -nocomplain result code i
} -constraints {eagle compile.TEST Eagle._Tests.Default.TestAddNamedFunction\
Eagle._Tests.Default.TestRemoveNamedFunction} -result \
{Error {interpreter is unusable (it may have been disposed)}}}

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

#
# HACK: Full test interpreter isolation is required for this test; otherwise,
#       the interpreter running the test suite could be corrupted if the test
#       fails.
#
runTest {test interp-1.20 {disposal during subst} -setup {
  set i [interp create]
} -body {
  interp eval $i {
    subst {[object invoke Interpreter.GetActive Dispose]}
  }
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle} -isolationLevel Interpreter -returnCodes 1 -result \
{interpreter is unusable (it may have been disposed)}}

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

#
# HACK: Full test interpreter isolation is required for this test; otherwise,
#       the interpreter running the test suite could be corrupted if the test
#       fails.
#
runTest {test interp-1.21 {disposal during expr} -setup {
  set i [interp create]
} -body {
  interp eval $i {
    expr {0 + [object invoke Interpreter.GetActive Dispose]}
  }
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle} -isolationLevel Interpreter -returnCodes 1 -result \
{interpreter is unusable (it may have been disposed)}}

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

runTest {test interp-1.22 {interp expr} -setup {
  set i [interp create]
} -body {
  list [interp expr $i 2 + 2] [interp expr $i {2 + 2}]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle} -result {4 4}}

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

runTest {test interp-1.23 {interp subst} -setup {
  set x {0\t1[set y 2]3${null}4\t\t5[incr y]\t6$y\t7[unset y]8$null\\}
  set i [interp create]
} -body {
  set result ""

  append result [catch {interp subst $i $x} r]
  append result " " $r " -- "
  append result [catch {interp subst -nobackslashes $i $x} r]
  append result " " $r " -- "
  append result [catch {interp subst -nocommands $i $x} r]
  append result " " $r " -- "
  append result [catch {interp subst -novariables $i $x} r]
  append result " " $r " -- "
  append result [catch {interp subst -nobackslashes -nocommands $i $x} r]
  append result " " $r " -- "
  append result [catch {interp subst -nobackslashes -novariables $i $x} r]
  append result " " $r " -- "
  append result [catch {interp subst -nobackslashes -nocommands -novariables \
      $i $x} r]
  append result " " $r
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain r result i x
} -constraints {eagle} -result \
"0 0\t123null4\t\t53\t63\t78null\\ -- 0 0\\t123null4\\t\\t53\\t63\\t78null\\\\\
-- 1 can't read \"y\": no such variable -- 0\
0\t123\${null}4\t\t53\t6\$y\t78\$null\\ -- 1 can't read \"y\": no such variable\
-- 0 0\\t123\${null}4\\t\\t53\\t6\$y\\t78\$null\\\\ -- 0 0\\t1\[set y\
2\]3\${null}4\\t\\t5\[incr y]\\t6\$y\\t7\[unset y\]8\$null\\\\"}

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

runTest {test interp-1.24 {disposal during expression evaluation} -setup {
  set i [interp create]
} -body {
  set result null
  set code [object invoke -alias Engine EvaluateExpression $i \
      {0 + [object invoke Interpreter.GetActive Dispose]} result]

  list $code [expr {[string length $result] > 0 ? [$result ToString] : ""}]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain result code i
} -constraints {eagle} -result \
{Error {interpreter is unusable (it may have been disposed)}}}

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

#
# HACK: We need to set an actual variable with the value of the quiet
#       constraint so that the [subst] invocation for the setup script for
#       the next test can use it to set the quiet mode for the isolated
#       interpreter created by that test.
#
unset -nocomplain quiet
set quiet [haveConstraint quiet]

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

unset -nocomplain test_log_if
set test_log_if [expr {[info exists test_log] ? $test_log : ""}]

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

unset -nocomplain test_have_winforms
set test_have_winforms [haveConstraint compile.WINFORMS]

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

#
# HACK: Full test interpreter isolation is required for this test; otherwise,
#       the interpreter running the test suite could be corrupted if the test
#       fails.
#
runTest {test interp-1.25 {disposal during async event} -setup [subst \
    -nocommands -nobackslashes {
  set i [interp create]

  #
  # NOTE: Check if the parent interpreter has the "quiet" test constraint
  #       enabled via the value of the $quiet variable that was magically
  #       "transferred" from the parent interpreter to this isolated test
  #       interpreter via the enclosing [subst] invocation.  If so, set
  #       the default quiet mode value.
  #
  unset -nocomplain savedDefaultQuiet

  if {$quiet} then {
    set savedDefaultQuiet [object invoke -flags +NonPublic \
        Interpreter DefaultQuiet]

    object invoke -flags +NonPublic Interpreter DefaultQuiet true
  }

  #
  # NOTE: Check for and load the Eagle test package, if necessary.
  #
  if {[catch {package present Eagle.Test}]} then {
    package require Eagle.Test
  }

  #
  # NOTE: Set the channel to use for test output, if necessary.
  #
  if {![info exists test_channel]} then {
    set test_channel {$test_channel}
  }

  #
  # NOTE: Set the log file name to use for test output, if necessary.
  #
  if {![info exists test_log]} then {
    set test_log {$test_log_if}
  }

  #
  # NOTE: Save the WinForms flag for use by the test body.
  #
  if {![info exists test_have_winforms]} then {
    set test_have_winforms {$test_have_winforms}
  }
}] -body {
  if {![info exists savedDefaultQuiet] && $test_have_winforms} then {
    tputs $test_channel [appendArgs \
        "please find the window marked \"" [info engine] \
        "\" and click \"OK\" to continue.\n"]
  }

  interp eval $i {
    after 1000 [list object invoke Interpreter.GetActive Dispose]
    after 2000 [list set ::result true]
    vwait ::result
  }
} -cleanup {
  catch {interp delete $i}

  if {[info exists savedDefaultQuiet]} then {
    object invoke -flags +NonPublic \
        Interpreter DefaultQuiet $savedDefaultQuiet

    unset -nocomplain savedDefaultQuiet
  }

  unset -nocomplain test_have_winforms test_log test_channel i
} -constraints {eagle} -isolationLevel Interpreter -returnCodes 1 -result \
{interpreter is unusable (it may have been disposed)}}

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

unset -nocomplain test_have_winforms
unset -nocomplain test_log_if

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

runTest {test interp-1.26 {interp alias w/multiple nested interps} -setup {
  set i(1) [interp create]
  set i(2) [interp create]
} -body {
  interp alias $i(1) aCmd $i(2) eval
  list [interp eval $i(1) aCmd set x 1] [interp delete $i(2)] \
      [catch {interp eval $i(1) aCmd set x 1} msg] $msg
} -cleanup {
  catch {interp delete $i(2)}
  catch {interp delete $i(1)}

  unset -nocomplain i msg
} -result {1 {} 1 {invalid command name "aCmd"}}}

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

runTest {test interp-1.27 {isolated slave interpreters} -setup {
  set i(1) [interp create]
  set i(2) [interp create test1]
  set i(3) [interp create -safe]
  set i(4) [interp create -isolated]
  set i(5) [interp create -isolated -safe]
  set i(6) [interp create -isolated test2]

  interp alias $i(1) aCmd {} eval
  interp alias $i(2) aCmd {} eval
  interp alias $i(3) aCmd {} eval
  interp alias $i(4) aCmd {} eval
  interp alias $i(5) aCmd {} eval
  interp alias $i(6) aCmd {} eval
} -body {
  set scripts(1) [list {info appdomain} {aCmd info appdomain}]
  set scripts(2) [list {interp issafe} {string length [pwd]}]

  foreach id [lsort -integer [array names i]] {
    lappend result $id $i($id)
    foreach script $scripts(1) {
      if {[catch {interp eval $i($id) $script} msg] == 0} then {
        lappend result 0 $msg [expr {$msg == [info appdomain]}]
      } else {
        lappend result 1 $msg False
      }
    }
    foreach script $scripts(2) {
      lappend result [catch {interp eval $i($id) $script} msg] $msg
    }
  }

  set result
} -cleanup {
  catch {interp delete $i(6)}
  catch {interp delete $i(5)}
  catch {interp delete $i(4)}
  catch {interp delete $i(3)}
  catch {interp delete $i(2)}
  catch {interp delete $i(1)}

  unset -nocomplain msg result scripts script i id
} -constraints {eagle compile.APPDOMAINS compile.ISOLATED_INTERPRETERS} -match \
regexp -result {^1 \d+ 0 \d+ True 0 \d+ True 0 False 0 \d+ 2 test1 0 \d+ True 0\
\d+ True 0 False 0 \d+ 3 \d+ 0 \d+ True 0 \d+ True 0 True 1 \{invalid command\
name "pwd"\} 4 \d+ 0 \d+ False 0 \d+ True 0 False 0 \d+ 5 \d+ 0 \d+ False 0 \d+\
True 0 True 1 \{invalid command name "pwd"\} 6 test2 0 \d+ False 0 \d+ True 0\
False 0 \d+$}}

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

runTest {test interp-1.28 {EvaluateTrustedScript method} -setup {
  set i [interp create -safe]
} -body {
  set interp [object invoke \
      -alias -flags +NonPublic -objectflags +NoDispose \
      Interpreter.GetActive.slaveInterpreters get_Item $i]

  set result null

  list [interp issafe $i] [$interp IsSafe] [object invoke $interp \
      EvaluateScript "info pid" result] [expr {[string length $result] > 0 ? \
      [object invoke $result ToString] : ""}] [object invoke $interp \
      EvaluateTrustedScript "info pid" false result] [expr {[string length \
      $result] > 0 ? [object invoke $result ToString] : ""}]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain result interp i
} -constraints {eagle} -match regexp -result \
{^True True Error \{invalid command name "info"\} Ok \d+$}}

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

#
# HACK: *MONO* This test is disabled on Mono due to internal implementation
#       details regarding how synchronization locks are handled.  Specifically,
#       what type of exceptions are raised, if any, when Monitor.Exit is called
#       on a lock not held by the calling thread.
#
runTest {test interp-1.29 {LockAndMarkTrusted / MarkSafeAndUnlock} -setup {
  set i [interp create]
} -body {
  set interp [object invoke \
      -alias -flags +NonPublic -objectflags +NoDispose \
      Interpreter.GetActive.slaveInterpreters get_Item $i]

  list [$interp IsSafe] [set error1 null] \
    [catch {$interp LockAndMarkTrusted error1} error2] \
    [expr {[string length $error1] > 0 && $error1 ne "null" ? \
        [object invoke $error1 ToString] : ""}] \
    $error2 [$interp IsSafe] [set error1 null] \
    [catch {$interp MarkSafeAndUnlock error1} error2] \
    [expr {[string length $error1] > 0 && $error1 ne "null" ? \
        [object invoke $error1 ToString] : ""}] \
    $error2 [$interp IsSafe] [set error1 null] \
    [catch {$interp MarkSafeAndUnlock error1} error2] \
    [expr {[string length $error1] > 0 && $error1 ne "null" ? \
        [object invoke $error1 ToString] : ""}] \
    $error2 [$interp IsSafe]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain error2 error1 interp i
} -constraints {eagle dotNet} -match regexp -result \
{^False null 0 \{interpreter is already marked as trusted\} Error False null 1\
\{\} \{System\.Reflection\.TargetInvocationException: .*? --->\
System\.Threading\.SynchronizationLockException: .*?\} True null 0\
\{interpreter is already marked as safe\} Error True$}}

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

runTest {test interp-1.30 {LockAndMarkTrusted / MarkSafeAndUnlock} -setup {
  set i [interp create -safe]
} -body {
  set interp [object invoke \
      -alias -flags +NonPublic -objectflags +NoDispose \
      Interpreter.GetActive.slaveInterpreters get_Item $i]

  list [$interp IsSafe] [set error null] [$interp LockAndMarkTrusted error] \
    [expr {[string length $error] > 0 ? [object invoke $error ToString] : ""}] \
    [$interp IsSafe] [set error null] [$interp MarkSafeAndUnlock error] [expr \
    {[string length $error] > 0 ? [object invoke $error ToString] : ""}] \
    [$interp IsSafe] [set error null] [$interp MarkSafeAndUnlock error] [expr \
    {[string length $error] > 0 ? [object invoke $error ToString] : ""}] \
    [$interp IsSafe]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain error interp i
} -constraints {eagle} -result {True null Ok {} False null Ok {} True null\
Error {interpreter is already marked as safe} True}}

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

runTest {test interp-1.31 {try/finally without Engine.ResetCancel} -setup {
  set i [interp create]

  interp eval $i {
    catch {
      #
      # HACK: Just in case it is actually the default, force the necessary
      #       interpreter flags to be disabled for the created interpreter.
      #
      object invoke -flags +NonPublic Interpreter.GetActive interpreterFlags \
          [appendArgs [object invoke -flags +NonPublic Interpreter.GetActive \
          interpreterFlags] " -FinallyResetCancel FinallyRestoreCancel"]
    }
  }
} -body {
  catch {
    interp eval $i {
      catch {
        try {
          #
          # NOTE: This script cancels itself.  Upon being canceled,
          #       the value of "x" will be "1".
          #
          set x 1; interp cancel -unwind -- {}; set x 3
        } finally {
          #
          # NOTE: This script block should never be evaluated.  Upon
          #       completion, the value of "x" should be "1".  If the
          #       value of "x" is "2" instead, that means this finally
          #       script block was evaluated.
          #
          incr x
        }
      }
      set x 5
    }
  }

  interp set $i x
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle tip285} -result {1}}

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

runTest {test interp-1.32 {try/finally with Engine.ResetCancel} -setup {
  set i [interp create]

  interp eval $i {
    catch {
      #
      # HACK: Just in case it is not actually the default, force the
      #       necessary interpreter flags to be enabled for the created
      #       interpreter.
      #
      object invoke -flags +NonPublic Interpreter.GetActive interpreterFlags \
          [appendArgs [object invoke -flags +NonPublic Interpreter.GetActive \
          interpreterFlags] " +FinallyResetCancel FinallyRestoreCancel"]
    }
  }
} -body {
  catch {
    interp eval $i {
      catch {
        try {
          #
          # NOTE: This script cancels itself.  Upon being canceled,
          #       the value of "x" will be "1".
          #
          set x 1; interp cancel -unwind -- {}; set x 3
        } finally {
          #
          # NOTE: This script block should always be evaluated.  Upon
          #       completion, the value of "x" should be "2".  If the
          #       value of "x" is "1" instead, that means this finally
          #       script block was not evaluated.
          #
          incr x
        }
      }
      set x 5
    }
  }

  interp set $i x
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle tip285} -result {2}}

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

runTest {test interp-1.33 {visible/hidden command resolution} -setup {
  unset -nocomplain error policy i

  set i [interp create -safe]
  interp recursionlimit $i 10
} -body {
  list [set policy [interp policy -type Eagle._Commands.Info $i {}]] \
      [catch {interp eval $i {exec this_is_not_found &}} error] $error \
      [interp nopolicy $i $policy]
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain error policy i
} -constraints {eagle testExec} -result {{Eagle._Components.Public.ScriptPolicy\
--> Eagle._Commands.Info} 1 {invalid command name "exec"} {}}}

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

runTest {test interp-1.34 {finally block without timeout} -setup {
  set i [interp create -safe]
  # interp finallytimeout $i -1

  #
  # NOTE: Check if the parent interpreter has the "quiet" test constraint
  #       enabled.  If so, enable quiet mode and disable the default
  #       background error handler for this isolated test interpreter to
  #       prevent superfluous console output.
  #
  if {$quiet} then {
    set interp [object invoke \
        -alias -flags +NonPublic -objectflags +NoDispose \
        Interpreter.GetActive.slaveInterpreters get_Item $i]

    set error null
    $interp LockAndMarkTrusted error

    try {
      interp expose $i object

      try {
        interp eval $i {
          object invoke Interpreter.GetActive Quiet true
          object invoke -flags +NonPublic Interpreter.GetActive \
              SetNoBackgroundError true
        }
      } finally {
        interp hide $i object
      }
    } finally {
      set error null
      $interp MarkSafeAndUnlock error
    }

    unset -nocomplain error interp
  }
} -body {
  set code(1) [catch {interp eval $i {
    try {
      #
      # NOTE: Simulate very long running try script being canceled.
      #
      after 4000 [list interp cancel -unwind]; vwait forever
    } finally {
      after 4000; # NOTE: Simulate very long running finally script.
      set x done; # NOTE: We SHOULD get here.
    }
  }} result(1)]

  set code(2) [catch {
    interp set $i x
  } result(2)]

  list $code(1) $result(1) $code(2) $result(2)
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain result code i
} -constraints {eagle tip285} -result {1 {eval unwound} 0 done}}

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

runTest {test interp-1.35 {finally block with timeout} -setup {
  set i [interp create -safe]
  interp finallytimeout $i 2000

  #
  # NOTE: Check if the parent interpreter has the "quiet" test constraint
  #       enabled.  If so, enable quiet mode and disable the default
  #       background error handler for this isolated test interpreter to
  #       prevent superfluous console output.
  #
  if {$quiet} then {
    set interp [object invoke \
        -alias -flags +NonPublic -objectflags +NoDispose \
        Interpreter.GetActive.slaveInterpreters get_Item $i]

    set error null
    $interp LockAndMarkTrusted error

    try {
      interp expose $i object

      try {
        interp eval $i {
          object invoke Interpreter.GetActive Quiet true
          object invoke -flags +NonPublic Interpreter.GetActive \
              SetNoBackgroundError true
        }
      } finally {
        interp hide $i object
      }
    } finally {
      set error null
      $interp MarkSafeAndUnlock error
    }

    unset -nocomplain error interp
  }
} -body {
  set code(1) [catch {interp eval $i {
    try {
      #
      # NOTE: Simulate very long running try script being canceled.
      #
      after 4000 [list interp cancel -unwind]; vwait forever
    } finally {
      after 4000; # NOTE: Simulate very long running finally script.
      set x done; # NOTE: We SHOULD NOT get here.
    }
  }} result(1)]

  set code(2) [catch {
    interp set $i x
  } result(2)]

  list $code(1) $result(1) $code(2) $result(2)
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain result code i
} -constraints {eagle tip285} -result {1 {runaway finally script unwound after\
2000 milliseconds} 1 {can't read "x": no such variable}}}

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

unset -nocomplain quiet

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

runTest {test interp-1.36 {interp enabled} -setup {
  set i [interp create -safe]
} -body {
  list [interp enabled $i] \
      [catch {interp eval $i {set x(1) 0}}] \
      [interp enabled $i false] \
      [interp enabled $i] \
      [catch {interp eval $i {set x(2) 1}}] \
      [interp enabled $i true] \
      [interp enabled $i] \
      [catch {interp eval $i {set x(3) 2}}] \
      [interp enabled $i] \
      [catch {interp eval $i {
        set y [list]; foreach name [lsort -integer [array names x]] {
          lappend y [list $name $x($name)]
        }; set y
      }} result] $result
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain result i
} -constraints {eagle} -result \
{True 0 False False 1 True True 0 True 0 {{1 0} {3 2}}}}

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

runTest {test interp-1.37 {"unsafe" load settings via script} -setup {
  unset -nocomplain fileName settings error code
} -body {
  set fileName [file join [getTemporaryPath] interp-1.37.eagle]

  writeFile $fileName {
    set a 1; set b [interp issafe]; set c 2; set d $a$c
    set e(1) 3; set e(2) 4; set e(3,0) 5
  }

  set settings null; set error null
  set code [object invoke -alias \
      Utility LoadSettingsViaScriptFile $fileName false settings error]

  list $code \
      [expr {[string length $settings] > 0 ? \
          [$settings KeysAndValuesToString null false] : ""}] \
      [expr {[string length $error] > 0 ? [$error ToString] : ""}]
} -cleanup {
  catch {file delete $fileName}

  unset -nocomplain fileName settings error code
} -constraints {eagle} -result \
{Ok {a 1 b False c 2 d 12 e(1) 3 e(2) 4 e(3,0) 5} {}}}

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

runTest {test interp-1.38 {"safe" load settings via script} -setup {
  unset -nocomplain fileName settings error code
} -body {
  set fileName [file join [getTemporaryPath] interp-1.38.eagle]

  writeFile $fileName {
    set a 1; set b [interp issafe]; set c 2; set d $a$c
    set e(1) 3; set e(2) 4; set e(3,0) 5
  }

  set settings null; set error null
  set code [object invoke -alias \
      Utility LoadSettingsViaScriptFile $fileName true settings error]

  list $code \
      [expr {[string length $settings] > 0 ? \
          [$settings KeysAndValuesToString null false] : ""}] \
      [expr {[string length $error] > 0 ? [$error ToString] : ""}]
} -cleanup {
  catch {file delete $fileName}

  unset -nocomplain fileName settings error code
} -constraints {eagle} -result \
{Ok {a 1 b True c 2 d 12 e(1) 3 e(2) 4 e(3,0) 5} {}}}

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

runTest {test interp-1.39 {try/finally without reset exit} -setup {
  set i [interp create]

  interp eval $i {
    catch {
      #
      # HACK: Just in case it is actually the default, force the necessary
      #       interpreter flags to be disabled for the created interpreter.
      #
      object invoke -flags +NonPublic Interpreter.GetActive interpreterFlags \
          [appendArgs [object invoke -flags +NonPublic Interpreter.GetActive \
          interpreterFlags] " -FinallyResetExit FinallyRestoreExit"]
    }
  }
} -body {
  catch {
    interp eval $i {
      catch {
        try {
          #
          # NOTE: This script exits.  Upon exiting, the value of "x"
          #       will be "1".
          #
          set x 1; exit; set x 3
        } finally {
          #
          # NOTE: This script block should never be evaluated.  Upon
          #       completion, the value of "x" should be "1".  If the
          #       value of "x" is "2" instead, that means this finally
          #       script block was evaluated.
          #
          incr x
        }
      }
      set x 5
    }
  }

  interp set $i x
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle tip285} -result {1}}

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

runTest {test interp-1.40 {try/finally with reset exit} -setup {
  set i [interp create]

  interp eval $i {
    catch {
      #
      # HACK: Just in case it is not actually the default, force the
      #       necessary interpreter flags to be enabled for the created
      #       interpreter.
      #
      object invoke -flags +NonPublic Interpreter.GetActive interpreterFlags \
          [appendArgs [object invoke -flags +NonPublic Interpreter.GetActive \
          interpreterFlags] " +FinallyResetExit FinallyRestoreExit"]
    }
  }
} -body {
  catch {
    interp eval $i {
      catch {
        try {
          #
          # NOTE: This script exits.  Upon exiting, the value of "x"
          #       will be "1".
          #
          set x 1; exit; set x 3
        } finally {
          #
          # NOTE: This script block should always be evaluated.  Upon
          #       completion, the value of "x" should be "2".  If the
          #       value of "x" is "1" instead, that means this finally
          #       script block was not evaluated.
          #
          incr x
        }
      }
      set x 5
    }
  }

  interp set $i x
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain i
} -constraints {eagle tip285} -result {2}}

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

runTest {test interp-1.41 {invoke hidden with -namespace} -setup {
  set i [interp create -safe]

  if {[isEagle]} then {
    interp eval $i {namespace enable true}
  }

  interp eval $i {namespace eval foo {}}
} -body {
  interp alias $i getNs $i namespace current; interp hide $i getNs
  list [catch {interp invokehidden $i -namespace foo getNs} error] $error
} -cleanup {
  catch {interp delete $i}

  unset -nocomplain error i
} -constraints {tip207 namespaces.available} -result {0 ::foo}}

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

runTest {test interp-1.42 {isolated [interp set]} -setup {
  set i(1) [interp create -isolated]
} -body {
  list [interp set $i(1) x 1] [interp set $i(1) x] \
      [interp eval $i(1) set x 2] [interp set $i(1) x]
} -cleanup {
  catch {interp delete $i(1)}

  unset -nocomplain i
} -constraints {eagle compile.APPDOMAINS compile.ISOLATED_INTERPRETERS} \
-result {1 1 2 2}}

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

source [file join [file normalize [file dirname [info script]]] epilogue.eagle]
