/*
 *  ticket-433.sli
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/* BeginDocumentation

Name: testsuite::ticket-433 - Test against receptor_type mishandling in ht_neuron

Synopsis: (ticket-433) run -> NEST exits if test fails

Description:
 ht_neuron accepted incoming connections with invalid receptor type, leading to
 unpredictable errors. Additionally, the topology library did not seem to pass
 on the receptor_type when making connections.
 
 Reported by Jonathan Williford.

Author: Hans Ekkehard Plesser, 2010-06-28 
 */

% This test should only run if we have GSL
statusdict/have_gsl :: not {statusdict/exitcodes/success :: quit_i} if

(unittest) run
/unittest using

M_INFO setverbosity

% the following the functions expect that the following variables
% are defined:
%  ampa --- AMPA receptor ID
%  retina, retina_gen, Tp --- layers

% Connection function using topology library
% receptor_type erroneously placed in dictionary
/connect_topo
{ 
  retina
  Tp
  << /sources << /model /RetinaNode >>
     /targets << /model /ThalamicNeuron >>
     /connection_type (divergent)
     /receptor_type ampa
     /synapse_model /ht_synapse
     /mask << /circular << /radius 2.0 >> >>
  >>
  ConnectLayers
}
bind def

% Connection function using topology library
% receptor_type correctly placed in synapse
/connect_topo_syn
{ 
  /ht_synapse /ht_syn_ampa << /receptor_type ampa >> CopyModel

  retina
  Tp
  << /sources << /model /RetinaNode >>
     /targets << /model /ThalamicNeuron >>
     /connection_type (divergent)
     /synapse_model /ht_syn_ampa
     /mask << /circular << /radius 2.0 >> >>
  >>
  ConnectLayers
}
bind def

% Connection function using explicit receptor type --- passes
/connect_explicit_rt
{
  [ retina_gen GetGlobalLeaves 100 Take 
    Tp GetGlobalLeaves 100 Take 
  ]
  { 
    << /receptor_type ampa >>
    Connect
    true
  }
  MapThread ;
} 
bind def

% Connection function not giving receptor type --- fails
/connect_implicit_rt
{
  [ retina_gen GetGlobalLeaves 100 Take 
    Tp GetGlobalLeaves 100 Take 
  ]
  {
    Connect
    true
  } 
  MapThread ;
} 
bind def

% Actual test function, expects connection function as input
/test_connect
{
  /connfun Set

  ResetKernel
  /ht_neuron /ThalamicNeuron CopyModel
  /poisson_generator /RetinaGen << /rate 10.0 >> CopyModel
  /parrot_neuron /RetinaNode CopyModel

  /ampa /ht_neuron GetDefaults /receptor_types get /AMPA get def
  
  /layerProps << /rows 10 /columns 10 /extent [8.0 8.0] >> def

  layerProps << /elements /RetinaGen >> join
  /retina_gen layerProps CreateLayer def

  layerProps << /elements /RetinaNode >> join
  /retina layerProps CreateLayer def

  layerProps << /elements /ThalamicNeuron >> join
  /Tp layerProps CreateLayer def

  [retina_gen GetGlobalLeaves retina GetGlobalLeaves] { Connect true } MapThread ;
  
  connfun load exec

  10. Simulate
}
bind def

% first test: explicit receptor type, should pass
{
  /connect_explicit_rt 
  test_connect
}
 pass_or_die

% second test: implicit receptor type
% this must raise an error, otherwise lacking receptor_type is not detected
{
  /connect_implicit_rt 
  test_connect
}
fail_or_die

% third test: connect using ConnectLayers
% this must raise an error, otherwise lacking receptor_type is not detected
{
  /connect_topo
  test_connect
}
fail_or_die

% fourth test: connect using ConnectLayers, but with 
% properly configured synapse model
{
  /connect_topo_syn
  test_connect
}
 pass_or_die

% fifth test: assert that all models that have /receptor_types reject
% connections with plain static_synapse
{
  ResetKernel % need clean modeldict
  modeldict keys 
  { 
    /mod Set 
    mod GetDefaults /dflts Set
    dflts /receptor_types known 
    { 
      % next condition avoids models with auto-generated
      % ports, such as iaf_cond_alpha_multisynapse
      % might be eliminated if #434 removes 0-port.
      dflts /receptor_types get cva length 0 geq
      {
        { ResetKernel 
          mod 2 Create ; 
          1 2 /static_synapse Connect 
        } fail_or_die 
      } if
    } if 
  } forall
}  pass_or_die % overall wrapper

endusing
