Contents tagged with Cecil
Just uploaded the latest release of SequenceViz which now includes the Reflector plugin with the WPF renderer.
This is the third part of the series on building CodeStore. In this blog post, I'll finish off the first user story which was
1) As a customer, I want to query the Assembly data in the database so that I can get metrics about the assembly.
The story is further decomposed into two tasks:
a) Read Assembly Data
b) Store Assembly Data in SQLite using Nhibernate.
We start with completing the unit test for AssemblyDataRepository. If you remember in Part II that I changed the Nhibernate integration to use Castle Nhibernate repository and therefore passing in the ISessionManager to the repository classes.
So, to test AssemblyDataRepository I'm going to mock ISessionManager to test the behaviour of the method. As this project is about learning so I've decided to use Moq (http://code.google.com/p/moq/) as the mocking container.
Moq uses lambdas to set expectations which takes some practice to get used to. You can read more about Moq on Daniel Cazzulino blog (http://www.clariusconsulting.net/blogs/kzu/).
So we start with a test for adding assembly data using the repository.
It uses the same test flow as other testing (mocking) frameworks.
Setup mocks, expectations
Verity the expectations
Once the test is passed, we can check off the second task of the story. I can write few more tests but let's not worry about that for now. The next thing to look into is actually parsing the assembly and extracting the required information from assembly metadata. To read IL, I'm going to use Cecil which can be downloaded from http://www.mono-project.com/Cecil.
The initial model for code analysis engine looks like this. (Please note that the following diagram is the snapshot taken at the end of this blog post)
I started with CodeAnalysisEngine whose job is to ask the CodeAssemblyLoader to load the assembly and then pass it to CodeAssemblyParser to do the actual information extraction.
The first two statements create the mock objects to use in the actual call. The next line sets the expectation to load the provided assembly file. The implementation of the SUT:
In the next test, I added the assembly parser in the constructor which prompts a change in the first test to take in another argument.
And the implementation now calls the Parse method on the assembly parser.
The next step is to unit test the CodeAssemblyLoader. This class is responsible for loading the assembly file using Cecil, so the test expects a call to Cecil's AssemblyFactory.GetAssembly method. Unfortunately AssemblyFactory.GetAssembly is a static call which makes it impossible to test. If it is Ruby then I can easily mock it but that's another thing :).
And, here is the implementation of CodeAssemblyLoader class
Testing CodeAssemblyParser is also not easy because of the same AssemblyFactory issue so I'm going to load the actual assembly but mock the repository to expect the add method.
Line no. 17 is loading the actual assembly. Then I mocked the repository and set it to the assembly parser. In the end, I set the expectation and call the method to verify. The Parse method internally calls the AddAssemblyData method with the assembly definition.
So the unit tests are passing, I still need to implement the dependency injection configuration to run the application.
Setting up Castle configuration for dependency injection:
There are two different dependencies which we need to resolve here. First, CodeAnalysisEngine depends on the CodeAssemblyLoader and CodeAssemblyParser which it takes in as constructor arguments.
Second, to resolve the dependency in assembly parser for assembly data repository which is a property.
After configuring the dependencies, I can run the application select the TestCases.dll file which we created in part II.
To confirm if it works by opening the codestore.db file in the database browser.
Source Code: http://code.google.com/p/code-store/
So, this completes the first story which is about storing the information about the assembly into the database. I also looked into interaction based testing and used the Moq framework to implement mocks inside the tests. The assembly parsing is then implemented using Cecil.
As always, your suggestions and comments are always welcome to improve the project.
Finally I managed to put it on CodePlex.
Update: SequenceViz for Reflector
SequenceViz can now show dependencies between different modules. It is probably not as good as some other proprietary softwares but it is fine for a start and I'm hoping to add more to this in the next release.
As always, here is a screenshot for the assembly dependency view.
Here is another screen shot of the type dependency graph
You can see that the interfaces are shown in a different way to make it more obvious. The anchor direction is towards the interface to illustrate that the type implements the interface. In this version, only the base type and the interfaces are classified as dependencies. I looking to include the private/public fields into that list as well; which will then show the true picture of the selected type.
In some cases, you will see clutter like the following due to the large number of tree nodes.
Here you can either Zoom (Ctrl + Mouse Wheel) to see this view in more detail or you can filter out specific type of items by using the options on the top of the screen. Here is the same diagram with types filtered out.
Currently, it is also not possible to filter out individual types from the user interface. In case you want to remove any individual type(s) then check the IgnoredTypeList property in the LiveSequence.exe.config file. The default setting looks like this.
<add key="IgnoredTypeList" value="Form|Type|Control|ControlCollection|String|Assert"/>
Hopefully, I'll also try to include filtering directly from the user interface. As always, try this out and see if you can get any benefit from this. I'll appreciate your comments or suggestions.
** I have no problem if you are willing to add this to Reflector. I may not help you in writing any code in that case but I'll be able to help in any other issue.
With the help of Mike and Steve, finally there are few visitors coming to the site looking for SequenceViz. For those new visitors and the existing loyal readers (if there are any :)), here is another version of SequenceViz.
A quick screenshot first.
1) Namespace Supports:
Now you can see in the assembly tree that the classes are grouped under namespaces.
2) Assembly Pie Chart:
In this version, I put a Tab control to easily add multiple views of the assembly data.
Assembly Stats is a new view where you can see the size of assemblies and namespaces. It works when you either select Assembly or Namespace in the left hand pane and switch to Assembly Stats tab.
If you have selected an assembly then the pie charts shows the different namespaces within the assembly. The size of each pie is proportional to the size of classes in each namespace. For eg. Here is a nice view of log4net assembly. You can see that the classes are nicely distributed among namespaces.
The view changes when you select a namespace as shown below. Now it shows the different classes in the selected namespace . Similarly, the size of the pie in this case is proportional to the size of methods in each class.
I was initially thinking about adding the chart to show the number of IL count for each method. In the end, I decided not to go along that path because IL doesn't reflect the true number of lines in a higher level language and can give an incorrect picture.
Finally, a big thanks to ZedGraph for building a wonderful open source chart package which comes with excellent document even though it is really really simple to use.
I'm trying to setup a CodePlex project so that SequenceViz can get its own home, but currently there are few areas of that website not working so I'll to wait until that get fixed. In the meantime, if you would like to see anything in the next release then please leave your comments. The main theme of SequenceViz is to use visualization to help people understanding an existing project.
For comments on putting this as Reflector Plugin:
I would like to control the shell and now as I am getting comfortable with using Cecil therefore I am sorry that I won't be able to integrate this in Reflector any time soon. In addition there are some features that I want to see in SequenceViz which would not be possible without controlling the shell application.
For SequenceViz, the next thing on the list was to export the SVG output to an image. I would like it to be included within the application but unfortunately I am unable to find a working implementation which I can use. (tried SVG# but didn't wok).
There are few ways where you can take the generated SVG and convert it into a raster image manually. These options are described below:
Please note that the SVG image of the diagram is generated to a file named Seq.svg before rendering in the browser control. So that is the file you should be converting.
1) Apache Batik:
Apache Batik is a wonder Java toolkit to manipulate SVG images and it does a lot more than that. See the website (http://xmlgraphics.apache.org/batik/index.html) for more information. For our purpose, it includes a tool called SVG Rasterizer (http://xmlgraphics.apache.org/batik/tools/rasterizer.html) which can convert SVG file to a raster format. There is sufficient information on the website to use that tool therefore I would recommened you to look at the above website.
2) Inkspace: (http://www.inkscape.org/)
Inkspace is an open source vector graphics editor similar to Illustrator, Freehand and Corel Draw. It can also be also to convert SVG to other formats.
Letme know if you have more information on any other way to convert SVG into other formats.
What is SequenceViz?
SequenceViz is a tool to generate sequence diagrams by reverse engineering .NET Assemblies. Read this post for setup and installation.
The latest release has the following enhacements:
See this post for introduction to MethodViz.
For those, who don't like Python or IronPython, here is a pure C# version of Method Visualizer. The following screenshot shows it in action.