IronRuby > .NET Testing plans

.NET Testing plans

Purpose

This document serves as a guide to testing IronRuby interoperation with .NET classes. It will cover interacting with the CLR, interacting with .NET Base Class Libraries, interacting with custom CLS compliant and non-CLS compliant libraries, and interacting with unsafe CLR. It will not cover COM interoperation, DLR specific interaction, inter-language interaction via the DLR.

Intro

.NET interop is one of our key features. It is also one of our more complex features. We will look at .NET interop in the following areas:

  • Loading .NET Interop
  • Getting .NET Types
  • Using .NET Types
  • Deriving from .NET Types
  • Special .NET Concepts

CLR to Ruby mapping

For this table, the only mapping rule is that a Namespace.[Namespaces].Class construct in .NET becomes Namespace::[Namespaces]::Class in IronRuby. For .NET enums, the enumerations become IronRuby constants of the type of the enum, so an enum Foo, with enumerations Bar and Baz, the IronRuby structure becomes class Foo, with Foo.Bar = Foo.new and Foo.Baz = Foo.new.

  

CLR

Ruby

Example

Class

Class

System.String => System:: String

Interface

Module

System.Idisposable => System::IDisposable

Namespace

Module

System => System

Enum

Class

System.StringComparison => System::StringComparison

Struct

Class

System.Int32 => System::Int32

Object

Object

  


Delegate

??

  


Generic

System::Scripting::Actions::TypeGroup

System.Nullable => System::Nullable

Loading .NET Interop

.Net interop is initiated by calling require or load with any .NET assembly, or using load_assembly with an assembly’s Strong Name. These test cases will be stored in interop/load.

Scenarios

  • mscorlib
  • .NET BCL assembly
  • .NET custom assembly
  • signed .NET custom assembly
  • signed assembly from the GAC
  • each of the above with a static constructor

Negative Scenarios

  • assembly outside of the search path

Cases

Load requires full file name. Require can use partial name, but will try to load the .rb file first if it finds one.

Load Cases

  • single
    • require
    • load
    • load_assembly
  • multiple
    • require, require
    • require, load
    • require, load_assembly
    • load, require
    • load, load
    • load, load_assembly
    • load_assembly, require
    • load_assembly, load
    • load_assembly, load_assembly

Simply Load

  • Attempt to load via each of the single cases
  • Attempt to load multiple assemblies via each of the multiple cases using different assemblies for each load.  Use PIC to identify proper permutations.

Loading Multiple Times

  • Load the same assembly using each of the multiple methods. Use load_assembly for Strong Named assemblies.
  • Using each of the multiple cases, load the assembly, modify it, then reload it
    • Modify top-level type
    • Modify nested type
    • Modify namespaced type
    • Modify namespace
    • Modify one member

Name and Namespace Clashes

Given a loaded type of:

  • NS.C and NS.C<T>
    • Try a type NS
    • Try a type NS<T>
    • Try a type DiffNS.D
    • Try a type DiffNS.D<T>
    • Try a type DiffNS.C
    • Try a type DiffNS.C<T>
    • Try a type NS.C
    • Try a type NS.C<T>
    • Try a type NS.D
    • Try a type NS.D<T>
  • C and C<T>
    • Try a namespace C

Try these with the loaded type coming from .NET, and from Ruby.

Getting .NET Types

These test cases will be stored in interop/mapping.

Scenarios

  • Deeply nested types
  • Arrays
    • One dimensional
    • Multi-dimensional
    • Jagged
    • Non-zero lower bound
  • Type which causes TypeLoad Exception
    • Assembly which has multiple types, one of which causes the exception
  • Forwarded type
  • Type from Reflection.Emit
  • Classes
    • Abstract class
      • Empty
      • Not Empty
      • Base
      • Derived abstract class
      • Abstract class derived from non-abstract class
      • Abstract class implementing an interface
    • Sealed class
    • Static class
    • Partial Class
      • All in CS isn’t interesting, gets compiled to one assembly.
      • Partially implemented in IronRuby
    • Generic Class
      • From 0 up to 1 template parameters
      • From 0 up to 2 template parameters
    • Class implementing an interface
    • Ruby class derived from each of these
  • Interface
    • Partially implemented interfaces
    • Generic interface
    • Interface declaring property, indexer, event, method
    • RubyClass < IOne < ITwo
    Ruby class mixing in a CLR interface
  • Enum
    • Empty
    • 1 member
    • Multiple members
    Struct
    • Empty
    • Implemented
  • Namespace
    • Unique namespace
    • Namespace collision
      • With other namespace
      • With a class
  • TypeGroup
    • From 0 up to 1 template parameter
    • From 0 up to 2 template parameters
    • From 1 up to 3 template parameters
    • Interface with each of these
  • Concrete instance of Typegroup (e.g. Nullable<int>)
    • Using [] notation
    • Using .of() notation
  • Simple types
    • Value Types
    • Reference Types
  • Delegates
  • Instances of
    • CLR simple types
    • CLR classes listed above
    • TypeGroups

Negative Scenarios

Cases

  • Get each of the scenario types. Ensure that mapping works correctly
  • Attempt to get basic types with name conflicts
  • Attempt to get basic types with non-CLS names
  • Attempt to get the type of the Template parameter for Generics
  • Attempt to get the type of a Template parameter that hasn’t been provided
  • Ensure name mapping and name mangling works

Using .NET Types

These test cases will be stored in interop/using. Status and discussion can be found here

Scenarios

  • Types in “Getting .NET Types”
  • Concrete instance of TypeGroup with [] notation and an array type
  • A class with multiple constructor overrides

Negative Scenarios

Instantiating Abstract class

Cases

Member Modification

  • Add a property
  • Add a method
    • Public
    • Private
    • Protected
    • Overloads
    • Various numbers of args
  • Attempt to add an existing method
  • Add a generic method
  • Add a method which conflicts with Ruby keywords
  • Add an overload to an existing method
  • Add a field
  • Modify a field
    • Static field
    • Instance field
    • Readonly field
    • Volatile field
    • Const field
  • Remove a property
    • Different casing (should error out)
    • Created in CLR
    • Created in Ruby
  • Remove a property multiple times
  • Remove a method
  • Remove an existing method
  • Remove a method overload (?)
  • Change the visibility of an existing method
  • Instantiate an object and use the above methods.
  • Remove an interface method and test for behavior in regard to the broken interface
  • Call Type.GetMembers() on a modified class to verify added/deleted members.
  • Modify a property defined in an interface
  • Use a property defined in an interface
  • Modify a virtual method
  • Attempt to use a virtual method

Special Parameter types

  • Use methods with different parameter lists. In C#:
    • public void M1(int x, int y) { }
    • public void M2(int x, params int[] y)
    • public void M3(int x, [DefaultParameterValue(5)] int y)
    • public void M6([Optional] int x, [Optional] object y)
    • public void M7([Out] int x)
    • public void M7([ParamsDictionary] IattributesCollection dict)
    • one special parameter
    • two special parameters
    • one normal, one special in both orders
    • multiple parameters of mixed kind
  • Use DefaultParameterValue/Optional attributes
    • Can optional parameters be omitted?
    • Do DefaultParameterValue’s get used?
  • Parameter names that collide with keywords or methods
  • Use a method with ref and out parameters in all these scenarios

Overloaded Methods

  • Override interesting methods like ToString() and ensure Ruby equivalent #to_str still works.
  • Add/remove an override for a base-class virtual method. Also, hide the base-class method with anew one

      

  • Add/remove new operator overloads and verify that the operators work in ruby
  • Add/remove operators for unsupported operators in Ruby
  • Add/remove implicitly and explicitly implemented interfaces
  • Pick an override from Ruby
  • Non-generic / generic methods have the same name
    • Activator.CreateInstance scenario
    • G<T>.M(int), G<T>.M(T)
    • G.M(int), G.M<T>(T)
  • Static method and instance method have the same name
    • instance M(C), static M(thisType, C)
  • Same name as explicit interface method

Ruby Considerations

  • Call existing Ruby methods from Object and Kernel on .NET objects
  • Use Ruby’s mixin functionality. For example, include Enumerable and define .each, and test for the existence of other Enumerable methods
  • Attempt to add a Ruby object to a typed .NET array
  • Attempt to assign an object of the wrong type to a field
  • Get an unbound .NET method
  • Direct call of method vs. send :method vs eval method

Indexers

  • Use a default indexer from within Ruby
  • Read only indexers :[]
  • Write only indexers :[]=
  • Indexer defined in interface
  • Overloaded interface
  • Indexer defined in VB style
  • Parameter list on indexer

Other

  • Implicit and explicit conversions in Ruby
  • Return a private type
  • Check for presence of private members with –X:PrivateBinding

Events/Delegates

  • Define a delegate from Ruby
  • Use a delegate from Ruby
  • Generic delegate types
  • With overloaded method
  • Add a method to a delegate
  • Add a method multiple times
  • Remove a method from a delegate
  • Remove a repeated method from a delegate (one time should remove one occurrence)
  • Remove all methods from the delegate (should be benign)
  • Remove a non-existent method from a delegate with other methods (should be benign)
  • Empty delegate should have an empty invocation list
  • Define an event in an interface
  • Define an event in a class
  • Static events
  • Static delegates
  • Add a delegate as an event
  • Remove a delegate as an event
  • Remove a method as an event
  • Event with Add only
  • Event with Remove only
  • Add a method as an event
  • Add an incompatible delegate and method to an event

Special Cases

IronRuby has the ability to modify modules dynamically. This should work for CLR types too. If you mix in a module to a class, create an instance of the modified class, add another method to the module, and use that method in the already existing instance. In pure Ruby, as an example:

    
      module M
        def foo
          &apos;foo&apos;
        end
      end

      class C
        include M
      end

      c = C.new

      c.foo #=> &apos;foo&apos;

      c.bar #=> NoMethodError

      module M
        def bar
          &apos;bar&apos;
        end
      end

      c.bar #=> &apos;bar&apos;
    
  

This should work for all of the above scenarios. You should also be able to add methods to any object that represents itself as a module in Ruby, including interfaces and namespaces.

Deriving from .NET Types

These test cases will be stored in interop/derivation.

Scenarios

  • Ruby Type deriving from each of the Types in “Getting .NET Types”
  • .NET class that has been reopened in Ruby
  • System.Delegate
  • System.Enum
  • System.ValueType
  • System.Array
  • System.MarhalByRefObject
  • Literal
  • Extensible<T>
  • Nullable<T>
  • Reopen .NET instance
  • RubyClass < DotNetB, RubyClassB < RubyClass
  • Modify type in Ruby, pass back to .NET and exercise modified/new methods

Negative Scenarios

  • Deriving from sealed class

Cases

  • All cases from “Using .NET Types”

Special .NET Concepts

These test cases will live in interop/special.

  • Array Types
    • Creation
    • Operations
      • Indexing
        • Ensure that A[1,2] does a multi-variable lookup
        • Indexing redefinition
    • Pass the array as an argument
  • Operations on an enum type and its members
  • Proper mapping for exception types
  • Proper handling of special .NET classes
    • IList, List, ArrayList
    • Hashtable, Dictionary`2, IDictionary
    • IEnumerable, IEnumerator
    • IComparable
  • Extensible<T>
  • Operations on namespaces
    • Top level
    • Nested
    • Bottom level
  • Operations on nested classes
  • Operations on Assembly and AssemblyBuilder
  • CLRString vs Ruby String
  • Delegates vs Ruby Proc’s
  • Getting at the underlying .NET type
  • Implicit and explicit conversions
Tag page
You must login to post a comment.