class TestCase::Exception {
  use TestCase::Minimal;
  use TestCase;
  use Comparator;
  
  our $CALLBACK_DIE_LINE  : rw int;
  our $ZERO_DIVIDE_INT_LINE : rw int;
  
  static method exception_get_field_object_undef : int () {
    
    my $test_case : TestCase = undef;
    
    eval {
      $test_case->{x_byte};
    };
    unless ($@) {
      return 1;
    }
      
    eval {
      $test_case->{x_short};
    };
    
    unless ($@) {
      return 0;
    }

    eval {
      $test_case->{x_int};
    };
    
    unless ($@) {
      return 0;
    }
    eval {
      $test_case->{x_long};
    };
    
    unless ($@) {
      return 0;
    }
    eval {
      $test_case->{x_float};
    };
    
    unless ($@) {
      return 0;
    }
    eval {
      $test_case->{x_double};
    };
    
    unless ($@) {
      return 0;
    }
    eval {
      $test_case->{x_test_case};
    };
    
    unless ($@) {
      return 0;
    }
    
    return 1;
  }
  
  static method exception_set_field_object_undef : int () {
    
    my $test_case : TestCase = undef;
    
    eval {
      $test_case->{x_byte} = (byte)1;
    };
    unless ($@) {
      return 0;
    }
    
    eval {
      $test_case->{x_short} = (short)1;
    };
    
    unless ($@) {
      return 0;
    }
    
    eval {
      $test_case->{x_int} = 1;
    };
    
    unless ($@) {
      return 0;
    }
    eval {
      $test_case->{x_long} = 1L;
    };
    
    unless ($@) {
      return 0;
    }
    eval {
      $test_case->{x_float} = 1f;
    };
    
    unless ($@) {
      return 0;
    }
    eval {
      $test_case->{x_double} = 1d;
    };
    
    unless ($@) {
      return 0;
    }
    eval {
      $test_case->{x_test_case} = undef;
    };
    
    unless ($@) {
      return 0;
    }
    
    return 1;
  }
  static method exception_eval_call_method : int () {
    eval {
      TestCase::Exception->exception_die_return_int();
    };
    
    unless ($@) {
      return 0;
    }
    
    return 1;
  }
  
  static method exception_call_stack : int () {
    TestCase::Exception->exception_die_return_int();
  }
  
  static method exception_die_return_byte : byte () {
    die "Error";
  }
  
  static method exception_die_return_short : short () {
    die "Error";
  }
  
  static method exception_die_return_int : int () {
    die "Error";
  }
  
  static method exception_die_return_long : long () {
    die "Error";
  }
  
  static method exception_die_return_float : float () {
    die "Error";
  }
  
  static method exception_die_return_double : double () {
    die "Error";
  }
  
  static method exception_die_return_object : TestCase::Minimal () {
    die "Error";
  }
  
  static method exception_die_return_void : void () {
    die "Error";
  }

  static method exception_die_return_int_eval_catch : int () {
    eval {
      die "Error";
    };
    
    
    unless ($@) {
      return 0;
    }
    
    return 1;
  }
  # Exception
  static method exception_zero_divide_int : int () {
    TestCase::Exception->SET_ZERO_DIVIDE_INT_LINE(__LINE__); 1 / 0;
  }

  static method exception_callback : int () {
    
    my $comparator = (Comparator)method : int ($x1 : object, $x2 : object) {
      TestCase::Exception->SET_CALLBACK_DIE_LINE(__LINE__); die "Comparator Error"; 
    };
    
    $comparator->(undef, undef);
  }

  static method die : int () {
    
    {
      eval { die "Error"; }
      my $error = $@;
      unless (Fn->index($error, "Error\n  ", 0) == 0) {
        return 0;
      }
    }
    
    {
      eval { die; }
      my $error = $@;
      unless (Fn->index($error, "Error\n  ", 0) == 0) {
        return 0;
      }
    }
    
    {
      eval { die undef; }
      my $error = $@;
      unless (Fn->index($error, "Error\n  ", 0) == 0) {
        return 0;
      }
    }
    
    {
      eval { die "Error"; }
      my $error = $@;
      unless (Fn->index($error, "Error\n  ", 0) == 0) {
        return 0;
      }
      unless (eval_error_id == basic_type_id Error) {
        return 0;
      }
    }
    
    {
      eval { die Error::System "Error Message"; }
      my $error = $@;
      unless (Fn->index($error, "Error Message\n  ", 0) == 0) {
        return 0;
      }
      unless (eval_error_id == basic_type_id Error::System) {
        return 0;
      }
    }

    {
      eval { die Error::System; }
      my $error = $@;
      unless (Fn->index($error, "Error\n  ", 0) == 0) {
        return 0;
      }
      unless (eval_error_id == basic_type_id Error::System) {
        return 0;
      }
    }
    
    {
      eval { die 1, "Error Message"; }
      my $error = $@;
      unless (Fn->index($error, "Error Message\n  ", 0) == 0) {
        return 0;
      }
      unless (eval_error_id == 1) {
        return 0;
      }
    }
    
    {
      eval { die -1, "Error Message"; }
      my $error = $@;
      unless (Fn->index($error, "Error Message\n  ", 0) == 0) {
        return 0;
      }
      unless (eval_error_id == -1) {
        return 0;
      }
    }
    
    {
      # Set 1 to eval_error_id
      eval { die -1, "Foo"; }
      
      # 0 means do not throw an exception
      die 0, "Error Message 0";
      
      my $error = $@;
      
      unless (Fn->index($error, "Error Message 0", 0) == 0) {
        return 0;
      }
      
      unless (eval_error_id == -1) {
        return 0;
      }
    }
    
    {
      # Set 1 to eval_error_id
      eval { die -1, "Foo"; }
      
      # 0 means do not throw an exception
      eval {
        die 0, "Error Message 1";
        
        unless (Fn->index($@, "Error Message 1", 0) == 0) {
          return 0;
        }
      }
      
      unless (eval_error_id == 0) {
        return 0;
      }
    }
    
    # die $@
    {
      {
        eval {
          eval {
            die Error::System "System Error";
          }
          
          die $@;
        }
        
        unless ($@ isa Error::System) {
          return 0;
        }
      }
      
      {
        eval {
          eval {
            die Error::System "System Error";
          }
          
          my $error_id = Fn->get_basic_type_id("Error::NotSupported");
          
          die $error_id, $@;
        }
        
        unless ($@ isa Error::NotSupported) {
          return 0;
        }
      }
      
    }
    
    return 1;
  }
  
  static method eval_block : int () {
    
    $@ = "a";
    eval {
      if ($@) {
        return 0;
      }
      $@ = "b";
    }
    
    if ($@) {
      return 0;
    }
    
    return 1;
  }

}
