CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Patrick Smacchia [MVP C#]

July 2008 - Posts

  • The Anonymous Namespace Bug

     

    Actively participating in the development of a static code analyzer such as NDepend is a good way to learn about tricky Common Type System (CTS) details. Every feature have to be tested properly to cop with things like managed and unsafe unmanaged pointer types (ref out and *), open/close generic types, generic parameter types, methods with special name (.ctor() .cctor() get_ set_ add_ remove_...), all sorts of arrays combination, virtual/new/override methods, explicit interface implemented methods, nested types, anonymous namespace... And of course, all this tricky points can be more or less combinated.

    During our tests we found 2 bugs related to anonymous namespace. One in VSTS Coverage and one in the Visual Studio Code Model. Hopefully, the other CTS peculiarity seems to be bug free as far as our tests are concerned.

    When the lightning strikes 2 times the same place, I suppose that this is not by chance: namespace are not first class citizen in the CTS. In other words, there are no metadata tables related to namespace in the metadata of an assembly. At IL level, namespace are just types names’ prefixes. This lack of support for namespace certainly leads team responsible for these features to forget about testing these cases.

     

    The Team Coverage Anonymous Namespace Bug

    This bug occurs when there is a nested type in a type declared in an anonymous namespace. For example testing this piece of code…

    …leads to the following Team Coverage XML. We can see that instead of having the class ClassInAnonymousNamespace.NestedClass declared inside the anonymous namespace, we have a class named .NestedClass declared in the ClassInAnonymousNamespace namespace.

     

    The Visual Studio Code Model Anonymous Namespace Bug

    This bug occurs with VB.NET, when you have a type declared in an anonymous namespace. For the VB.NET compiler, the anonymous namespace doesn’t exist. Indeed, the VB.NET compiler makes it so that every type declared in an assembly are prefixed by the name of the assembly. I don’t know the reason for such choice, but I hope there is a solid one because it is really not elegant. Update 24 July 2008:  This is a customizable VB.NET project setting: Application > Root Namespace.


    Now consider the VisualStudio code model. As shown below, a VB.NET type declared in an anonymous namespace doesn’t reference a namespace object. To see the assembly name namespace, one needs to analyze the delta between the FullName and the Name of the type.


     

  • Dealing with Code Un-Coverable by Tests

    Code UnCoverable by Tests

    Even for test-coverage addict (as me), there is some code that simply cannot be covered by tests. An example?

    The call to MessageBox.Show() cannot be tested automatically since it is blocking. Of course we could mock calls to MessageBox.Show(), but at the end of the day, there will be at least one call to this method in the code base, one call that cannot be covered by tests.

    This example is not isolated. There are a multitude of cases where code reacts to things that cannot be automatized, such as exception that cannot be reproduced…

    …but also some environment settings, folder or file browsing by user, showing a modal custom dialog and more.

     

    The Problem

    As we explained above, while using mocking can help reduce the surface of code not-coverable by tests, it can’t help to get a code base 100% covered. When exploring test coverage results, with NCoverExplorer, Visual Studio Team System or NDepend, this problem will lead to classes 98% or so covered. It is taking time and it is error prone to figured out each time you are exploring coverage results, that these are false alert. This is clearly anti-agile! What we want is a clean 100% coverage result!

     

    The Solution

    NCover and NDepend are tackling this problem the same way: you can exclude some methods from coverage results through a dedicated (and eventually custom) attribute.

    NCover knows about this attribute through the /ea command line option as describe here by Jamie Cansdale. Code tagged with this attribute will be excluded from coverage statistics and won’t disturb while exploring test coverage results. For example, if all methods of the class Foo are 100% covered except the method MessageBoxShow(), if the method MessageBoxShow() is tagged with the coverage exclude attribute then the class Foo will be shown as 100% covered. A great side-effect is that developers reviewing the code will know about this coverage exclusion.

    With NDepend, the coverage excluding attribute can be provided through the import coverage file dialog as shown below. This will have the same effect as with NCover. For convenience, the assembly NDepend.CQL.dll can be linked from your project. This assembly contains the dedicated attribute: NDepend.CQL.UncoverableByTestAttribute. If you prefer you can provide your own attribute.


     

    As far as I know, Visual Studio team System Coverage doesn’t support yet this feature but it might be considered for future releases, as explained here.

     

    The Bonus

    Actually the burden of exploring coverage results to make sure that some classes or namespaces should be 100% covered is not really agile. It is manual, time consuming, error-prone and cannot be capitalized for future iterations.

    NDepend can help automate this task with just 2 CQL rules:

    // <Name>Types 100% covered by tests</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE 
    HasAttribute "NDepend.CQL.FullCoveredAttribute" AND PercentageCoverage < 100
     
    // <Name>Methods 100% covered by tests</Name>
    WARN IF Count > 0 IN SELECT METHODS WHERE 
    HasAttribute "NDepend.CQL.FullCoveredAttribute" AND PercentageCoverage < 100

     

    Basically, these rules will check automatically that all types and methods tagged with the attribute FullCoveredAttribute are indeed 100% covered. Insert these rules in your build process and you’ll be informed as soon as a lack of test coverage is luring. Here also, as a bonus side-effect developer reviewing or refactoring the code will know instantly which part of the code is supposed to be 100% covered.  For applying this trick to the code base of NDepend since the code coverage feature is available, I can testify that it is a really, really time saving trick.

    For convenience, the assembly NDepend.CQL.dll can be linked from your project and it contains the dedicated attribute: NDepend.CQL.FullCoveredAttribute (and also the attribute NDepend.CQL.MoreThan95PercentCoveredAttribute). Of course, CQL rules can be readily tweaked to work with a custom attribute.

     

  • Rules for CLR Add-Ins Contract and View Assemblies

     
    The CLR Add-In team recently released on CodePlex a set of 49 FxCop rules to verify View and Contract assemblies. If you don’t don’t know what is View and Contract assemblies, I suggest reading this excellent CLR Inside-Out MSDN Magazine article by Jack Gudenkauf and Jesse Kaplan. It is about how to harness System.AddIn facilities.

     

    I spent an hour implementing these rules with CQL and came to the following result. Notice that these rules can be readily adapted if you have sereval contract or view assemblies.

     

     

    Rules on Contract assemblies

    // <Name>Contract assemblies should not have references to non-framework assemblies</Name>
    WARN IF Count > 0 IN SELECT ASSEMBLIES WHERE 
      
    IsUsedBy "MyContractAssembly" AND 
      
    // Framework assemblies are mscorlib and thos whose names begin with System.
      (!NameIs "mscorlib" AND 
       
    !NameLike "^System.")

    // Don't reference non-framework assemblies from the contract assembly unless you can 
    // be sure that the assembly can be loaded on both sides of the contract boundary.


    // <Name>Contract types that represent add-ins should be marked with the AddInContractAttribute</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE
      
    IsInterface AND // Limiting search to interfaces only.
      !HasAttribute "OPTIONAL:System.AddIn.Pipeline.AddInContractAttribute"

    // Contracts that represent add-ins and are intended to be activated should be marked 
    // with the AddInContractAttribute. Contracts that only represent objects passed between 
    // hosts and add-ins do not need this attribute; if all of the types in this assembly 
    // fall under this category then this rule can be ignored.


    // <Name>Value types defined in a contract assembly should be serializable</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE
      
    IsStructure AND !IsSerializable 

    // Value types (structs) are only useful in the contract assembly if 
    // they can be serialized across the boundary.

    // <Name>Contract interfaces must implement IContract</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE 
      
    IsInterface AND
      
    !Implement "OPTIONAL:System.AddIn.Contract.IContract"

    // All interfaces defined in the contract assembly must implement IContract, 
    // or another interface that implements IContract. Using an interface that 
    // doesn’t implement IContract can cause problems with lifetime management.


    // <Name>Contract assemblies should not define types that derive from MarshalByRefObject</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE
      
    DeriveFrom "OPTIONAL:System.MarshalByRefObject"

    // MarshalByRefObjects can cause problems with lifetime management. If this type 
    // represents an object you want to marshal by reference, you should define an 
    // interface implementing IContract to represent it. If it is intended to be copied 
    // (and thus passed by value), then you should define a simple serializable value 
    // type (struct) to represent it.


    // <Name>Exception types must implement ISerializable and be marked with the Serializable attribute</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE
      
    DeriveFrom "OPTIONAL:System.Exception" AND
      
    (!Implement "OPTIONAL:System.Runtime.Serialization.ISerializable" OR
       
    !HasAttribute "OPTIONAL:System.SerializableAttribute")

    // Exception must implement ISerializable and be marked with the SerializableAttribute 
    // in order to function well across AppDomain boundaries.


    // <Name>Contract assemblies should not define static types</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE IsStatic

    // The state of static types is stored per AppDomain and changes to state 
    // in one AppDomain are not reflected in others.


    // <Name>Contract assemblies should not define reference types other than exception types</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE
      
    IsClass AND !DeriveFrom "OPTIONAL:System.Exception"

    // The only valid reference type is an Exception type. If this type represents an 
    // object you want to marshal by reference, you should define an interface implementing 
    // IContract to represent it. If it is intended to be copied (and thus passed by value),
    // then you should define a simple serializable value type (struct) to represent it.


    // <Name>Contract assemblies should not define delegate types</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE IsDelegate 

    // Delegates do no marshal well across AppDomain or Process boundaries 
    // and so should not be used in contracts.


    // <Name>Contract assemblies should not define exception types</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE
      
    DeriveFrom "OPTIONAL:System.Exception"

    // If possible, you should use one of the framework defined exceptions rather 
    // than defining your own. If you decide to define your own exceptions, 
    // they become part of your object model and you will need to define corresponding 
    // Exception types in the views and perform the conversion in your adapters.


    // <Name>Non-exception types should prefer [Serializable] attribute over ISerializable</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE
      
    !DeriveFrom "OPTIONAL:System.Exception" AND
      
    Implement "OPTIONAL:System.Runtime.Serialization.ISerializable"

    // Implementing ISerializable adds a significant performance overhead compared 
    // to standard serialization. If possible, use the built in serialization 
    // engine instead of customizing it with ISerializable.


    // <Name>All interfaces used in contract assemblies should implement IContract</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE 
      
    IsDirectlyUsedBy "MyContractAssembly" AND
      
    IsInterface AND
      
    !Implement "OPTIONAL:System.AddIn.Contract.IContract"

    // All interfaces used in the contract assembly should implement IContract, or 
    // another interface that implements IContract. Using an interface that doesn’t 
    // implement IContract can cause problems with lifetime management.


    // <Name>Contract assemblies should not use types that derive from MarshalByRefObject</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyContractAssembly" AND
      
    DeriveFrom "OPTIONAL:System.MarshalByRefObject"

    // MarshalByRefObjects can cause problems with lifetime management. If this
    // type represents an object you want to marshal by reference, you should define
    // an interface implementing IContract to represent it. If it is intended to be
    // copied (and thus passed by value), then you should define a simple serializable
    // value type (struct) to represent it.


    // <Name>Contract assemblies should only use types that are either serializable or are interfaces implementing IContract</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE 
      
    IsDirectlyUsedBy "MyContractAssembly" AND
      
    (!IsSerializable OR
       
    (IsInterface AND !Implement "OPTIONAL:System.AddIn.Contract.IContract"))

    // This type is neither serializable nor is it an interface implementing IContract. 
    // If it represents an object you want to marshal by reference, you should define an 
    // interface implementing IContract to represent it. If it is intended to be copied 
    // (and thus passed by value), then you should define a simple serializable value 
    // type (struct) to represent it.


    // <Name>Contract assemblies should not contain any static members</Name>
    WARN IF Count > 0 IN SELECT FIELDS FROM ASSEMBLIES "MyContractAssembly" WHERE IsStatic

    // Static state is stored per AppDomain, and changes to state in one 
    // AppDomain are not reflected in others.


    // <Name>Contract assemblies should not use delegates</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyContractAssembly" AND
      
    IsDelegate 

    // Delegates do not marshal well across AppDomain or Process boundaries, 
    // and so should not be used in contracts.


    // <Name>Contract assemblies should not use arbitrary types that implement ICollection<T> or ICollection</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyContractAssembly" AND
      
    (Implement "OPTIONAL:System.Collections.Generic.ICollection<T>" OR
       
    Implement "OPTIONAL:System.Collections.ICollection")


    // <Name>Contract assemblies should not use a type defined simply as System.Object</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyContractAssembly" AND
      
    FullNameIs "System.Object"

    // Using the System.Object type makes it very difficult to ensure that this
    // will version well over time. If this is intended to represent an arbitrary
    // contract, type you should use IContract instead.


    // <Name>Contract assemblies should not use a type defined simply as System.Type</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyContractAssembly" AND
      
    FullNameIs "System.Type"

    // Passing a System.Type across the boundary causes the assembly containing it 
    // to be loaded in the other domain as well. This greatly lessens the value of 
    // the isolation boundary for versioning, security, unloadability, and reliability.


    // <Name>Contract assemblies should not use types from the System.Reflection namespace other than AssemblyName</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyContractAssembly" AND
      
    FullNameLike "System.Reflection." AND
      
    !NameIs "AssemblyName"

    // Passing most reflection types across the boundary causes the assembly 
    // containing it to be loaded in that domain as well. This greatly lessens 
    // the value of the isolation boundary for versioning, security, unloadability, 
    // and reliability.


    // <Name>Contract assemblies should not use System.AddIn.Contract.Collections.IListContract<T></Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyContractAssembly" AND
      
    FullNameIs "OPTIONAL:System.AddIn.Contract.Collections.IListContract<C>"

    // System.AddIn contains helper methods for using System.AddIn.Contract.IList<T> and 
    // should be preffered over the version in System.AddIn.Contracts.Collections. 
    // System.AddIn.Contract.Collections.IList<T> is in place largely for compatibility reasons.


    // <Name>Contract assemblies should not use System.AddIn.Contract.Collections.*</Name>
    WARN IF Count > 0 IN SELECT NAMESPACES WHERE
      
    IsDirectlyUsedBy "MyContractAssembly" AND
      
    NameIs "OPTIONAL:System.AddIn.Contract.Collections"

    // System.AddIn.Contract.Collections.* should not be used. If you use 
    // System.AddIn.Contract.Collections, you will need to write your own adapters for them.


    // <Name>Contract assemblies should not declare non-public types</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyContractAssembly" WHERE !IsPublic

    // The purpose of a contract assembly is to allow adapters to represent the 
    // types across isolation and versioning boundaries. Making a type in this 
    // assembly non-public prevents it from being used in this fashion.

     

     

    Rules on View assemblies 


    // <Name>Activatable add-in types should be marked with the AddInBaseAttribute</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyViewAssembly" WHERE 
      
    (IsInterface OR IsAbstract) AND 
      
    HasAttribute "OPTIONAL:System.AddIn.Pipeline.AddInBaseAttribute"

    // No type in the assembly is marked with the AddInBaseAttribute. For this assembly 
    // to be used (without modification) as an AddInView, the types that represent 
    // add-ins need to be marked with the AddInBaseAttribute. Even if the type will not 
    // be used directly through System.AddIn today, applying this attribute now will 
    // make it easier to migrate while still maintaining compatibility.

    // <Name>View types should not implement IContract or an interface that implements IContract</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyViewAssembly" WHERE 
      
    Implement "OPTIONAL:System.AddIn.Contract.IContract"

    // Exposing a contract directly in the view can cause a variety of problems. It makes it
    // very difficult to version over time since it strongly binds the consumer of the view 
    // to a particular version of the contract assembly.


    // <Name>View types should not be marked serializable</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyViewAssembly" WHERE 
      
    Implement "OPTIONAL:System.Runtime.Serialization.ISerializable" OR
      
    HasAttribute "OPTIONAL:System.SerializableAttribute"

    // Types defined in views should never have to directly cross any isolation 
    // boundary, and so shouldn’t need to be serializable. You can mark these 
    // serializable if you need to store them to disk but you shouldn’t do so in
    // order to pass them across boundaries.


    // <Name>View types should not derive from MarshalByRefObject</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyViewAssembly" WHERE 
      
    DeriveFrom "OPTIONAL:System.MarshalByRefObject"

    // Types defined in views should never have to directly cross any isolation
    // boundary and so shouldn’t need to be a MarshalByRefObject.


    // <Name>View types should not have generic parameters</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyViewAssembly" WHERE 
      
    IsGeneric 

    // Types with generic parameters are difficult to isolate, since their 
    // pipeline components cannot be generated automatically.


    // <Name>There should be no exception types defined in the view assembly</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyViewAssembly" WHERE 
      
    DeriveFrom "OPTIONAL:System.Exception"

    // Defining your own exception makes that exception type part of your object model. 
    // This means it will require a different exception type to cross the isolation 
    // boundary, and adapting logic on either side to do the conversion. If at all 
    // possible, you should use one of the existing framework exception types.


    // <Name>View types should not be marked static</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyViewAssembly" WHERE 
      
    IsStatic 

    // The state of static types is stored per AppDomain, and changes to state 
    // in one AppDomain are not reflected in others. Static types in the view 
    // are only OK if they provide simple helper functionality, and are stateless.


    // <Name>View types should not inherit from FrameworkElement</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyViewAssembly" WHERE 
      
    DeriveFrom "OPTIONAL:System.Windows.FrameworkElement" 

    // Building adapters for types that inherit from FrameworkElement is difficult, 
    // since you have to hand-code them..


    // <Name>View types should not inherit from Control</Name>
    WARN IF Count > 0 IN SELECT TYPES FROM ASSEMBLIES "MyViewAssembly" WHERE 
      
    DeriveFrom "OPTIONAL:System.Windows.Forms.Control" 

    // Building adapters for types that inherit from Control is difficult, 
    // since you have to hand-code them.


    // <Name>Types used in views should not implement IContract</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyViewAssembly" AND
      
    Implement "OPTIONAL:System.AddIn.Contract.IContract"

    // Exposing a contract directly in the view can cause a variety of problems. 
    // It makes it very difficult to version over time since it strongly binds 
    // the consumer of the view to a particular version of the contract assembly.


    // <Name>Concrete reference types used in views should be serializable</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyViewAssembly" AND
      
    IsClass AND
      
    !IsSerializable 

    // Concrete types are very difficult isolate down the road. They should only 
    // be in this assembly if they inherit/implement an abstract base class or interface 
    // in this assembly. Other types should reference the abstract base class or 
    // interface, and not the concrete helper type.


    // <Name>Concrete reference types used in views should belong to a framework assembly</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyViewAssembly" AND
      
    IsClass AND
      
    !FullNameLike "^System." // The namespace name must begin with System. for framework type.

    // Concrete types are very difficult isolate down the road. They should only be in 
    // this assembly if they inherit/implement an abstract base class or interface in this 
    // assembly. Other types should reference the abstract base class or interface, and not 
    // the concrete helper type. You can pass types across the boundary directly only if 
    // they are serializable and can be loaded in both sides of an isolation boundary. 
    // This is only true for serializable framework types.


    // <Name>Events should be of the generic type EventHandler<T></Name>
    WARN IF Count > 0 IN SELECT FIELDS FROM ASSEMBLIES "MyViewAssembly" WHERE
      
    IsEventDelegateObject AND 
      
    !IsOfType "OPTIONAL:System.EventHandler<TEventArgs>"

    // There are no tools that automatically generate adapters for events other
    // than EventHandler<T> which would make building adapters for this member 
    // very difficult.

    // <Name>IList<T> should be used instead of arbitrary ICollections</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyViewAssembly" AND
      
    (Implement "OPTIONAL:System.Collections.Generic.ICollection<T>" OR
       
    Implement "OPTIONAL:System.Collections.ICollection")
      
    AND !FullNameIs "OPTIONAL:System.Collections.Generic.IList<T>"

    // There are no pre-built contracts or adapters for collections other than IList<T>. 
    // Thus, if you use these you will have to write your own adapters.


    // <Name>Non-concrete types used in views should be defined in the view assembly</Name>
    WARN IF Count > 0 IN SELECT TYPES OUT OF ASSEMBLIES "MyViewAssembly" WHERE
      
    IsDirectlyUsedBy "MyViewAssembly" AND
      
    (IsInterface OR IsAbstract)

    // If you are using an interface/abstract base class defined in a different assembly,
    // you need to ensure that that type follows the same rules that govern types in this assembly.


    // <Name>Serializable types used by views should be defined in a framework assembly</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyViewAssembly" AND
      
    IsSerializable AND 
      
    !FullNameLike "^System." // The namespace name must begin with System. for framework type.

    // If you intend to serialize this type across an isolation boundary, you need to be 
    // sure that the type is going to be available to load in both sides of the isolation 
    // boundary. This is hard to ensure if it is not a framework type.


    // <Name>View members should not have open generic parameters</Name>
    WARN IF Count > 0 IN SELECT METHODS FROM ASSEMBLIES "MyViewAssembly" WHERE
      
    IsGeneric 

    // There are no tools to automatically generate pipelines for open generic types. 
    // Thus, if you decide to isolate this type later you will have to do the adapter 
    // logic by hand.


    // <Name>Views should not use a type defined simply as System.Object</Name>
    WARN IF Count > 0 IN SELECT TYPES WHERE
      
    IsDirectlyUsedBy "MyViewAssembly" AND
      
    FullNameIs "System.Object"

    // Defining the type as System.Object makes it very difficult to ensure that
    // it versions well over time. If this is intended to represent an arbitrary 
    // contract type, you should use a more specific type instead.


    // <Name>View members should not be marked static</Name>
    WARN IF Count > 0 IN SELECT FIELDS FROM ASSEMBLIES "MyViewAssembly" WHERE
      
    IsStatic 

    // The state of static types is stored per AppDomain, and changes to state 
    // in one AppDomain are not reflected in others. Static types in the view are OK 
    // as long as they provide simple helper functionality and are stateless.


    42 on 49 rules can readily be expressed with CQL. Here are the ones that cannot be written with the current version of CQL :

     

    (Contract)Arrays should not contain types that implement IContract ; (Contract)Arrays should only contain serializable types ; (View) Arrays should only contain serializable types : While you can constraint some methods to use some array or not with some regex on method signature and the term [], CQL cannot yet constraint element types of an array.

    (Contract)Contract interfaces should not implement non-IContract interfaces : This rules implies 2 composite queries, the first one to get contract interfaces and the second one takes the result of the first one and check if it implements non-IContract interfaces. Composing queries this way is not yet possible with CQL but it is a major feature for the future.

    (View) Concrete Reference Types Should Derive From A View Type Defined In The Current Assembly : Here also this rule need 2 composite queries to be implemented.

    (Contract)Contract assemblies should not define events ; (View) Events should only be declared on Interfaces, not AbstractBaseClasses: While you can check if a class or a structure define some event by checking fields with the condition IsEventDelegateObject, CQL cannot so far check if an interface define some events.
     
  • Are you sure added and refactored code is covered by tests?

    I would like to make a point here on the fact that NDepend can answer what is maybe the most important question for an agile programmer:

    Does added and refactored code is covered by tests?

    There are 4 CQL default rules to address this need:

    // <Name>Method where code was changed partially covered by tests</Name>
    WARN IF Count > 0 IN SELECT METHODS WHERE 
      
    PercentageCoverage < 100 AND PercentageCoverage > 0 AND
      
    CodeWasChanged 
      
    ORDER BY PercentageCoverage DESC , NbLinesOfCodeCovered , NbLinesOfCodeNotCovered 
     

    // <Name>Method added partially covered by tests</Name>
    WARN IF Count > 0 IN SELECT METHODS WHERE 
      
    PercentageCoverage < 100 AND PercentageCoverage > 0 AND
      
    WasAdded  
      
    ORDER BY PercentageCoverage DESC , NbLinesOfCodeCovered , NbLinesOfCodeNotCovered 

    // <Name>Method where code was changed not covered at all</Name>
    WARN IF Count > 0 IN SELECT METHODS WHERE 
    PercentageCoverage == 0 AND CodeWasChanged ORDER BY NbLinesOfCode DESC 


    // <Name>Method added not covered at all</Name>
    WARN IF Count > 0 IN SELECT METHODS WHERE 
    PercentageCoverage == 0 AND WasAdded ORDER BY NbLinesOfCode DESC 


    Of course, these rules can be refactored at whim, for example, to have just one rule that tells about all added or refactored method that are not covered by tests:

    // <Name>Method added or refactored not fully covered by tests</Name>
    WARN IF Count > 0 IN SELECT METHODS WHERE 
      
    PercentageCoverage < 100 AND
      
    (CodeWasChanged OR WasAdded)
      
    ORDER BY PercentageCoverage DESC , NbLinesOfCodeCovered , NbLinesOfCodeNotCovered 

     

    I wanted to make this point because I figured out that my friend Jason Gorman, which is certainly an advanced NDepend user, wasn't sure about this possibility (Identifying High Risk .NET Code With NDepend).

     

    Why knowing if added or refactored code is covered is so important? Because if you can answer yes each time you are about to release a new version you'll end up after N iterations (N not too large) with a code well tested. As a consequence the number of bug will decrease, iterations after iterations, and a lot of time spent doing manual testing will be saved.

    Is it pure theory? I can't answer, my common sense say yes it works, however I am not a theorist but a programmer. But I can attest that since we applied this methodology on the development of NDepend, the number of bug reported has decreased dramatically and our productivity increased significantly.

     

    Concretely, these CQL rules sits on 2 features of NDepend that are:

    Build comparison: the ability to compare 2 builds and to ask for which part of the code have been added/refactored/removed.

     

    Test coverage data import from NCover™ or VSTS™ the possibility to ask for code fully, partially or not covered by tests.

    And as all NDepend features, this possibility to know about refactored/nont-tested code can be harnessed from the VisualNDepend UI but also from the CI build process by customizing NDepend project properties, as shown below:


     

     

  • An Amazing Introduction to NDepend

    Andre Loker just published an amazing introduction to NDepend on its blog. Most of features are introduced with some methodology reminder about why it is useful. Very nice job!

    Such introduction is welcomed. Indeed, something difficult in promoting a tool such as NDepend is to educate about what it can bring to your development shop in terms of agility. NDepend comes with a set of innovative features currently not supported by any other .NET tool. I like to think that what tools such as ReSharper or CodeRush are doing to your code at micro level (i.e methods' body structuring), NDepend does it at macro level (i.e class, namespace, assembly structuring). Hence, as a developer I personally use both kind of tools to automatically control every aspects of the code base I am working on.

    It seems that the direction taken by NDepend is a promising one since the future Microsoft Oslo will support some similar features such as Architecture Explorer / Dependencies Matrix. Although this could be considered as a threat for the future of NDepend, my opinion is that it is a bless both because the Oslo schedule lets enought time to continue innovating and choose complementary directions, and because this will de-facto educate massively developers/architects about the usefulness of such tooling.

     

     

     
     

     

  • Some RichTextBox tricks

    I have recently been responsible for refactoring the Code Query Language query editor in NDepend to fix some imperfections.

     

     

     

    The CQL query editor implementation is based on a class derived from the System.Windows.Controls.RichTextBox class. It was the opportunity to learn some tricks that I would like to share in the current post.

     

    Text Coloring

    If you google how to color the text displayed in a RichTextBox, you’ll certainly end up using the coloring selection trick, using the RichTextBox method Select() and properties SelectionColor, SelectionBackColor:

     
    Using a search engine is very misleading here. We end up to the conclusion that this approach comes with extremely bad performance, even on short text with just dozens of word to color.

     

    A much better way we found is to use the Rtf / Rich Text Format capabilities of the RichTextBox. You just need to format a rtf string and use this code:

     

    I won’t detail the Rtf format here. The Rtf string for the query above looks like:

     

    Notice that using the SelectedRtf property is also a good way to prevent improper formatted text copy/pasted from Microsoft Word or a Browse for example.

     

    Avoid flickering problem

    When you update the content of your RichTextBox, you’ll certainly notice some pesky flickering. Hopefully, an efficient solution to this problem can be found here. Basically the solution consists in disabling text redrawing by calling some win32 APIs:

     

    Testing for ScrollBars’ visibility

    After looking for a way to test if the RichTextBox’s ScrollBars are visible or not, the only way I found is to infer this information from the delta between this.ClientRectangle and this.Size. This is certainly not the cleanest way but it is working well in every context I tried:

     

    Get/Set the ScrollBars’ positions

    To achieve this I came to the conclusion that it must be done throught the good-old win32. Being able to get and set the ScrollBars’ positions is especially useful to avoid some pesky automatic RichTextBox content re-locating I notice in some circumstances, such as inserting or modifying a long text. Here is the code:

     

    Url Detection

    Something that I wasn’t aware: if you want to display Urls that can be clicked in your text box, just set the RichTextBox.DetectUrls property to true. You can then use the RichTextBox.LinkClicked event to handle the url click.

     

     

More Posts

Our Sponsors