text: #include <assert.h>
  
  #include "express/expr.h"
  
  /* core */
  #include "express/hash.h"
  #include "express/linklist.h"
  
  /* non-core */
  #include "express/dict.h"
  #include "express/variable.h"
  
  #include "driver.h"
  #include "fff.h"
  
  /*
   * mock globals
   */
  
  char * EXPRESSprogram_name;
  int yylineno;
  int __SCOPE_search_id;
  
  Error ERROR_warn_unsupported_lang_feat;
  Error WARNING_case_skip_label;
  Error ERROR_undefined_attribute;
  
  /*
   * mock functions
   */
  
  DEFINE_FFF_GLOBALS
  
  FAKE_VALUE_FUNC( int, EXPRESS_fail, Express )
  FAKE_VALUE_FUNC( Variable, ENTITYfind_inherited_attribute, struct Scope_ *, char *, struct Symbol_ ** )
  FAKE_VALUE_FUNC( Variable, ENTITYresolve_attr_ref, Entity, Symbol *, Symbol * )
  FAKE_VALUE_FUNC( struct Scope_ *, ENTITYfind_inherited_entity, struct Scope_ *, char *, int )
  FAKE_VOID_FUNC( EXP_resolve, Expression, Scope, Type )
  
  void setup() {
      EXPinitialize();
  
      RESET_FAKE( EXPRESS_fail );
      RESET_FAKE( ENTITYfind_inherited_attribute );
      RESET_FAKE( ENTITYresolve_attr_ref );
      RESET_FAKE( ENTITYfind_inherited_entity );
      RESET_FAKE( EXP_resolve );
  }
  
  /* TODO: remove DICTlookup after eliminating DICT_type */
  void EXP_resolve_type_handler( Expression exp, Scope cxt, Type typ ) {
      ( void ) typ;
      Type res_typ = DICTlookup( cxt->symbol_table, exp->symbol.name );
      exp->type = res_typ;
      exp->return_type = res_typ;
      exp->symbol.resolved = RESOLVED;
  }
  
  int test_resolve_select_enum_member() {
      Schema scope;
      Symbol * e_type_id, *enum_id, *s_type_id;
      Type enum_typ, select_typ, chk_typ;
      TypeBody tb;
      Expression expr, op1, op2, exp_enum_id;
  
      /*
       * boilerplate: create objects, populate symbol tables
       */
      scope = SCHEMAcreate();
  
      s_type_id = SYMBOLcreate( "sel1", 1, "test1" );
      e_type_id = SYMBOLcreate( "enum1", 1, "test1" );
      enum_id = SYMBOLcreate( "val1", 1, "test1" );
  
      enum_typ = TYPEcreate_name( e_type_id );
      enum_typ->symbol_table = DICTcreate( 50 );
  
      exp_enum_id = EXPcreate( enum_typ );
      exp_enum_id->symbol = *enum_id;
      exp_enum_id->u.integer = 1;
  
      tb = TYPEBODYcreate( enumeration_ );
      tb->list = LISTcreate();
      LISTadd_last( tb->list, enum_id );
      enum_typ->u.type->body = tb;
  
      DICT_define( scope->symbol_table, e_type_id->name, enum_typ, &enum_typ->symbol, OBJ_TYPE );
  
      /* TODO: OBJ_ENUM / OBJ_EXPRESSION are used interchangeably, this is confusing. */
      DICT_define( scope->enum_table, exp_enum_id->symbol.name, exp_enum_id, &exp_enum_id->symbol, OBJ_EXPRESSION );
      DICT_define( enum_typ->symbol_table, enum_id->name, exp_enum_id, enum_id, OBJ_EXPRESSION );
  
      select_typ = TYPEcreate_name( s_type_id );
      tb = TYPEBODYcreate( select_ );
      tb->list = LISTcreate();
      LISTadd_last( tb->list, enum_typ );
      select_typ->u.type->body = tb;
      DICT_define( scope->symbol_table, s_type_id->name, select_typ, &select_typ->symbol, OBJ_TYPE );
  
      op1 = EXPcreate_from_symbol( Type_Identifier, s_type_id );
      op2 = EXPcreate_from_symbol( Type_Identifier, enum_id );
      expr = BIN_EXPcreate( OP_DOT, op1, op2 );
  
      /*
       * test: sel_ref '.' enum_id
       * expectation: enum_typ
       */
      EXP_resolve_fake.custom_fake = EXP_resolve_type_handler;
  
      chk_typ = EXPresolve_op_dot( expr, scope );
  
      assert( EXP_resolve_fake.call_count == 1 );
      assert( expr->e.op1->type == select_typ );
      assert( chk_typ == enum_typ );
  
      /* in case of error SIGABRT will be raised (and non-zero returned) */
  
      return 0;
  }
  
  /* TODO: remove DICTlookup after eliminating DICT_type */
  void EXP_resolve_entity_handler( Expression exp, Scope cxt, Type unused ) {
      ( void ) unused;
      Entity ent = DICTlookup( cxt->symbol_table, exp->symbol.name );
      Type typ = ent->u.entity->type;
      exp->type = typ;
      exp->return_type = typ;
      exp->symbol.resolved = RESOLVED;
  }
  
  Variable ENTITY_resolve_attr_handler( Entity ent, Symbol * grp_ref, Symbol * attr_ref ) {
      ( void ) grp_ref;
      Variable v = DICTlookup( ent->symbol_table, attr_ref->name );
      return v;
  }
  
  int test_resolve_entity_attribute() {
      Schema scope;
      Symbol * e_type_id, *attr_id;
      Entity ent;
      Type attr_typ, chk_typ;
      TypeBody tb;
      Expression exp_attr, expr, op1, op2;
      Variable var_attr;
      Linked_List explicit_attr_list;
  
      /*
       * boilerplate: create objects, populate symbol tables
       */
      scope = SCHEMAcreate();
  
      e_type_id = SYMBOLcreate( "entity1", 1, "test2" );
      ent = ENTITYcreate( e_type_id );
      DICT_define( scope->symbol_table, e_type_id->name, ent, &ent->symbol, OBJ_ENTITY );
  
      attr_id = SYMBOLcreate( "attr1", 1, "test2" );
      exp_attr = EXPcreate_from_symbol( Type_Attribute, attr_id );
      tb = TYPEBODYcreate( number_ );
      attr_typ = TYPEcreate_from_body_anonymously( tb );
      attr_typ->superscope = ent;
      var_attr = VARcreate( exp_attr, attr_typ );
      var_attr->flags.attribute = 1;
      explicit_attr_list = LISTcreate();
      LISTadd_last( explicit_attr_list, var_attr );
  
      LISTadd_last( ent->u.entity->attributes, explicit_attr_list );
      DICTdefine( ent->symbol_table, attr_id->name, var_attr, &var_attr->name->symbol, OBJ_VARIABLE );
  
      op1 = EXPcreate_from_symbol( Type_Identifier, e_type_id );
      op2 = EXPcreate_from_symbol( Type_Attribute, attr_id );
      expr = BIN_EXPcreate( OP_DOT, op1, op2 );
  
      /*
       * test: entity_ref '.' attribute_id
       * expectation: attr_typ
       */
      EXP_resolve_fake.custom_fake = EXP_resolve_entity_handler;
      ENTITYresolve_attr_ref_fake.custom_fake = ENTITY_resolve_attr_handler;
  
      chk_typ = EXPresolve_op_dot( expr, scope );
  
      assert( EXP_resolve_fake.call_count == 1 );
      assert( expr->e.op1->type == ent->u.entity->type );
      assert( chk_typ == attr_typ );
  
      /* in case of error SIGABRT will be raised (and non-zero returned) */
  
      return 0;
  }
  
  struct test_def tests[] = {
      {"resolve_select_enum_member", test_resolve_select_enum_member},
      {"resolve_entity_attribute", test_resolve_entity_attribute},
      {NULL}
  };
  