QtTest: header updated

QtTest: header updated

Modified:
source/QtTest/QTestEventListClass.prg

/*

  Qt4xHb - Bindings libraries for Harbour/xHarbour and Qt Framework 4

  Copyright (C) 2017 Marcos Antonio Gambeta <marcosgambeta AT outlook DOT com>

*/

#include "hbclass.ch"

CLASS QTestEventList

   DATA pointer
   DATA self_destruction INIT .F.

   METHOD new1
   METHOD new2
   METHOD new
   METHOD delete
   METHOD addDelay
   METHOD addKeyClick1
   METHOD addKeyClick2
   METHOD addKeyClick
   METHOD addKeyClicks
   METHOD addKeyPress1
   METHOD addKeyPress2
   METHOD addKeyPress
   METHOD addKeyRelease1
   METHOD addKeyRelease2
   METHOD addKeyRelease
   METHOD addMouseClick
   METHOD addMouseDClick
   METHOD addMouseMove
   METHOD addMousePress
   METHOD addMouseRelease
   METHOD clear
   METHOD simulate
   METHOD newFrom
   METHOD newFromObject
   METHOD newFromPointer
   METHOD selfDestruction
   METHOD setSelfDestruction
   DESTRUCTOR destroyObject

END CLASS

PROCEDURE destroyObject () CLASS QTestEventList
   IF ::self_destruction
      ::delete()
   ENDIF
RETURN

#pragma BEGINDUMP

#include <QTestEventList>

#include "qt4xhb_common.h"
#include "qt4xhb_macros.h"
#include "qt4xhb_utils.h"

/*
QTestEventList ()
*/
HB_FUNC_STATIC( QTESTEVENTLIST_NEW1 )
{
  QTestEventList * o = new QTestEventList ();
  _qt4xhb_storePointerAndFlag ( o, false );
}

/*
QTestEventList ( const QTestEventList & other )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_NEW2 )
{
  QTestEventList * o = new QTestEventList ( *PQTESTEVENTLIST(1) );
  _qt4xhb_storePointerAndFlag ( o, false );
}

//[1]QTestEventList ()
//[2]QTestEventList ( const QTestEventList & other )

HB_FUNC_STATIC( QTESTEVENTLIST_NEW )
{
  if( ISNUMPAR(0) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_NEW1 );
  }
  else if( ISNUMPAR(1) && ISQTESTEVENTLIST(1) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_NEW2 );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

HB_FUNC_STATIC( QTESTEVENTLIST_DELETE )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );

  if( obj )
  {
    delete obj;
    obj = NULL;
    PHB_ITEM self = hb_stackSelfItem();
    PHB_ITEM ptr = hb_itemPutPtr( NULL, NULL );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
  }

  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addDelay ( int msecs )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDDELAY )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->addDelay ( PINT(1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addKeyClick ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYCLICK1 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->addKeyClick ( (Qt::Key) hb_parni(1), ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addKeyClick ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYCLICK2 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    char par1 = ISCHAR(1)? (char) hb_parc(1)[0] : (ISNUM(1)? hb_parni(1) : 0);
    obj->addKeyClick ( par1, ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

//[1]void addKeyClick ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
//[2]void addKeyClick ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )

HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYCLICK )
{
  if( ISBETWEEN(1,3) && ISNUM(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYCLICK1 );
  }
  else if( ISBETWEEN(1,3) && ISCHAR(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYCLICK2 );
  }
}

/*
void addKeyClicks ( const QString & keys, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYCLICKS )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->addKeyClicks ( PQSTRING(1), ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addKeyPress ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYPRESS1 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->addKeyPress ( (Qt::Key) hb_parni(1), ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addKeyPress ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYPRESS2 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    char par1 = ISCHAR(1)? (char) hb_parc(1)[0] : (ISNUM(1)? hb_parni(1) : 0);
    obj->addKeyPress ( par1, ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

//[1]void addKeyPress ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
//[2]void addKeyPress ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )

HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYPRESS )
{
  if( ISBETWEEN(1,3) && ISNUM(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYPRESS1 );
  }
  else if( ISBETWEEN(1,3) && ISCHAR(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYPRESS2 );
  }
}

/*
void addKeyRelease ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYRELEASE1 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->addKeyRelease ( (Qt::Key) hb_parni(1), ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addKeyRelease ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYRELEASE2 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    char par1 = ISCHAR(1)? (char) hb_parc(1)[0] : (ISNUM(1)? hb_parni(1) : 0);
    obj->addKeyRelease ( par1, ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

//[1]void addKeyRelease ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
//[2]void addKeyRelease ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )

HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYRELEASE )
{
  if( ISBETWEEN(1,3) && ISNUM(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYRELEASE1 );
  }
  else if( ISBETWEEN(1,3) && ISCHAR(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYRELEASE2 );
  }
}

/*
void addMouseClick ( Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDMOUSECLICK )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  int par2 = ISNIL(2)? 0 :  hb_parni(2);
  if( obj )
  {
    QPoint par3 = ISNIL(3)? QPoint() : *(QPoint *) hb_itemGetPtr( hb_objSendMsg( hb_param(3, HB_IT_OBJECT ), "POINTER", 0 ) );
    obj->addMouseClick ( (Qt::MouseButton) hb_parni(1), (Qt::KeyboardModifiers) par2, par3, OPINT(4,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addMouseDClick ( Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDMOUSEDCLICK )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  int par2 = ISNIL(2)? 0 :  hb_parni(2);
  if( obj )
  {
    QPoint par3 = ISNIL(3)? QPoint() : *(QPoint *) hb_itemGetPtr( hb_objSendMsg( hb_param(3, HB_IT_OBJECT ), "POINTER", 0 ) );
    obj->addMouseDClick ( (Qt::MouseButton) hb_parni(1), (Qt::KeyboardModifiers) par2, par3, OPINT(4,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addMouseMove ( QPoint pos = QPoint(), int delay = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDMOUSEMOVE )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    QPoint par1 = ISNIL(1)? QPoint() : *(QPoint *) hb_itemGetPtr( hb_objSendMsg( hb_param(1, HB_IT_OBJECT ), "POINTER", 0 ) );
    obj->addMouseMove ( par1, OPINT(2,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addMousePress ( Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDMOUSEPRESS )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  int par2 = ISNIL(2)? 0 :  hb_parni(2);
  if( obj )
  {
    QPoint par3 = ISNIL(3)? QPoint() : *(QPoint *) hb_itemGetPtr( hb_objSendMsg( hb_param(3, HB_IT_OBJECT ), "POINTER", 0 ) );
    obj->addMousePress ( (Qt::MouseButton) hb_parni(1), (Qt::KeyboardModifiers) par2, par3, OPINT(4,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addMouseRelease ( Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDMOUSERELEASE )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  int par2 = ISNIL(2)? 0 :  hb_parni(2);
  if( obj )
  {
    QPoint par3 = ISNIL(3)? QPoint() : *(QPoint *) hb_itemGetPtr( hb_objSendMsg( hb_param(3, HB_IT_OBJECT ), "POINTER", 0 ) );
    obj->addMouseRelease ( (Qt::MouseButton) hb_parni(1), (Qt::KeyboardModifiers) par2, par3, OPINT(4,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void clear ()
*/
HB_FUNC_STATIC( QTESTEVENTLIST_CLEAR )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->clear ();
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void simulate ( QWidget * w )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_SIMULATE )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->simulate ( PQWIDGET(1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

HB_FUNC_STATIC( QTESTEVENTLIST_NEWFROM )
{
  PHB_ITEM self = hb_stackSelfItem();

  if( hb_pcount() == 1 && ISOBJECT(1) )
  {
    PHB_ITEM ptr = hb_itemPutPtr( NULL, (void *) hb_itemGetPtr( hb_objSendMsg( hb_param(1, HB_IT_OBJECT ), "POINTER", 0 ) ) );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
    PHB_ITEM des = hb_itemPutL( NULL, false );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }
  else if( hb_pcount() == 1 && ISPOINTER(1) )
  {
    PHB_ITEM ptr = hb_itemPutPtr( NULL, (void *) hb_itemGetPtr( hb_param(1, HB_IT_POINTER ) ) );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
    PHB_ITEM des = hb_itemPutL( NULL, false );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }

  hb_itemReturn( self );
}

HB_FUNC_STATIC( QTESTEVENTLIST_NEWFROMOBJECT )
{
  HB_FUNC_EXEC( QTESTEVENTLIST_NEWFROM );
}

HB_FUNC_STATIC( QTESTEVENTLIST_NEWFROMPOINTER )
{
  HB_FUNC_EXEC( QTESTEVENTLIST_NEWFROM );
}

HB_FUNC_STATIC( QTESTEVENTLIST_SELFDESTRUCTION )
{
  hb_retl( (bool) hb_itemGetL( hb_objSendMsg( hb_stackSelfItem(), "SELF_DESTRUCTION", 0 ) ) );
}

HB_FUNC_STATIC( QTESTEVENTLIST_SETSELFDESTRUCTION )
{
  PHB_ITEM self = hb_stackSelfItem();

  if( hb_pcount() == 1 && ISLOG(1) )
  {
    PHB_ITEM des = hb_itemPutL( NULL, hb_parl(1) );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }

  hb_itemReturn( self );
}

#pragma ENDDUMP

/*

  Qt4xHb - bibliotecas de ligao entre Harbour/xHarbour e Qt Framework 4

  Copyright (C) 2017 Marcos Antonio Gambeta <marcosgambeta AT outlook DOT com>

*/

#include "hbclass.ch"

CLASS QTestEventList

   DATA pointer
   DATA self_destruction INIT .F.

   METHOD new1
   METHOD new2
   METHOD new
   METHOD delete
   METHOD addDelay
   METHOD addKeyClick1
   METHOD addKeyClick2
   METHOD addKeyClick
   METHOD addKeyClicks
   METHOD addKeyPress1
   METHOD addKeyPress2
   METHOD addKeyPress
   METHOD addKeyRelease1
   METHOD addKeyRelease2
   METHOD addKeyRelease
   METHOD addMouseClick
   METHOD addMouseDClick
   METHOD addMouseMove
   METHOD addMousePress
   METHOD addMouseRelease
   METHOD clear
   METHOD simulate
   METHOD newFrom
   METHOD newFromObject
   METHOD newFromPointer
   METHOD selfDestruction
   METHOD setSelfDestruction
   DESTRUCTOR destroyObject

END CLASS

PROCEDURE destroyObject () CLASS QTestEventList
   IF ::self_destruction
      ::delete()
   ENDIF
RETURN

#pragma BEGINDUMP

#include <QTestEventList>

#include "qt4xhb_common.h"
#include "qt4xhb_macros.h"
#include "qt4xhb_utils.h"

/*
QTestEventList ()
*/
HB_FUNC_STATIC( QTESTEVENTLIST_NEW1 )
{
  QTestEventList * o = new QTestEventList ();
  _qt4xhb_storePointerAndFlag ( o, false );
}

/*
QTestEventList ( const QTestEventList & other )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_NEW2 )
{
  QTestEventList * o = new QTestEventList ( *PQTESTEVENTLIST(1) );
  _qt4xhb_storePointerAndFlag ( o, false );
}

//[1]QTestEventList ()
//[2]QTestEventList ( const QTestEventList & other )

HB_FUNC_STATIC( QTESTEVENTLIST_NEW )
{
  if( ISNUMPAR(0) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_NEW1 );
  }
  else if( ISNUMPAR(1) && ISQTESTEVENTLIST(1) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_NEW2 );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

HB_FUNC_STATIC( QTESTEVENTLIST_DELETE )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );

  if( obj )
  {
    delete obj;
    obj = NULL;
    PHB_ITEM self = hb_stackSelfItem();
    PHB_ITEM ptr = hb_itemPutPtr( NULL, NULL );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
  }

  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addDelay ( int msecs )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDDELAY )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->addDelay ( PINT(1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addKeyClick ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYCLICK1 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->addKeyClick ( (Qt::Key) hb_parni(1), ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addKeyClick ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYCLICK2 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    char par1 = ISCHAR(1)? (char) hb_parc(1)[0] : (ISNUM(1)? hb_parni(1) : 0);
    obj->addKeyClick ( par1, ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

//[1]void addKeyClick ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
//[2]void addKeyClick ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )

HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYCLICK )
{
  if( ISBETWEEN(1,3) && ISNUM(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYCLICK1 );
  }
  else if( ISBETWEEN(1,3) && ISCHAR(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYCLICK2 );
  }
}

/*
void addKeyClicks ( const QString & keys, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYCLICKS )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->addKeyClicks ( PQSTRING(1), ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addKeyPress ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYPRESS1 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->addKeyPress ( (Qt::Key) hb_parni(1), ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addKeyPress ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYPRESS2 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    char par1 = ISCHAR(1)? (char) hb_parc(1)[0] : (ISNUM(1)? hb_parni(1) : 0);
    obj->addKeyPress ( par1, ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

//[1]void addKeyPress ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
//[2]void addKeyPress ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )

HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYPRESS )
{
  if( ISBETWEEN(1,3) && ISNUM(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYPRESS1 );
  }
  else if( ISBETWEEN(1,3) && ISCHAR(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYPRESS2 );
  }
}

/*
void addKeyRelease ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYRELEASE1 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->addKeyRelease ( (Qt::Key) hb_parni(1), ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addKeyRelease ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYRELEASE2 )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    char par1 = ISCHAR(1)? (char) hb_parc(1)[0] : (ISNUM(1)? hb_parni(1) : 0);
    obj->addKeyRelease ( par1, ISNIL(2)? Qt::NoModifier : (Qt::KeyboardModifiers) hb_parni(2), OPINT(3,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

//[1]void addKeyRelease ( Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )
//[2]void addKeyRelease ( char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1 )

HB_FUNC_STATIC( QTESTEVENTLIST_ADDKEYRELEASE )
{
  if( ISBETWEEN(1,3) && ISNUM(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYRELEASE1 );
  }
  else if( ISBETWEEN(1,3) && ISCHAR(1) && (ISNUM(2)||ISNIL(2)) && (ISNUM(3)||ISNIL(3)) )
  {
    HB_FUNC_EXEC( QTESTEVENTLIST_ADDKEYRELEASE2 );
  }
}

/*
void addMouseClick ( Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDMOUSECLICK )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  int par2 = ISNIL(2)? 0 :  hb_parni(2);
  if( obj )
  {
    QPoint par3 = ISNIL(3)? QPoint() : *(QPoint *) hb_itemGetPtr( hb_objSendMsg( hb_param(3, HB_IT_OBJECT ), "POINTER", 0 ) );
    obj->addMouseClick ( (Qt::MouseButton) hb_parni(1), (Qt::KeyboardModifiers) par2, par3, OPINT(4,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addMouseDClick ( Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDMOUSEDCLICK )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  int par2 = ISNIL(2)? 0 :  hb_parni(2);
  if( obj )
  {
    QPoint par3 = ISNIL(3)? QPoint() : *(QPoint *) hb_itemGetPtr( hb_objSendMsg( hb_param(3, HB_IT_OBJECT ), "POINTER", 0 ) );
    obj->addMouseDClick ( (Qt::MouseButton) hb_parni(1), (Qt::KeyboardModifiers) par2, par3, OPINT(4,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addMouseMove ( QPoint pos = QPoint(), int delay = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDMOUSEMOVE )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    QPoint par1 = ISNIL(1)? QPoint() : *(QPoint *) hb_itemGetPtr( hb_objSendMsg( hb_param(1, HB_IT_OBJECT ), "POINTER", 0 ) );
    obj->addMouseMove ( par1, OPINT(2,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addMousePress ( Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDMOUSEPRESS )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  int par2 = ISNIL(2)? 0 :  hb_parni(2);
  if( obj )
  {
    QPoint par3 = ISNIL(3)? QPoint() : *(QPoint *) hb_itemGetPtr( hb_objSendMsg( hb_param(3, HB_IT_OBJECT ), "POINTER", 0 ) );
    obj->addMousePress ( (Qt::MouseButton) hb_parni(1), (Qt::KeyboardModifiers) par2, par3, OPINT(4,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void addMouseRelease ( Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay = -1 )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_ADDMOUSERELEASE )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  int par2 = ISNIL(2)? 0 :  hb_parni(2);
  if( obj )
  {
    QPoint par3 = ISNIL(3)? QPoint() : *(QPoint *) hb_itemGetPtr( hb_objSendMsg( hb_param(3, HB_IT_OBJECT ), "POINTER", 0 ) );
    obj->addMouseRelease ( (Qt::MouseButton) hb_parni(1), (Qt::KeyboardModifiers) par2, par3, OPINT(4,-1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void clear ()
*/
HB_FUNC_STATIC( QTESTEVENTLIST_CLEAR )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->clear ();
  }
  hb_itemReturn( hb_stackSelfItem() );
}

/*
void simulate ( QWidget * w )
*/
HB_FUNC_STATIC( QTESTEVENTLIST_SIMULATE )
{
  QTestEventList * obj = (QTestEventList *) hb_itemGetPtr( hb_objSendMsg( hb_stackSelfItem(), "POINTER", 0 ) );
  if( obj )
  {
    obj->simulate ( PQWIDGET(1) );
  }
  hb_itemReturn( hb_stackSelfItem() );
}

HB_FUNC_STATIC( QTESTEVENTLIST_NEWFROM )
{
  PHB_ITEM self = hb_stackSelfItem();

  if( hb_pcount() == 1 && ISOBJECT(1) )
  {
    PHB_ITEM ptr = hb_itemPutPtr( NULL, (void *) hb_itemGetPtr( hb_objSendMsg( hb_param(1, HB_IT_OBJECT ), "POINTER", 0 ) ) );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
    PHB_ITEM des = hb_itemPutL( NULL, false );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }
  else if( hb_pcount() == 1 && ISPOINTER(1) )
  {
    PHB_ITEM ptr = hb_itemPutPtr( NULL, (void *) hb_itemGetPtr( hb_param(1, HB_IT_POINTER ) ) );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
    PHB_ITEM des = hb_itemPutL( NULL, false );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }

  hb_itemReturn( self );
}

HB_FUNC_STATIC( QTESTEVENTLIST_NEWFROMOBJECT )
{
  HB_FUNC_EXEC( QTESTEVENTLIST_NEWFROM );
}

HB_FUNC_STATIC( QTESTEVENTLIST_NEWFROMPOINTER )
{
  HB_FUNC_EXEC( QTESTEVENTLIST_NEWFROM );
}

HB_FUNC_STATIC( QTESTEVENTLIST_SELFDESTRUCTION )
{
  hb_retl( (bool) hb_itemGetL( hb_objSendMsg( hb_stackSelfItem(), "SELF_DESTRUCTION", 0 ) ) );
}

HB_FUNC_STATIC( QTESTEVENTLIST_SETSELFDESTRUCTION )
{
  PHB_ITEM self = hb_stackSelfItem();

  if( hb_pcount() == 1 && ISLOG(1) )
  {
    PHB_ITEM des = hb_itemPutL( NULL, hb_parl(1) );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }

  hb_itemReturn( self );
}

#pragma ENDDUMP


Fixed a NoMethod Test

Fixed a NoMethod Test

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System
USING System.Collections.Generic
USING System.Linq
USING System.Text
USING XUnit

BEGIN NAMESPACE XSharp.VO.Tests

	CLASS OOPTests

		[Fact, Trait("Category", "OOP")];
		METHOD CreateInstanceTests() AS VOID
			LOCAL oObject AS OBJECT
            LOCAL oObject2 AS TestStrong
			/// note that vulcan does not return true for IsClassOf(#Tester, "Object")
			oObject := CreateInstance(#Tester)
			Assert.NotEqual(NULL_OBJECT, oObject)
			IVarPut(oObject,"Name", "X#")
			Assert.Equal("X#", IVarGet(oObject, "Name"))
			IVarPut(oObject,"Age",42)
			Assert.Equal(42, (INT) IVarGet(oObject, "Age"))
            oObject2 := CreateInstance(#TestStrong) // no parameters passed
            Assert.Equal(NULL, oObject2:Param)
            oObject2 := CreateInstance(#TestStrong, oObject, oObject) // too many parameters passed
            Assert.Equal(oObject, oObject2:Param)

		[Fact, Trait("Category", "OOP")];
		METHOD MetadataTests() AS VOID
			LOCAL oObject AS OBJECT
			LOCAL uValue AS USUAL
			Assert.Equal(TRUE, IsClass("tester"))
			Assert.Equal(TRUE, IsClass(#tester))
			Assert.Equal(TRUE, IsClassOf(#tester,#Father))
			oObject := CreateInstance(#Tester)
			// ClassName
			Assert.Equal("TESTER", ClassName(oObject))
			// IsMethod
			Assert.Equal(FALSE, IsMethod(oObject, "Doesnotexist"))
			Assert.Equal(TRUE, IsMethod(oObject, "GetHashCode"))
			// InstanceOf
			Assert.Equal(TRUE, IsInstanceOf(oObject, #Father))
			uValue := oObject
			Assert.Equal(TRUE, IsInstanceOfUsual(uValue, #Father))
			// IVarLIst
			VAR aVars := IVarList(oObject)
			Assert.Equal(3, (INT) ALen(aVars))
			// MethodList
			VAR aMethods := MethodList(oObject)
			Assert.Equal(7, (INT) ALen(aMethods))		// 4 METHODS of the OBJECT CLASS + TestMe + TestMe2
			// ClassTree
			VAR aTree := ClassTree(oObject)
			Assert.Equal(3, (INT) ALen(atree))		// TESTER, FATHER and OBJECT
			aTree := ClassTreeClass(#Tester)
			Assert.Equal(3, (INT) ALen(atree))		// TESTER, FATHER and OBJECT

			aTree := OOPTree(oObject)
			Assert.Equal(3, (INT) ALen(aTree))			// 3 classes, TESTER, FATHER and OBJECT
			Assert.Equal(3, (INT) ALen(aTree[1]))		// symbol, Ivars, Methods
			Assert.Equal(3, (INT) ALen(aTree[1][2]))	// 2 Ivars: Name & Age & FullName = same as IVarLIst
			Assert.Equal(9, (INT) ALen(aTree[1][3]))	// 9 Methods = same as MethodList + 2 non public methods
			aTree := OOPTreeClass(#tester)
			Assert.Equal(3, (INT) ALen(aTree))			// 3 classes, TESTER, FATHER and OBJECT
			Assert.Equal(3, (INT) ALen(aTree[1]))		// symbol, Ivars, Methods
			Assert.Equal(3, (INT) ALen(aTree[1][2]))	// 2 Ivars: Name & Age & FullName = same as IVarLIst
			Assert.Equal(9, (INT) ALen(aTree[1][3]))	// 9 Methods = same as MethodList + 2 non public methods

		[Fact, Trait("Category", "OOP")];
		METHOD SendTests() AS VOID
			LOCAL oObject AS OBJECT
			oObject := CreateInstance(#Tester)
			Assert.Equal(2121+1+2+3, (INT) Send(oObject, #TestMe,1,2,3))
			Assert.Equal(4242+1+2+3, (INT) Send(oObject, #TestMe2,1,2,3))
			Assert.Equal(6363+1+2+3, (INT) Send(oObject, #TestMe3,1.0,2,3))             // the float causes the USUAL overload to be called
			Assert.Equal(8484+1+2+3, (INT) __InternalSend(oObject, #TestMe3,1,2,3))     // all int so the int overload gets called

		[Fact, Trait("Category", "OOP")];
		METHOD NoMethodTests() AS VOID
			LOCAL oObject AS OBJECT
			oObject := CreateInstance("ClassWithNoMethod")
			assert.Equal(9, (INT) Send(oObject, "ADD",2,3,4))
			assert.Equal(24, (INT) Send(oObject, "MUL",2,3,4))
            //assert.Equal(24, (int) oObject:Mul(2,3,4))
			assert.Equal("DIV", (STRING) Send(oObject, "DIV",2,3,4))

		[Fact, Trait("Category", "OOP")];
		METHOD ParamCountTests() AS VOID
            VObject{}
			assert.Equal(3, (INT) FParamCount("STR"))
			assert.Equal(2, (INT) FParamCount("STR2"))
			assert.Equal(3, (INT) FParamCount("STR3"))
			assert.Equal(3, (INT) MParamCount(#Tester, #TestMe))
			assert.Equal(0, (INT) MParamCount("VObject", "Destroy"))
			//assert.Throws( FParamCount("ProcName"))	// ambiguous

		[Fact, Trait("Category", "OOP")];
		METHOD CallClipFuncTests() AS VOID
			SetDecimalSep( (WORD) '.')
			assert.Equal("10.00", (STRING) _CallClipFunc("STR", {10,5,2}))
			assert.Equal("   10.01", (STRING) _CallClipFunc("STR", {10.01,8,2}))
			assert.Equal("   10.02", (STRING) _CallClipFunc("STR3", {10.02,8,2}))
			assert.Equal("2.50", (STRING) _CallClipFunc("STR3", {2.49999,4,2}))

		[Fact, Trait("Category", "OOP")];
		METHOD ObjectToArrayTest() AS VOID
			LOCAL oObject AS OBJECT
			oObject := CreateInstance(#Tester)
			Assert.Equal(3, (INT) ALen(Object2Array(oObject)))

		[Fact, Trait("Category", "OOP")];
		METHOD LateBindingMethods() AS VOID
			LOCAL u AS USUAL
			u := AnotherClass{}
			u:TestOther()
			u:TestOtherC()
			Assert.Equal(1, (INT)u:TestOther1())
			Assert.Equal("22", (STRING)u:TestOther2(2))
			Assert.Equal("22", (STRING)u:TestOther2C(2))

			u := Tester{}
			Assert.Equal(2121+1+2+3, (INT)u:TestMe(1,2,3))
			Assert.Equal(4242+1+2+3, (INT)u:TestMe2(1,2,3))
            Assert.Equal(6363+1+2+3, (INT)u:TestMe3(1.0,2,3))   // overloaded. The float calls the USUAL variant
			Assert.Equal(8484+1+2+3, (INT)u:TestMe3(1,2,3))     // overloaded. all ints, so the INT variant is called
		RETURN

		[Fact, Trait("Category", "OOP")];
		METHOD LateBindingFields_Props() AS VOID
			LOCAL u AS USUAL
			u := Tester{}

			u:age := 42
			Assert.Equal(42, (INT)u:AGE)

			u:FULLNAME := "Olympiacos"
			Assert.Equal("Olympiacos", u:fullNAme)
		RETURN

        [Fact, Trait("Category", "OOP")];
        METHOD IConvertibleTest() AS VOID
           //Issue 1 - runtime - Object must implement IConvertible
            LOCAL o AS AzControl
            LOCAL x AS OBJECT

            o := AzControl{}
            o:cbWhen := {||TRUE}

            x := o

            x:cbWhen := {||TRUE}

            Assert.NotEqual(o:cbWhen, NULL)

		[Fact, Trait("Category", "OOP")];
		METHOD IsClassOf_Tests() AS VOID
			Assert.True(IsClassOf(#TestClassChild, #TestClassParent))
			Assert.False(IsClassOf(#TestClassParent, #TestClassChild))
			Assert.True(IsClassOf(#TestClassChild, #TestClassChild))
			Assert.True(IsClassOf(#TestClassParent, #TestClassParent))
			Assert.False(IsClassOf(#None, #None))
			Assert.False(IsClassOf(#None, #TestClassChild))
			Assert.False(IsClassOf(#TestClassChild, #None))

		[Fact, Trait("Category", "OOP")];
		METHOD IsInstanceOf_Tests() AS VOID
			Assert.True(IsInstanceOf(123 , "Int32"))
			Assert.True(IsInstanceOf(TRUE , "Boolean"))
			Assert.False(IsInstanceOf(123 , "Nothing"))

        [Fact, Trait("Category", "OOP")];
        METHOD IsAccessAssignMethod_tests() AS VOID
        	LOCAL o AS GeneralLBTestClass
        	o := GeneralLBTestClass{}
        	Assert.True( IsAccess(o , #acc_exp) )
        	Assert.True( IsAccess(o , #acc_prot) )
        	Assert.True( IsAccess(o , #acc_priv) )
        	Assert.True( IsAssign(o , #asn_exp) )
        	Assert.True( IsAssign(o , #asn_prot) )
        	Assert.True( IsAssign(o , #asn_priv) )

        	Assert.False( IsAssign(o , #acc_exp) )
        	Assert.False( IsAssign(o , #acc_prot) )
        	Assert.False( IsAccess(o , #asn_exp) )
        	Assert.False( IsAccess(o , #asn_prot) )

        	Assert.True( IsMethod(o , #meth_exp) )
        	Assert.True( IsMethod(o , #meth_prot) )
        	Assert.True( IsMethod(o , #meth_priv) )

        [Fact, Trait("Category", "OOP")];
        METHOD IVarPutGetSet_tests() AS VOID
        	LOCAL o AS GeneralLBTestClass
        	o := GeneralLBTestClass{}

        	IVarPut(o , #fld_exp , 1)
        	Assert.Equal(1 , (INT) IVarGet(o , #fld_exp))

        	IVarPutSelf(o , #fld_prot , 2)
        	Assert.Equal(2 , (INT) IVarGetSelf(o , #fld_prot))

        	Assert.ThrowsAny<Exception>( { => IVarPut(o , #fld_prot , 3) })
        	Assert.Equal(2 , (INT) IVarGetSelf(o , #fld_prot))

        	Assert.ThrowsAny<Exception>( { => IVarGet(o , #fld_prot) })
        	Assert.ThrowsAny<Exception>( { => IVarGet(o , #fld_priv) })

        [Fact, Trait("Category", "OOP")];
        METHOD PtrAndIntPtrMethodCalls() AS VOID
        	LOCAL o AS GeneralLBTestClass
        	o := GeneralLBTestClass{}

        	LOCAL pPtr AS PTR
        	LOCAL pIntPtr AS PTR
        	pPtr := o:MethodPtr()
        	? pPtr
        	pIntPtr := o:MethodPtr()
        	? pIntPtr
        	Assert.NotEqual((INT)pIntPtr , 0)
        	Assert.NotEqual((INT)pPtr , 0)

        	pPtr := o:MethodIntPtr()
        	? pPtr
        	pIntPtr := o:MethodIntPtr()
        	? pIntPtr
        	Assert.NotEqual((INT)pIntPtr , 0)
        	Assert.NotEqual((INT)pPtr , 0)

        	LOCAL u AS USUAL
        	u := o
        	pPtr := u:MethodPtr()
        	? pPtr
        	pIntPtr := u:MethodPtr()
        	? pIntPtr
        	Assert.NotEqual((INT)pIntPtr , 0)
        	Assert.NotEqual((INT)pPtr , 0)

        	pPtr := u:MethodIntPtr()
        	? pPtr
        	pIntPtr := u:MethodIntPtr()
        	? pIntPtr
        	Assert.NotEqual((INT)pIntPtr , 0)
        	Assert.NotEqual((INT)pPtr , 0)

        [Fact, Trait("Category", "OOP")];
        METHOD DefaultParameters() AS VOID
        	LOCAL o AS USUAL
        	o := AnotherClass{}

        	Assert.Equal(60 , (INT)o:DefaultParams1(10,20,30))
        	Assert.Equal(15 , (INT)o:DefaultParams1(10))
//        	Assert.Equal(32 , (INT)o:DefaultParams1(10,,20)) // doesn't work in VO either
        	Assert.Equal(113 , (INT)o:DefaultParams1(10,100))

        	Assert.Equal("TESTFALSE" , (STRING)o:DefaultParams2("TEST",FALSE))
        	Assert.Equal("TESTTRUE" , (STRING)o:DefaultParams2("TEST",TRUE))
        	Assert.Equal("TESTTRUE" , (STRING)o:DefaultParams2("TEST"))
        	Assert.Equal("abcTRUE" , (STRING)o:DefaultParams2())

        	Assert.Equal(2000.10.20 , (DATE)o:DefaultParams3(2000.10.20))
        	Assert.Equal(2018.10.20 , (DATE)o:DefaultParams3())

        	Assert.Equal(#MYSYMBOL , (SYMBOL)o:DefaultParams4())

		// TECH-6YD882O372, Runtime exception with inexact equals operator and NIL values
		[Fact, Trait("Category", "StringUsual")];
		METHOD StringNil_InExactEquals() AS VOID
			LOCAL u := NIL AS USUAL
			LOCAL c := "abc" AS STRING
			Assert.False( c == u ) // FALSE, ok
			Assert.False( c = u  ) // FALSE, ok
			Assert.False( u == c ) // FALSE, ok
			Assert.False( u = c  ) // exception
			Assert.False( u = "aaa"  ) // exception

			// exceptions also:
			u := NilTestClass{}
			Assert.False( u:NilMethod() = "A" )
			Assert.False( u:NilMethodUntyped() = "A" )
			Assert.False( u:NilAccess = "A" )
		RETURN

		// TECH-CLZ71NH0C3, Runtime exception with return value of NoIVarGet()
        [Fact, Trait("Category", "OOP")];
        METHOD NilComparisons() AS VOID
			LOCAL n := 0 AS INT
			LOCAL c := "abc" AS STRING
			LOCAL uNil := NIL AS USUAL
			Assert.False( n == NIL  )
			Assert.False( n == uNil )
			Assert.False( NIL == n  )
			Assert.False( uNil == n )

			Assert.False( uNil == c )
			Assert.False( c == NIL  )
			Assert.False( c == uNil )

			LOCAL o AS USUAL
			o := NilTestClass{}
			Assert.False( o:NilMethod() == 1        )
			Assert.False( o:NilMethodUntyped() == 1 )
			Assert.False( o:NilMethodUntyped() == c )

			Assert.False( o:NilAccess == 1  )
			Assert.False( o:NilAccess == "" )

			Assert.False( o:DoesNotExistAccess == 1   )
			Assert.False( o:DoesNotExistMethod() == 1 )

			Assert.False( o:DoesNotExistMethodNil() == 1     )
			Assert.False( o:DoesNotExistMethodNil() == "abc" )

			Assert.False( o:DoesNotExistAccessNil == 1     ) // exception here "Value does not fall within the expected range."
			Assert.False( o:DoesNotExistAccessNil == "abc" ) // exception
			Assert.False( o:DoesNotExistAccessNil == n     ) // exception

			Assert.True( o:DoesNotExistAccessNil == NIL   ) // OK, TRUE
		RETURN

		// TECH-R2TJNB9TZ7, Problem with late bound assign of DateTime to DATE iVar
		[Fact, Trait("Category", "Date")];
		METHOD Date_to_DateTime_and_back() AS VOID

			LOCAL o := DateClass{} AS DateClass

			o:FieldDate := DateTime{2000,12,1}
			Assert.Equal(o:FieldDate , ConDate(2000,12,1))
			o:AssignDate := DateTime{2000,12,2}
			Assert.Equal(o:FieldDate , ConDate(2000,12,2))
			Assert.Equal(o:AccessDate , ConDate(2000,12,2))

			o:FieldDateTime := ConDate(2020,12,1)
			Assert.Equal(o:FieldDateTime , DateTime{2020,12,1})
			o:AssignDateTime := ConDate(2020,12,2)
			Assert.Equal(o:FieldDateTime , DateTime{2020,12,2})
			Assert.Equal(o:AccessDateTime , DateTime{2020,12,2})

			o:Date_DateTime_Method(DateTime{2010,1,1} , 2012.02.02)
			Assert.Equal(o:FieldDate , 2010.01.01)
			Assert.Equal(o:FieldDateTime , DateTime{2012,02,02})

			LOCAL u := DateClass{} AS USUAL

			u:FieldDate := DateTime{2000,12,1}
			Assert.Equal((DATE)u:FieldDate , ConDate(2000,12,1))
			u:AssignDate := DateTime{2000,12,2}
			Assert.Equal((DATE)u:FieldDate , ConDate(2000,12,2))
			Assert.Equal((DATE)u:AccessDate , ConDate(2000,12,2))

			u:FieldDateTime := ConDate(2020,12,1)
			Assert.Equal((DateTime)u:FieldDateTime , DateTime{2020,12,1})
			u:AssignDateTime := ConDate(2020,12,2)
			Assert.Equal((DateTime)u:FieldDateTime , DateTime{2020,12,2})
			Assert.Equal((DateTime)u:AccessDateTime , DateTime{2020,12,2})

			u:Date_DateTime_Method(DateTime{2010,1,1} , 2012.02.02)
			Assert.Equal((DATE) u:FieldDate , 2010.01.01)
			Assert.Equal((DateTime) u:FieldDateTime , DateTime{2012,02,02})
		RETURN

		CLASS DateClass
			EXPORT FieldDate := Today() AS DATE
			EXPORT FieldDateTime := DateTime.Now AS DateTime
			ASSIGN AssignDate(val AS DATE)
				SELF:FieldDate := val
			ASSIGN AssignDateTime(val AS DateTime)
				SELF:FieldDateTime := val
			ACCESS AccessDate AS DATE
				RETURN SELF:FieldDate
			ACCESS AccessDateTime AS DateTime
				RETURN SELF:FieldDateTime
			METHOD Date_DateTime_Method(d AS DATE, dt AS DateTime) AS VOID
				SELF:FieldDate := d
				SELF:FieldDateTime := dt
			RETURN
		END CLASS

        CLASS AzControl

            EXPORT cbWhen AS CODEBLOCK

        END CLASS
	END CLASS

END NAMESPACE

CLASS Tester INHERIT father
	PROPERTY name AS STRING AUTO
	PROPERTY age AS INT AUTO
	EXPORT fullname AS STRING
CONSTRUCTOR CLIPPER
	METHOD TestMe(a AS INT,b AS INT,c AS INT) AS LONG
		RETURN 2121+a+b+c
	METHOD TestMe2(a,b, c ) AS LONG
		RETURN 4242+a+b+c
	METHOD TestMe3(a AS USUAL,b AS USUAL, c AS USUAL) AS LONG
		RETURN 6363+a+b+c
	METHOD TestMe3(a AS INT,b AS INT, c AS INT) AS LONG
        RETURN 8484+a+b+c

END CLASS

CLASS Father
END CLASS

CLASS ClassWithNoMethod
	METHOD NoMethod(arg1, arg2, arg3)
        VAR cMethodName := NoMethod()
		IF cMethodName = "ADD"
			RETURN arg1+arg2+arg3
		ELSEIF cMethodName = "MUL"
			RETURN arg1*arg2*arg3
		ENDIF
		RETURN cMethodName
END CLASS

CLASS AnotherClass
	METHOD TestOther() AS VOID PASCAL
	METHOD TestOtherC() CLIPPER
	RETURN NIL
	METHOD TestOther1() AS INT
	RETURN 1
	METHOD TestOther2(n AS INT) AS STRING
	RETURN "2" + n:ToString()
	METHOD TestOther2C(n)
	RETURN "2" + ((INT)n):ToString()

	METHOD DefaultParams1(n1 AS INT, n2 := 2 AS INT, n3 := 3 AS INT) AS INT
	RETURN n1 + n2 + n3
	METHOD DefaultParams2(cValue := "abc" AS STRING, lValue := TRUE AS LOGIC) AS STRING
	RETURN cValue + lValue:ToString():ToUpper()
	METHOD DefaultParams3(dValue := 2018.10.20 AS DATE) AS DATE
	RETURN dValue
	METHOD DefaultParams4(sValue := #MYSYMBOL AS SYMBOL) AS SYMBOL
	RETURN sValue

END CLASS

CLASS GeneralLBTestClass
	EXPORT fld_exp AS INT
	PROTECT fld_prot AS INT
	PRIVATE fld_priv AS INT
	INSTANCE fld_inst AS INT
	ACCESS acc_exp AS INT
	RETURN 0
	PROTECT ACCESS acc_prot AS INT
	RETURN 0
	PRIVATE ACCESS acc_priv AS INT
	RETURN 0

	ASSIGN asn_exp(n AS INT)
	PROTECT ASSIGN asn_prot(n AS INT)
	PROTECT ASSIGN asn_priv(n AS INT)

	METHOD meth_exp(a,b,c,d)
	RETURN NIL
	PROTECTED METHOD meth_prot(a,b,c,d)
	RETURN NIL
	PRIVATE METHOD meth_priv(a,b,c,d)
	RETURN NIL

	METHOD MethodPtr() AS PTR
		LOCAL n AS INT
	RETURN @n
	METHOD MethodIntPtr() AS IntPtr
		LOCAL n AS INT
	RETURN @n
END CLASS

CLASS TestClassParent
END CLASS
CLASS TestClassChild INHERIT TestClassParent
END CLASS

CLASS TestStrong
    PROPERTY Param AS OBJECT AUTO
    CONSTRUCTOR(otest AS OBJECT)
        SELF:Param := oTest
END CLASS

CLASS NilTestClass
	METHOD NilMethod() AS USUAL
	RETURN NIL
	METHOD NilMethodUntyped()
	RETURN NIL
	ACCESS NilAccess
	RETURN NIL

	METHOD NoMethod(c)
		? c
		IF AsString(c) == "DOESNOTEXISTMETHOD"
			RETURN 2
		END IF
	RETURN NIL
	METHOD NoIVarGet(c)
		? c
		IF AsString(c) == "DOESNOTEXISTACCESS"
			RETURN 2
		END IF
	RETURN NIL
END CLASS

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System
USING System.Collections.Generic
USING System.Linq
USING System.Text
USING XUnit

BEGIN NAMESPACE XSharp.VO.Tests

	CLASS OOPTests

		[Fact, Trait("Category", "OOP")];
		METHOD CreateInstanceTests() AS VOID
			LOCAL oObject AS OBJECT
            LOCAL oObject2 AS TestStrong
			/// note that vulcan does not return true for IsClassOf(#Tester, "Object")
			oObject := CreateInstance(#Tester)
			Assert.NotEqual(NULL_OBJECT, oObject)
			IVarPut(oObject,"Name", "X#")
			Assert.Equal("X#", IVarGet(oObject, "Name"))
			IVarPut(oObject,"Age",42)
			Assert.Equal(42, (INT) IVarGet(oObject, "Age"))
            oObject2 := CreateInstance(#TestStrong) // no parameters passed
            Assert.Equal(NULL, oObject2:Param)
            oObject2 := CreateInstance(#TestStrong, oObject, oObject) // too many parameters passed
            Assert.Equal(oObject, oObject2:Param)

		[Fact, Trait("Category", "OOP")];
		METHOD MetadataTests() AS VOID
			LOCAL oObject AS OBJECT
			LOCAL uValue AS USUAL
			Assert.Equal(TRUE, IsClass("tester"))
			Assert.Equal(TRUE, IsClass(#tester))
			Assert.Equal(TRUE, IsClassOf(#tester,#Father))
			oObject := CreateInstance(#Tester)
			// ClassName
			Assert.Equal("TESTER", ClassName(oObject))
			// IsMethod
			Assert.Equal(FALSE, IsMethod(oObject, "Doesnotexist"))
			Assert.Equal(TRUE, IsMethod(oObject, "GetHashCode"))
			// InstanceOf
			Assert.Equal(TRUE, IsInstanceOf(oObject, #Father))
			uValue := oObject
			Assert.Equal(TRUE, IsInstanceOfUsual(uValue, #Father))
			// IVarLIst
			VAR aVars := IVarList(oObject)
			Assert.Equal(3, (INT) ALen(aVars))
			// MethodList
			VAR aMethods := MethodList(oObject)
			Assert.Equal(7, (INT) ALen(aMethods))		// 4 METHODS of the OBJECT CLASS + TestMe + TestMe2
			// ClassTree
			VAR aTree := ClassTree(oObject)
			Assert.Equal(3, (INT) ALen(atree))		// TESTER, FATHER and OBJECT
			aTree := ClassTreeClass(#Tester)
			Assert.Equal(3, (INT) ALen(atree))		// TESTER, FATHER and OBJECT

			aTree := OOPTree(oObject)
			Assert.Equal(3, (INT) ALen(aTree))			// 3 classes, TESTER, FATHER and OBJECT
			Assert.Equal(3, (INT) ALen(aTree[1]))		// symbol, Ivars, Methods
			Assert.Equal(3, (INT) ALen(aTree[1][2]))	// 2 Ivars: Name & Age & FullName = same as IVarLIst
			Assert.Equal(9, (INT) ALen(aTree[1][3]))	// 9 Methods = same as MethodList + 2 non public methods
			aTree := OOPTreeClass(#tester)
			Assert.Equal(3, (INT) ALen(aTree))			// 3 classes, TESTER, FATHER and OBJECT
			Assert.Equal(3, (INT) ALen(aTree[1]))		// symbol, Ivars, Methods
			Assert.Equal(3, (INT) ALen(aTree[1][2]))	// 2 Ivars: Name & Age & FullName = same as IVarLIst
			Assert.Equal(9, (INT) ALen(aTree[1][3]))	// 9 Methods = same as MethodList + 2 non public methods

		[Fact, Trait("Category", "OOP")];
		METHOD SendTests() AS VOID
			LOCAL oObject AS OBJECT
			oObject := CreateInstance(#Tester)
			Assert.Equal(2121+1+2+3, (INT) Send(oObject, #TestMe,1,2,3))
			Assert.Equal(4242+1+2+3, (INT) Send(oObject, #TestMe2,1,2,3))
			Assert.Equal(6363+1+2+3, (INT) Send(oObject, #TestMe3,1.0,2,3))             // the float causes the USUAL overload to be called
			Assert.Equal(8484+1+2+3, (INT) __InternalSend(oObject, #TestMe3,1,2,3))     // all int so the int overload gets called

		[Fact, Trait("Category", "OOP")];
		METHOD NoMethodTests() AS VOID
			LOCAL oObject AS OBJECT
			oObject := CreateInstance("ClassWithNoMethod")
			assert.Equal(9, (INT) Send(oObject, "ADD",2,3,4))
			assert.Equal(24, (INT) Send(oObject, "MUL",2,3,4))
            //assert.Equal(24, (int) oObject:Mul(2,3,4))
			assert.Equal("DIV", (STRING) Send(oObject, "DIV",2,3,4))

		[Fact, Trait("Category", "OOP")];
		METHOD ParamCountTests() AS VOID
            VObject{}
			assert.Equal(3, (INT) FParamCount("STR"))
			assert.Equal(2, (INT) FParamCount("STR2"))
			assert.Equal(3, (INT) FParamCount("STR3"))
			assert.Equal(3, (INT) MParamCount(#Tester, #TestMe))
			assert.Equal(0, (INT) MParamCount("VObject", "Destroy"))
			//assert.Throws( FParamCount("ProcName"))	// ambiguous

		[Fact, Trait("Category", "OOP")];
		METHOD CallClipFuncTests() AS VOID
			SetDecimalSep( (WORD) '.')
			assert.Equal("10.00", (STRING) _CallClipFunc("STR", {10,5,2}))
			assert.Equal("   10.01", (STRING) _CallClipFunc("STR", {10.01,8,2}))
			assert.Equal("   10.02", (STRING) _CallClipFunc("STR3", {10.02,8,2}))
			assert.Equal("2.50", (STRING) _CallClipFunc("STR3", {2.49999,4,2}))

		[Fact, Trait("Category", "OOP")];
		METHOD ObjectToArrayTest() AS VOID
			LOCAL oObject AS OBJECT
			oObject := CreateInstance(#Tester)
			Assert.Equal(3, (INT) ALen(Object2Array(oObject)))

		[Fact, Trait("Category", "OOP")];
		METHOD LateBindingMethods() AS VOID
			LOCAL u AS USUAL
			u := AnotherClass{}
			u:TestOther()
			u:TestOtherC()
			Assert.Equal(1, (INT)u:TestOther1())
			Assert.Equal("22", (STRING)u:TestOther2(2))
			Assert.Equal("22", (STRING)u:TestOther2C(2))

			u := Tester{}
			Assert.Equal(2121+1+2+3, (INT)u:TestMe(1,2,3))
			Assert.Equal(4242+1+2+3, (INT)u:TestMe2(1,2,3))
            Assert.Equal(6363+1+2+3, (INT)u:TestMe3(1.0,2,3))   // overloaded. The float calls the USUAL variant
			Assert.Equal(8484+1+2+3, (INT)u:TestMe3(1,2,3))     // overloaded. all ints, so the INT variant is called
		RETURN

		[Fact, Trait("Category", "OOP")];
		METHOD LateBindingFields_Props() AS VOID
			LOCAL u AS USUAL
			u := Tester{}

			u:age := 42
			Assert.Equal(42, (INT)u:AGE)

			u:FULLNAME := "Olympiacos"
			Assert.Equal("Olympiacos", u:fullNAme)
		RETURN

        [Fact, Trait("Category", "OOP")];
        METHOD IConvertibleTest() AS VOID
           //Issue 1 - runtime - Object must implement IConvertible
            LOCAL o AS AzControl
            LOCAL x AS OBJECT

            o := AzControl{}
            o:cbWhen := {||TRUE}

            x := o

            x:cbWhen := {||TRUE}

            Assert.NotEqual(o:cbWhen, NULL)

		[Fact, Trait("Category", "OOP")];
		METHOD IsClassOf_Tests() AS VOID
			Assert.True(IsClassOf(#TestClassChild, #TestClassParent))
			Assert.False(IsClassOf(#TestClassParent, #TestClassChild))
			Assert.True(IsClassOf(#TestClassChild, #TestClassChild))
			Assert.True(IsClassOf(#TestClassParent, #TestClassParent))
			Assert.False(IsClassOf(#None, #None))
			Assert.False(IsClassOf(#None, #TestClassChild))
			Assert.False(IsClassOf(#TestClassChild, #None))

		[Fact, Trait("Category", "OOP")];
		METHOD IsInstanceOf_Tests() AS VOID
			Assert.True(IsInstanceOf(123 , "Int32"))
			Assert.True(IsInstanceOf(TRUE , "Boolean"))
			Assert.False(IsInstanceOf(123 , "Nothing"))

        [Fact, Trait("Category", "OOP")];
        METHOD IsAccessAssignMethod_tests() AS VOID
        	LOCAL o AS GeneralLBTestClass
        	o := GeneralLBTestClass{}
        	Assert.True( IsAccess(o , #acc_exp) )
        	Assert.True( IsAccess(o , #acc_prot) )
        	Assert.True( IsAccess(o , #acc_priv) )
        	Assert.True( IsAssign(o , #asn_exp) )
        	Assert.True( IsAssign(o , #asn_prot) )
        	Assert.True( IsAssign(o , #asn_priv) )

        	Assert.False( IsAssign(o , #acc_exp) )
        	Assert.False( IsAssign(o , #acc_prot) )
        	Assert.False( IsAccess(o , #asn_exp) )
        	Assert.False( IsAccess(o , #asn_prot) )

        	Assert.True( IsMethod(o , #meth_exp) )
        	Assert.True( IsMethod(o , #meth_prot) )
        	Assert.True( IsMethod(o , #meth_priv) )

        [Fact, Trait("Category", "OOP")];
        METHOD IVarPutGetSet_tests() AS VOID
        	LOCAL o AS GeneralLBTestClass
        	o := GeneralLBTestClass{}

        	IVarPut(o , #fld_exp , 1)
        	Assert.Equal(1 , (INT) IVarGet(o , #fld_exp))

        	IVarPutSelf(o , #fld_prot , 2)
        	Assert.Equal(2 , (INT) IVarGetSelf(o , #fld_prot))

        	Assert.ThrowsAny<Exception>( { => IVarPut(o , #fld_prot , 3) })
        	Assert.Equal(2 , (INT) IVarGetSelf(o , #fld_prot))

        	Assert.ThrowsAny<Exception>( { => IVarGet(o , #fld_prot) })
        	Assert.ThrowsAny<Exception>( { => IVarGet(o , #fld_priv) })

        [Fact, Trait("Category", "OOP")];
        METHOD PtrAndIntPtrMethodCalls() AS VOID
        	LOCAL o AS GeneralLBTestClass
        	o := GeneralLBTestClass{}

        	LOCAL pPtr AS PTR
        	LOCAL pIntPtr AS PTR
        	pPtr := o:MethodPtr()
        	? pPtr
        	pIntPtr := o:MethodPtr()
        	? pIntPtr
        	Assert.NotEqual((INT)pIntPtr , 0)
        	Assert.NotEqual((INT)pPtr , 0)

        	pPtr := o:MethodIntPtr()
        	? pPtr
        	pIntPtr := o:MethodIntPtr()
        	? pIntPtr
        	Assert.NotEqual((INT)pIntPtr , 0)
        	Assert.NotEqual((INT)pPtr , 0)

        	LOCAL u AS USUAL
        	u := o
        	pPtr := u:MethodPtr()
        	? pPtr
        	pIntPtr := u:MethodPtr()
        	? pIntPtr
        	Assert.NotEqual((INT)pIntPtr , 0)
        	Assert.NotEqual((INT)pPtr , 0)

        	pPtr := u:MethodIntPtr()
        	? pPtr
        	pIntPtr := u:MethodIntPtr()
        	? pIntPtr
        	Assert.NotEqual((INT)pIntPtr , 0)
        	Assert.NotEqual((INT)pPtr , 0)

        [Fact, Trait("Category", "OOP")];
        METHOD DefaultParameters() AS VOID
        	LOCAL o AS USUAL
        	o := AnotherClass{}

        	Assert.Equal(60 , (INT)o:DefaultParams1(10,20,30))
        	Assert.Equal(15 , (INT)o:DefaultParams1(10))
//        	Assert.Equal(32 , (INT)o:DefaultParams1(10,,20)) // doesn't work in VO either
        	Assert.Equal(113 , (INT)o:DefaultParams1(10,100))

        	Assert.Equal("TESTFALSE" , (STRING)o:DefaultParams2("TEST",FALSE))
        	Assert.Equal("TESTTRUE" , (STRING)o:DefaultParams2("TEST",TRUE))
        	Assert.Equal("TESTTRUE" , (STRING)o:DefaultParams2("TEST"))
        	Assert.Equal("abcTRUE" , (STRING)o:DefaultParams2())

        	Assert.Equal(2000.10.20 , (DATE)o:DefaultParams3(2000.10.20))
        	Assert.Equal(2018.10.20 , (DATE)o:DefaultParams3())

        	Assert.Equal(#MYSYMBOL , (SYMBOL)o:DefaultParams4())

		// TECH-6YD882O372, Runtime exception with inexact equals operator and NIL values
		[Fact, Trait("Category", "StringUsual")];
		METHOD StringNil_InExactEquals() AS VOID
			LOCAL u := NIL AS USUAL
			LOCAL c := "abc" AS STRING
			Assert.False( c == u ) // FALSE, ok
			Assert.False( c = u  ) // FALSE, ok
			Assert.False( u == c ) // FALSE, ok
			Assert.False( u = c  ) // exception
			Assert.False( u = "aaa"  ) // exception

			// exceptions also:
			u := NilTestClass{}
			Assert.False( u:NilMethod() = "A" )
			Assert.False( u:NilMethodUntyped() = "A" )
			Assert.False( u:NilAccess = "A" )
		RETURN

		// TECH-CLZ71NH0C3, Runtime exception with return value of NoIVarGet()
        [Fact, Trait("Category", "OOP")];
        METHOD NilComparisons() AS VOID
			LOCAL n := 0 AS INT
			LOCAL c := "abc" AS STRING
			LOCAL uNil := NIL AS USUAL
			Assert.False( n == NIL  )
			Assert.False( n == uNil )
			Assert.False( NIL == n  )
			Assert.False( uNil == n )

			Assert.False( uNil == c )
			Assert.False( c == NIL  )
			Assert.False( c == uNil )

			LOCAL o AS USUAL
			o := NilTestClass{}
			Assert.False( o:NilMethod() == 1        )
			Assert.False( o:NilMethodUntyped() == 1 )
			Assert.False( o:NilMethodUntyped() == c )

			Assert.False( o:NilAccess == 1  )
			Assert.False( o:NilAccess == "" )

			Assert.False( o:DoesNotExistAccess == 1   )
			Assert.False( o:DoesNotExistMethod() == 1 )

			Assert.False( o:DoesNotExistMethodNil() == 1     )
			Assert.False( o:DoesNotExistMethodNil() == "abc" )

			Assert.False( o:DoesNotExistAccessNil == 1     ) // exception here "Value does not fall within the expected range."
			Assert.False( o:DoesNotExistAccessNil == "abc" ) // exception
			Assert.False( o:DoesNotExistAccessNil == n     ) // exception

			Assert.True( o:DoesNotExistAccessNil == NIL   ) // OK, TRUE
		RETURN

		// TECH-R2TJNB9TZ7, Problem with late bound assign of DateTime to DATE iVar
		[Fact, Trait("Category", "Date")];
		METHOD Date_to_DateTime_and_back() AS VOID

			LOCAL o := DateClass{} AS DateClass

			o:FieldDate := DateTime{2000,12,1}
			Assert.Equal(o:FieldDate , ConDate(2000,12,1))
			o:AssignDate := DateTime{2000,12,2}
			Assert.Equal(o:FieldDate , ConDate(2000,12,2))
			Assert.Equal(o:AccessDate , ConDate(2000,12,2))

			o:FieldDateTime := ConDate(2020,12,1)
			Assert.Equal(o:FieldDateTime , DateTime{2020,12,1})
			o:AssignDateTime := ConDate(2020,12,2)
			Assert.Equal(o:FieldDateTime , DateTime{2020,12,2})
			Assert.Equal(o:AccessDateTime , DateTime{2020,12,2})

			o:Date_DateTime_Method(DateTime{2010,1,1} , 2012.02.02)
			Assert.Equal(o:FieldDate , 2010.01.01)
			Assert.Equal(o:FieldDateTime , DateTime{2012,02,02})

			LOCAL u := DateClass{} AS USUAL

			u:FieldDate := DateTime{2000,12,1}
			Assert.Equal((DATE)u:FieldDate , ConDate(2000,12,1))
			u:AssignDate := DateTime{2000,12,2}
			Assert.Equal((DATE)u:FieldDate , ConDate(2000,12,2))
			Assert.Equal((DATE)u:AccessDate , ConDate(2000,12,2))

			u:FieldDateTime := ConDate(2020,12,1)
			Assert.Equal((DateTime)u:FieldDateTime , DateTime{2020,12,1})
			u:AssignDateTime := ConDate(2020,12,2)
			Assert.Equal((DateTime)u:FieldDateTime , DateTime{2020,12,2})
			Assert.Equal((DateTime)u:AccessDateTime , DateTime{2020,12,2})

			u:Date_DateTime_Method(DateTime{2010,1,1} , 2012.02.02)
			Assert.Equal((DATE) u:FieldDate , 2010.01.01)
			Assert.Equal((DateTime) u:FieldDateTime , DateTime{2012,02,02})
		RETURN

		CLASS DateClass
			EXPORT FieldDate := Today() AS DATE
			EXPORT FieldDateTime := DateTime.Now AS DateTime
			ASSIGN AssignDate(val AS DATE)
				SELF:FieldDate := val
			ASSIGN AssignDateTime(val AS DateTime)
				SELF:FieldDateTime := val
			ACCESS AccessDate AS DATE
				RETURN SELF:FieldDate
			ACCESS AccessDateTime AS DateTime
				RETURN SELF:FieldDateTime
			METHOD Date_DateTime_Method(d AS DATE, dt AS DateTime) AS VOID
				SELF:FieldDate := d
				SELF:FieldDateTime := dt
			RETURN
		END CLASS

        CLASS AzControl

            EXPORT cbWhen AS CODEBLOCK

        END CLASS
	END CLASS

END NAMESPACE

CLASS Tester INHERIT father
	PROPERTY name AS STRING AUTO
	PROPERTY age AS INT AUTO
	EXPORT fullname AS STRING
CONSTRUCTOR CLIPPER
	METHOD TestMe(a AS INT,b AS INT,c AS INT) AS LONG
		RETURN 2121+a+b+c
	METHOD TestMe2(a,b, c ) AS LONG
		RETURN 4242+a+b+c
	METHOD TestMe3(a AS USUAL,b AS USUAL, c AS USUAL) AS LONG
		RETURN 6363+a+b+c
	METHOD TestMe3(a AS INT,b AS INT, c AS INT) AS LONG
        RETURN 8484+a+b+c

END CLASS

CLASS Father
END CLASS

CLASS ClassWithNoMethod
	METHOD NoMethod(cMethodName, arg1, arg2, arg3)
		IF cMethodName = "ADD"
			RETURN arg1+arg2+arg3
		ELSEIF cMethodName = "MUL"
			RETURN arg1*arg2*arg3
		ENDIF
		RETURN cMethodName
END CLASS

CLASS AnotherClass
	METHOD TestOther() AS VOID PASCAL
	METHOD TestOtherC() CLIPPER
	RETURN NIL
	METHOD TestOther1() AS INT
	RETURN 1
	METHOD TestOther2(n AS INT) AS STRING
	RETURN "2" + n:ToString()
	METHOD TestOther2C(n)
	RETURN "2" + ((INT)n):ToString()

	METHOD DefaultParams1(n1 AS INT, n2 := 2 AS INT, n3 := 3 AS INT) AS INT
	RETURN n1 + n2 + n3
	METHOD DefaultParams2(cValue := "abc" AS STRING, lValue := TRUE AS LOGIC) AS STRING
	RETURN cValue + lValue:ToString():ToUpper()
	METHOD DefaultParams3(dValue := 2018.10.20 AS DATE) AS DATE
	RETURN dValue
	METHOD DefaultParams4(sValue := #MYSYMBOL AS SYMBOL) AS SYMBOL
	RETURN sValue

END CLASS

CLASS GeneralLBTestClass
	EXPORT fld_exp AS INT
	PROTECT fld_prot AS INT
	PRIVATE fld_priv AS INT
	INSTANCE fld_inst AS INT
	ACCESS acc_exp AS INT
	RETURN 0
	PROTECT ACCESS acc_prot AS INT
	RETURN 0
	PRIVATE ACCESS acc_priv AS INT
	RETURN 0

	ASSIGN asn_exp(n AS INT)
	PROTECT ASSIGN asn_prot(n AS INT)
	PROTECT ASSIGN asn_priv(n AS INT)

	METHOD meth_exp(a,b,c,d)
	RETURN NIL
	PROTECTED METHOD meth_prot(a,b,c,d)
	RETURN NIL
	PRIVATE METHOD meth_priv(a,b,c,d)
	RETURN NIL

	METHOD MethodPtr() AS PTR
		LOCAL n AS INT
	RETURN @n
	METHOD MethodIntPtr() AS IntPtr
		LOCAL n AS INT
	RETURN @n
END CLASS

CLASS TestClassParent
END CLASS
CLASS TestClassChild INHERIT TestClassParent
END CLASS

CLASS TestStrong
    PROPERTY Param AS OBJECT AUTO
    CONSTRUCTOR(otest AS OBJECT)
        SELF:Param := oTest
END CLASS

CLASS NilTestClass
	METHOD NilMethod() AS USUAL
	RETURN NIL
	METHOD NilMethodUntyped()
	RETURN NIL
	ACCESS NilAccess
	RETURN NIL

	METHOD NoMethod(c)
		? c
		IF AsString(c) == "DOESNOTEXISTMETHOD"
			RETURN 2
		END IF
	RETURN NIL
	METHOD NoIVarGet(c)
		? c
		IF AsString(c) == "DOESNOTEXISTACCESS"
			RETURN 2
		END IF
	RETURN NIL
END CLASS


[Runtime] Added code to initialize multi dimensional string arrays when /vo2 is enabled

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System.Collections.Generic
USING System.Runtime.InteropServices
///
/// This class contains helper code that is called by the compiler to support various XBase language constructs, such as the
/// automatic memory management of PSZ values created with String2Psz().
///
STATIC CLASS XSharp.Internal.CompilerServices
	///
    /// Subtract 2 strings.
    ///
	STATIC METHOD StringSubtract (lhs AS STRING, rhs AS STRING) AS STRING
		IF lhs != NULL .AND. rhs != NULL
			VAR len := lhs:Length + rhs:Length
			RETURN (lhs:TrimEnd() + rhs:TrimEnd()):PadRight(len)
		ELSEIF lhs != NULL
			RETURN lhs
		ELSEIF rhs != NULL
			RETURN rhs
		ENDIF
		RETURN String.Empty

	///
    /// Allocate a PSZ and add it to the list
    ///
	STATIC METHOD String2Psz(s AS STRING, pszList AS List<IntPtr>) AS IntPtr
		LOCAL pResult AS IntPtr
		IF s == NULL || s:Length == 0
			pResult := MemAlloc(1)
            Marshal.WriteByte(pResult, 0, 0)	 // end of string
        ELSE
            pResult := String2Mem(s)
		ENDIF
		pszList:Add(pResult)
		RETURN pResult

	///
    /// Free all PSZ values in the List
    ///
	STATIC METHOD String2PszRelease(pszList AS List<IntPtr>) AS VOID
		FOREACH VAR p IN pszList
			TRY
				MemFree(p)
			END TRY
		NEXT
		RETURN

	///
    /// Increment the SEQUENCE counter for a BEGIN SEQUENCE statement
    ///
    STATIC METHOD EnterBeginSequence AS VOID
		RuntimeState.GetInstance():BreakLevel+= 1

	///
    /// Decrement the SEQUENCE counter for a BEGIN SEQUENCE statement
    ///
    STATIC METHOD ExitBeginSequence	 AS VOID
		RuntimeState.GetInstance():BreakLevel-= 1

    ///
    /// Determine if we are inside a BEGIN SEQUENCE .. END by looking at the SEQUENCE counter in the runtime.
    ///
	STATIC METHOD CanBreak AS LOGIC
		RETURN RuntimeState.GetInstance():BreakLevel > 0

    STATIC METHOD StringArrayInit(a as System.Array) AS VOID
        LOCAL ranks     := a:Rank AS INT
        LOCAL counters  := INT[]{ranks} as INT[]
        LOCAL bounds    := INT[]{ranks} as INT[]
        FOR VAR i := 1 to ranks
            bounds[i] := A:GetLength(i-1)
        NEXT
        StringArrayInitHelper(a, counters, bounds, ranks)
        RETURN

    PRIVATE STATIC METHOD StringArrayInitHelper( s as System.Array, counters as  int[] , bounds as int[], rank as int) AS VOID
        if  rank == 1
            for var x:= 1 UPTO  Bounds[1] STEP 1
                counters[rank] := x -1             // the counters array must be 0 based
                s:SetValue( "", counters )
            NEXT
        else
            for var x := 1 UPTO  Bounds[rank]  STEP 1
                counters[rank] := x -1          // the counters array must be 0 based
                StringArrayInitHelper( s, counters, bounds, rank - 1 )
            NEXT
        ENDIF

END CLASS

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System.Collections.Generic
USING System.Runtime.InteropServices
///
/// This class contains helper code that is called by the compiler to support various XBase language constructs, such as the
/// automatic memory management of PSZ values created with String2Psz().
///
STATIC CLASS XSharp.Internal.CompilerServices
	///
    /// Subtract 2 strings.
    ///
	STATIC METHOD StringSubtract (lhs AS STRING, rhs AS STRING) AS STRING
		IF lhs != NULL .AND. rhs != NULL
			VAR len := lhs:Length + rhs:Length
			RETURN (lhs:TrimEnd() + rhs:TrimEnd()):PadRight(len)
		ELSEIF lhs != NULL
			RETURN lhs
		ELSEIF rhs != NULL
			RETURN rhs
		ENDIF
		RETURN String.Empty

	///
    /// Allocate a PSZ and add it to the list
    ///
	STATIC METHOD String2Psz(s AS STRING, pszList AS List<IntPtr>) AS IntPtr
		LOCAL pResult AS IntPtr
		IF s == NULL || s:Length == 0
			pResult := MemAlloc(1)
            Marshal.WriteByte(pResult, 0, 0)	 // end of string
        ELSE
            pResult := String2Mem(s)
		ENDIF
		pszList:Add(pResult)
		RETURN pResult

	///
    /// Free all PSZ values in the List
    ///
	STATIC METHOD String2PszRelease(pszList AS List<IntPtr>) AS VOID
		FOREACH VAR p IN pszList
			TRY
				MemFree(p)
			END TRY
		NEXT
		RETURN

	///
    /// Increment the SEQUENCE counter for a BEGIN SEQUENCE statement
    ///
    STATIC METHOD EnterBeginSequence AS VOID
		RuntimeState.GetInstance():BreakLevel+= 1

	///
    /// Decrement the SEQUENCE counter for a BEGIN SEQUENCE statement
    ///
    STATIC METHOD ExitBeginSequence	 AS VOID
		RuntimeState.GetInstance():BreakLevel-= 1

    ///
    /// Determine if we are inside a BEGIN SEQUENCE .. END by looking at the SEQUENCE counter in the runtime.
    ///
	STATIC METHOD CanBreak AS LOGIC
		RETURN RuntimeState.GetInstance():BreakLevel > 0

END CLASS


[Runtime] Fixes #95

[Runtime] Fixes #95

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//

USING System.IO
/// <summary>
/// Fill a series of Array with directory information.
/// </summary>
/// <param name="cFileSpec">The file specification for the directory search.
/// Besides a file name, this specification may include an optional drive, directory, and extension.
/// The file name and extension may include the standard wildcard characters (* and ?).
/// If you do not specify a drive and directory, this function uses the SetDefault() setting.</param>
/// <param name="aFName">The array to fill with the file names matching <paramref name="cFileSpec" />.  Each element will contain the file name and extension as a string, in all uppercase letters. </param>
/// <param name="aFSize">The array to fill with the sizes of the corresponding files in <paramref name="aFName" />. </param>
/// <param name="aFDate">The array to fill with the dates of the corresponding files in <paramref name="aFName" />.</param>
/// <param name="aFTime">The array to fill with the times of the corresponding files in <paramref name="aFName" />, in the form hh:mm:ss. </param>
/// <param name="aFAttr">The array to fill with attributes of the corresponding files in <paramref name="aFName" />.  If <paramref name="aFAttr" /> is specified, hidden, system, and
/// directory files are included as well as normal files.  If <paramref name="aFAttr" /> is not specified, only normal files are included.</param>
/// <returns>The number of files matching the directory skeleton described in <paramref name="cFileSpec" />./// </returns>
/// <remarks>ADir() is a compatibility function and therefore not recommended.
/// It is superseded by the Directory() function, which returns all file information in a multidimensional array.</remarks>
FUNCTION ADir(cFileSpec ,aFName ,aFSize ,aFDate,aFTime,aFAttr) AS DWORD CLIPPER
	IF ! cFileSpec:IsString
		THROW Error.ArgumentError(__FUNCTION__, NAMEOF(cFileSpec), 1,<OBJECT>{cFileSpec})
	ENDIF
	LOCAL aFiles	AS ARRAY
	LOCAL lHasArg   := FALSE AS LOGIC
	LOCAL aNames	:= NULL_ARRAY AS ARRAY
	LOCAL aSizes	:= NULL_ARRAY AS ARRAY
	LOCAL aDates	:= NULL_ARRAY AS ARRAY
	LOCAL aTimes    := NULL_ARRAY AS ARRAY
	LOCAL aAttribs  := NULL_ARRAY AS ARRAY
	IF aFName:IsArray
		aNames  := aFName
		lHasArg := TRUE
	ENDIF
	IF aFSize:IsArray
		aSizes  := aFSize
		lHasArg := TRUE
	ENDIF
	IF aFDate:IsArray
		aDates  := aFDate
		lHasArg := TRUE
	ENDIF
	IF aFTIME:IsArray
		aTimes  := aFTIME
		lHasArg := TRUE
	ENDIF
	IF aFAttr:IsArray
		aAttribs  := aFAttr
		lHasArg := TRUE
	ENDIF
    if aAttribs != NULL_ARRAY
        aFiles := Directory(cFileSpec,FA_NORMAL+FC_HIDDEN+FC_SYSTEM+FC_READONLY)
    ELSE
        aFiles := Directory(cFileSpec,FA_NORMAL)
    ENDIF
	IF ALen(aFiles) > 0 .AND. lHasArg
		LOCAL x AS DWORD
		FOR x := 1 TO ALen(aFiles)
			VAR aFile := aFiles[x]
			IF aNames != NULL_ARRAY .AND. x <= ALen(aNames)
				aNames[x] := aFile[ F_NAME]
			ENDIF
			IF aSizes != NULL_ARRAY .AND. x <= ALen(aSizes)
				aSizes[x] := aFile[ F_SIZE]
			ENDIF
			IF aDates != NULL_ARRAY .AND. x <= ALen(aDates)
				aDates[x] := aFile[ F_DATE]
			ENDIF
			IF aTimes != NULL_ARRAY .AND. x <= ALen(aTimes)
				aTimes[x] := aFile[ F_TIME]
			ENDIF
			IF aAttribs != NULL_ARRAY .AND. x <= ALen(aAttribs)
				aAttribs[x] := aFile[ F_ATTR]
			ENDIF
		NEXT
	ENDIF
	RETURN ALen(aFiles)

/// <summary>
/// Create an Array of directory and file information.
/// </summary>
/// <param name="cFileSpec">The file specification for the search.  Besides a file name, this specification can include an optional drive, directory, and extension.  The file name and extension can include the standard wildcard characters (* and ?).  If you do not specify a drive and directory, the Windows defaults are used.</param>
/// <param name="xAttr">Specifies inclusion of files with special attributes in the returned information.  uAttributes can be a string or a numeric.</param>
/// <returns>An array of subarrays, with each subarray containing information about each file matching cFileSpec.  </returns>
FUNCTION Directory(cFileSpec AS STRING, xAttr := NIL AS USUAL) AS ARRAY
	LOCAL nAttr		AS DWORD
	LOCAL aReturn	AS ARRAY
	LOCAL cPath		AS STRING
	LOCAL cFileMask AS STRING
    LOCAL lWild     AS LOGIC
    IF xAttr:IsNil
		nAttr := 0
	ELSEIF xAttr:IsNumeric
		nAttr := (DWORD) xAttr
	ELSEIF xAttr:IsString
		nAttr := String2FAttr((STRING) xAttr)
	ELSE
		THROW Error.ArgumentError(__FUNCTION__, NAMEOF(xAttr), 2,<OBJECT>{xAttr})
	ENDIF
	aReturn := {}
	IF (nAttr & FA_VOLUME ) != 0
		TRY
			AAdd(aReturn, {DriveInfo{cFileSpec}:VolumeLabel, 0, NULL_DATE, "00:00:00", "V"})
		END TRY
	ENDIF
    lWild := cFileSpec:Contains("*") .or. cFileSpec:Contains("?")
    IF ! lWild
        local sPathSep as STRING
        sPathSep := System.IO.Path.DirectorySeparatorChar:ToString()
	    IF System.IO.Directory.Exists(cFileSpec)
            cFileSpec += sPathSep + "*.*"
	    ELSEIF cFileSpec:EndsWith(sPathSep)
		    cFileSpec += "*.*"
	    ELSEIF cFileSpec:Length == 2 && cFileSpec[1] == System.IO.Path.VolumeSeparatorChar .AND. Char.IsLetter( cFileSpec, 0 )   // only a drive letter specified
		    VAR  curdir := Environment.CurrentDirectory
		    IF Char.ToUpper( cFileSpec[0] ) == Char.ToUpper( curdir[0] )  // if same drive, use current directory
                cFileSpec := curdir + sPathSep + "*.*"
		    ELSE
			    cFileSpec += sPathSep  + "*.*"
		    ENDIF
	    ENDIF
    ENDIF
	TRY
		cPath := Path.GetDirectoryName(cFileSpec)
	CATCH
		cPath := ""
	END TRY

	IF String.IsNullOrEmpty( cPath )
		cPath := System.IO.Directory.GetCurrentDirectory()
	ENDIF

	cFileMask := Path.GetFileName( cFileSpec )

	IF _AND( nAttr,FA_DIRECTORY ) == FA_DIRECTORY
		DirectoryHelper.AddDirectoryInfo( aReturn, cFileSpec, nAttr , NULL)
	ENDIF

	// File Info
	LOCAL files AS STRING[]
	TRY
		files := System.IO.Directory.GetFiles( cPath, cFileMask )
		IF files != NULL
			FOREACH cFile AS STRING IN files
				VAR oFile := System.IO.FileInfo{cFile}
				DirectoryHelper.AddFileInfo(aReturn, oFile, nAttr)
			NEXT
		ENDIF
	CATCH
		NOP
	END TRY

	// Directory info
	IF _AND( nAttr, FA_DIRECTORY) == FA_DIRECTORY .AND. ( cFileSpec:Contains( "*" ) || cFileSpec:Contains( "?" ) )
	    cPath := Path.GetDirectoryName( cFileSpec )
        VAR cName := Path.GetFileName( cFileSpec )
		IF cPath != Path.GetPathRoot( cFileSpec ) .AND. ( cName == "*.*" || cName == "*" )
			DirectoryHelper.AddDirectoryInfo( aReturn, cPath, nAttr, "." )
			DirectoryHelper.AddDirectoryInfo( aReturn, cPath, nAttr, ".." )
		ENDIF

		TRY
			files := System.IO.Directory.GetDirectories( cPath, cName )
			FOR VAR i := 1 UPTO files:Length
				DirectoryHelper.AddDirectoryInfo( aReturn, files[i], nAttr, NULL )
			NEXT
		CATCH
			NOP
		END TRY
	ENDIF

	IF ALen(aReturn) > 1
		aReturn := ASort( aReturn, 1, aReturn:Length, {|x,y| x[1] < y[1] } )
	ENDIF
	RETURN aReturn

INTERNAL STATIC CLASS DirectoryHelper

INTERNAL STATIC METHOD AddFileInfo(aReturn AS ARRAY, oFile AS System.IO.FileInfo, nAttr AS DWORD) AS VOID

	LOCAL lAdd := TRUE AS LOGIC
    VAR cAttribute := DecodeAttributes(oFile:Attributes, nAttr, REF lAdd)
	IF lAdd
        VAR aFile := DirectoryHelper.FileSystemInfo2Array(oFile, cAttribute)
        aadd(aReturn, aFile)
	ENDIF
	RETURN

INTERNAL STATIC METHOD AddDirectoryInfo( aReturn AS ARRAY, cDirectory AS STRING, nAttr AS DWORD, cName AS STRING ) AS VOID
	TRY
		LOCAL oDir := System.IO.DirectoryInfo{ cDirectory } AS DirectoryInfo
		IF oDir:Exists
			LOCAL lAdd      := TRUE AS LOGIC
			VAR cAttribute  := DecodeAttributes(oDir:Attributes, nAttr, REF lAdd)
			IF lAdd
				cAttribute += "D"
                VAR aFile := DirectoryHelper.FileSystemInfo2Array(oDir, cAttribute)
                IF !String.IsNullOrEmpty(cName)
                    aFile[F_NAME] := cName
                ENDIF
                aadd(aReturn, aFile)
			ENDIF
		ENDIF
	CATCH
		NOP
	END TRY

	RETURN

INTERNAL STATIC METHOD FileSystemInfo2Array(info AS FileSystemInfo, cAttribute AS STRING) AS ARRAY
    VAR aFile := ArrayNew(F_LEN)
    aFile[F_NAME] := info:Name
    IF info IS FileInfo
        VAR oFile := (System.IO.FileInfo) info
        aFile[F_SIZE] := oFile:Length
        IF oFile:Length < 0x7FFFFFFFL
            aFile[F_SIZE] := (Int32) oFile:Length
        ENDIF
    ELSE
        aFile[F_SIZE] := 0
    ENDIF
    aFile[F_DATE] := (DATE)  info:LastWriteTime
    aFile[F_TIME] := ConTime(info:LastWriteTime)
    aFile[F_ATTR] := cAttribute
    aFile[F_EA_SIZE] := info:Attributes
    aFile[F_CREATION_DATE] := (DATE)  info:CreationTime
    aFile[F_CREATION_TIME] := ConTime(info:CreationTime)
    aFile[F_ACCESS_DATE] := (DATE)  info:LastAccessTime
    aFile[F_ACCESS_TIME] := ConTime(info:LastAccessTime)
RETURN aFile

INTERNAL STATIC METHOD DecodeAttributes(attributes AS System.IO.FileAttributes, nAttr AS DWORD, lOk REF LOGIC) AS STRING
    VAR cAttribute := ""
    lOk := TRUE
	IF attributes:HasFlag(System.IO.FileAttributes.ReadOnly)
		cAttribute += "R"
	ENDIF
	IF attributes:HasFlag(System.IO.FileAttributes.Hidden)
		lOk := lOk .AND. _AND(nAttr,FC_HIDDEN) == FC_HIDDEN
		cAttribute += "H"
	ENDIF
	IF attributes:HasFlag(System.IO.FileAttributes.System)
		lOk := lOk .AND. _AND(nAttr,FC_SYSTEM) == FC_SYSTEM
		cAttribute += "S"
	ENDIF
	IF attributes:HasFlag(System.IO.FileAttributes.Archive)
		cAttribute += "A"
	ENDIF
    RETURN cAttribute
END CLASS

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//

USING System.IO
/// <summary>
/// Fill a series of Array with directory information.
/// </summary>
/// <param name="cFileSpec">The file specification for the directory search.
/// Besides a file name, this specification may include an optional drive, directory, and extension.
/// The file name and extension may include the standard wildcard characters (* and ?).
/// If you do not specify a drive and directory, this function uses the SetDefault() setting.</param>
/// <param name="aFName">The array to fill with the file names matching <paramref name="cFileSpec" />.  Each element will contain the file name and extension as a string, in all uppercase letters. </param>
/// <param name="aFSize">The array to fill with the sizes of the corresponding files in <paramref name="aFName" />. </param>
/// <param name="aFDate">The array to fill with the dates of the corresponding files in <paramref name="aFName" />.</param>
/// <param name="aFTime">The array to fill with the times of the corresponding files in <paramref name="aFName" />, in the form hh:mm:ss. </param>
/// <param name="aFAttr">The array to fill with attributes of the corresponding files in <paramref name="aFName" />.  If <paramref name="aFAttr" /> is specified, hidden, system, and
/// directory files are included as well as normal files.  If <paramref name="aFAttr" /> is not specified, only normal files are included.</param>
/// <returns>The number of files matching the directory skeleton described in <paramref name="cFileSpec" />./// </returns>
/// <remarks>ADir() is a compatibility function and therefore not recommended.
/// It is superseded by the Directory() function, which returns all file information in a multidimensional array.</remarks>
FUNCTION ADir(cFileSpec ,aFName ,aFSize ,aFDate,aFTime,aFAttr) AS DWORD CLIPPER
	IF ! cFileSpec:IsString
		THROW Error.ArgumentError(__FUNCTION__, NAMEOF(cFileSpec), 1,<OBJECT>{cFileSpec})
	ENDIF
	LOCAL aFiles	AS ARRAY
	LOCAL lHasArg   := FALSE AS LOGIC
	LOCAL aNames	:= NULL_ARRAY AS ARRAY
	LOCAL aSizes	:= NULL_ARRAY AS ARRAY
	LOCAL aDates	:= NULL_ARRAY AS ARRAY
	LOCAL aTimes    := NULL_ARRAY AS ARRAY
	LOCAL aAttribs  := NULL_ARRAY AS ARRAY
	IF aFName:IsArray
		aNames  := aFName
		lHasArg := TRUE
	ENDIF
	IF aFSize:IsArray
		aSizes  := aFSize
		lHasArg := TRUE
	ENDIF
	IF aFDate:IsArray
		aDates  := aFDate
		lHasArg := TRUE
	ENDIF
	IF aFTIME:IsArray
		aTimes  := aFTIME
		lHasArg := TRUE
	ENDIF
	IF aFAttr:IsArray
		aAttribs  := aFAttr
		lHasArg := TRUE
	ENDIF
    if aAttribs != NULL_ARRAY
        aFiles := Directory(cFileSpec,FA_NORMAL+FC_HIDDEN+FC_SYSTEM+FC_READONLY)
    ELSE
        aFiles := Directory(cFileSpec,FA_NORMAL)
    ENDIF
	IF ALen(aFiles) > 0 .AND. lHasArg
		LOCAL x AS DWORD
		FOR x := 1 TO ALen(aFiles)
			VAR aFile := aFiles[x]
			IF aNames != NULL_ARRAY .AND. x <= ALen(aNames)
				aNames[x] := aFile[ F_NAME]
			ENDIF
			IF aSizes != NULL_ARRAY .AND. x <= ALen(aSizes)
				aSizes[x] := aFile[ F_SIZE]
			ENDIF
			IF aDates != NULL_ARRAY .AND. x <= ALen(aDates)
				aDates[x] := aFile[ F_DATE]
			ENDIF
			IF aTimes != NULL_ARRAY .AND. x <= ALen(aTimes)
				aTimes[x] := aFile[ F_TIME]
			ENDIF
			IF aAttribs != NULL_ARRAY .AND. x <= ALen(aAttribs)
				aAttribs[x] := aFile[ F_ATTR]
			ENDIF
		NEXT
	ENDIF
	RETURN ALen(aFiles)

/// <summary>
/// Create an Array of directory and file information.
/// </summary>
/// <param name="cFileSpec">The file specification for the search.  Besides a file name, this specification can include an optional drive, directory, and extension.  The file name and extension can include the standard wildcard characters (* and ?).  If you do not specify a drive and directory, the Windows defaults are used.</param>
/// <param name="xAttr">Specifies inclusion of files with special attributes in the returned information.  uAttributes can be a string or a numeric.</param>
/// <returns>An array of subarrays, with each subarray containing information about each file matching cFileSpec.  </returns>
FUNCTION Directory(cFileSpec AS STRING, xAttr := NIL AS USUAL) AS ARRAY
	LOCAL nAttr		AS DWORD
	LOCAL aReturn	AS ARRAY
	LOCAL cPath		AS STRING
	LOCAL cFileMask AS STRING
    LOCAL lWild     AS LOGIC
    IF xAttr:IsNil
		nAttr := 0
	ELSEIF xAttr:IsNumeric
		nAttr := (DWORD) xAttr
	ELSEIF xAttr:IsString
		nAttr := String2FAttr((STRING) xAttr)
	ELSE
		THROW Error.ArgumentError(__FUNCTION__, NAMEOF(xAttr), 2,<OBJECT>{xAttr})
	ENDIF
	aReturn := {}
	IF (nAttr & FA_VOLUME ) != 0
		TRY
			AAdd(aReturn, {DriveInfo{cFileSpec}:VolumeLabel, 0, NULL_DATE, "00:00:00", "V"})
		END TRY
	ENDIF
    lWild := cFileSpec:Contains("*") .or. cFileSpec:Contains("?")
    IF ! lWild
        local sPathSep as STRING
        sPathSep := System.IO.Path.DirectorySeparatorChar:ToString()
	    IF System.IO.Directory.Exists(cFileSpec)
            cFileSpec += sPathSep + "*.*"
	    ELSEIF cFileSpec:EndsWith(sPathSep)
		    cFileSpec += "*.*"
	    ELSEIF cFileSpec:Length == 2 && cFileSpec[1] == System.IO.Path.VolumeSeparatorChar .AND. Char.IsLetter( cFileSpec, 0 )   // only a drive letter specified
		    VAR  curdir := Environment.CurrentDirectory
		    IF Char.ToUpper( cFileSpec[0] ) == Char.ToUpper( curdir[0] )  // if same drive, use current directory
                cFileSpec := curdir + sPathSep + "*.*"
		    ELSE
			    cFileSpec += sPathSep  + "*.*"
		    ENDIF
	    ENDIF
    ENDIF
	TRY
		cPath := Path.GetDirectoryName(cFileSpec)
	CATCH
		cPath := ""
	END TRY

	IF String.IsNullOrEmpty( cPath )
		cPath := System.IO.Directory.GetCurrentDirectory()
	ENDIF

	cFileMask := Path.GetFileName( cFileSpec )

	IF _AND( nAttr,FA_DIRECTORY ) == FA_DIRECTORY
		DirectoryHelper.AddDirectoryInfo( aReturn, cFileSpec, nAttr , NULL)
	ENDIF

	// File Info
	LOCAL files AS STRING[]
	TRY
		files := System.IO.Directory.GetFiles( cPath, cFileMask )
		IF files != NULL
			FOREACH cFile AS STRING IN files
				VAR oFile := System.IO.FileInfo{cFile}
				DirectoryHelper.AddFileInfo(aReturn, oFile, nAttr)
			NEXT
		ENDIF
	CATCH
		NOP
	END TRY

	// Directory info
	IF _AND( nAttr, FA_DIRECTORY) == FA_DIRECTORY .AND. ( cFileSpec:Contains( "*" ) || cFileSpec:Contains( "?" ) )
	    cPath := Path.GetDirectoryName( cFileSpec )
        VAR cName := Path.GetFileName( cFileSpec )
		IF cPath != Path.GetPathRoot( cFileSpec ) .AND. ( cName == "*.*" || cName == "*" )
			DirectoryHelper.AddDirectoryInfo( aReturn, cPath, nAttr, "." )
			DirectoryHelper.AddDirectoryInfo( aReturn, cPath, nAttr, ".." )
		ENDIF

		TRY
			files := System.IO.Directory.GetDirectories( cPath, cName )
			FOR VAR i := 1 UPTO files:Length
				DirectoryHelper.AddDirectoryInfo( aReturn, files[i], nAttr, NULL )
			NEXT
		CATCH
			NOP
		END TRY
	ENDIF

	IF ALen(aReturn) > 1
		aReturn := ASort( aReturn, 1, aReturn:Length, {|x,y| x[1] < y[1] } )
	ENDIF
	RETURN aReturn

INTERNAL STATIC CLASS DirectoryHelper

INTERNAL STATIC METHOD AddFileInfo(aReturn AS ARRAY, oFile AS System.IO.FileInfo, nAttr AS DWORD) AS VOID

	LOCAL lAdd := TRUE AS LOGIC
    VAR cAttribute := DecodeAttributes(oFile:Attributes, nAttr, REF lAdd)
	IF lAdd
        VAR aFile := DirectoryHelper.FileSystemInfo2Array(oFile, cAttribute)
        aadd(aReturn, aFile)
	ENDIF
	RETURN

INTERNAL STATIC METHOD AddDirectoryInfo( aReturn AS ARRAY, cDirectory AS STRING, nAttr AS DWORD, cName AS STRING ) AS VOID
	TRY
		LOCAL oDir := System.IO.DirectoryInfo{ cDirectory } AS DirectoryInfo
		IF oDir:Exists
			LOCAL lAdd      := TRUE AS LOGIC
			VAR cAttribute  := DecodeAttributes(oDir:Attributes, nAttr, REF lAdd)
			IF lAdd
				cAttribute += "D"
                VAR aFile := DirectoryHelper.FileSystemInfo2Array(oDir, cAttribute)
                aFile[F_NAME] := cName
                aadd(aReturn, aFile)
			ENDIF
		ENDIF
	CATCH
		NOP
	END TRY

	RETURN

INTERNAL STATIC METHOD FileSystemInfo2Array(info AS FileSystemInfo, cAttribute AS STRING) AS ARRAY
    VAR aFile := ArrayNew(F_LEN)
    aFile[F_NAME] := info:Name
    IF info IS FileInfo
        VAR oFile := (System.IO.FileInfo) info
        aFile[F_SIZE] := oFile:Length
        IF oFile:Length < 0x7FFFFFFFL
            aFile[F_SIZE] := (Int32) oFile:Length
        ENDIF
    ELSE
        aFile[F_SIZE] := 0
    ENDIF
    aFile[F_DATE] := (DATE)  info:LastWriteTime
    aFile[F_TIME] := ConTime(info:LastWriteTime)
    aFile[F_ATTR] := cAttribute
    aFile[F_EA_SIZE] := info:Attributes
    aFile[F_CREATION_DATE] := (DATE)  info:CreationTime
    aFile[F_CREATION_TIME] := ConTime(info:CreationTime)
    aFile[F_ACCESS_DATE] := (DATE)  info:LastAccessTime
    aFile[F_ACCESS_TIME] := ConTime(info:LastAccessTime)
RETURN aFile

INTERNAL STATIC METHOD DecodeAttributes(attributes AS System.IO.FileAttributes, nAttr AS DWORD, lOk REF LOGIC) AS STRING
    VAR cAttribute := ""
    lOk := TRUE
	IF attributes:HasFlag(System.IO.FileAttributes.ReadOnly)
		cAttribute += "R"
	ENDIF
	IF attributes:HasFlag(System.IO.FileAttributes.Hidden)
		lOk := lOk .AND. _AND(nAttr,FC_HIDDEN) == FC_HIDDEN
		cAttribute += "H"
	ENDIF
	IF attributes:HasFlag(System.IO.FileAttributes.System)
		lOk := lOk .AND. _AND(nAttr,FC_SYSTEM) == FC_SYSTEM
		cAttribute += "S"
	ENDIF
	IF attributes:HasFlag(System.IO.FileAttributes.Archive)
		cAttribute += "A"
	ENDIF
    RETURN cAttribute
END CLASS