Buy Me a Coffee

Buy Me a Coffee!

Monday, February 13, 2017

Reading Structured Binary files in C#: Part 8

This post is going to follow my addition of SpecFlow tests to the DissectPECOFFBinary program.  I added the SpecFlow project and set it up to use MSTest in the previous post, so this will be pure testing.  One thing that I left out of the previous post is that you will need to install the IDE integration package as outlined in the Getting Started section of the SpecFlow site.  That done, the first thing that you need to do is get your testing artifacts in place.  In this case, they are the 22 binary files that we have been examining in the Dissecting C# Executables series.  We can add more later, but for now these should be sufficient.
  1. Adding a new folder in the project by right clicking on the project and clicking Add and New Folder
    Add new folder
  2. Name it TestArtifacts and then drag the executable files into it
    Test artifacts

Next, create a Feature file.  We will add a feature for each of the parts, that gives us the most flexibility.  Here are the steps to add the first one

  1. Right click on the project and Add a New Item
    Add a new item
  2. Choose the SpecFlowFeatureFile and name the file MSDOS20Section.feature
    Add the MSDOS20Section.feature file
  3. Fill in the feature file with a Scenario Outline as follows:
Now, we need to implement the steps of the feature.

  1. Right click in the feature file and click on Generate Step Definitions
    Generate Step Definitions
  2. Accept the defaults (they almost always are the best) and click Generate and then save the generated file
    Generate Steps
  3. Open up the new file and fill in the steps with good C# code.  Well, first we need to fix the attributes.  When the SpecFlow addin generates the attributes it looks for numbers and other parts that it believes to be arguments.  For us, we need to adjust the generated code to match this:
  4. Add a reference to the DissectBECOFFBinary project in the test project
  5. Update the first step to store the file name in the current context.  This is an important concept, your steps need to be independent, but they can assume that certain information is available in the current context.
  6. Update the second step to read in the MSDOS20Section by...what?
    OK, here is where we need to make some decisions.  Do we just add a method on the PECOFFBinary class that takes in a file name and reads the given section?  Do we add a constructor for the PECOFFBinary class that takes in a file name and then add a method for reading the struct?  Do we just open the file and read directly?  Since this is the MSDOS20Section feature, we are going to read it directly.
    Add the code to open the file and read in the struct.  You will need to change the visibility of the struct to public so that it can be used in the test project.  There are ways around this, but for now we will just go with it.
    Add the structure you read to the current context.
  7. Get the MSDOS20Section from the current context and add an assertion that the expected value matches the found value
Your code should look something like this:

Now run your test!  Wait, there is a problem!  The test failed.  Add a break point on line 36 of the MSDOS20SectionSteps.cs file and debug the test by right clicking on it and choosing Debug Selected Test.  Once you do, you can peek at the value the MSDOS20Section.Signature actually has by hovering your mouse over the property.  I pinned the value to make it easier to capture as an image:
MSDOS20Section.Signature value within a test run
Where did the \u0090 come from and how do I add it to the test?  After a  quick Bing search I found that it is a Device Control String character.  I played around a bit and found that I could type it in using the ALT+#144 method, but it doesn't show up visually in the feature file.  The test passes, but that is not maintainable.  Instead, I added an html encoded version to the Signature and used WebUtility.HtmlDecode() to process the incoming signature.  That did it!  The test passes!

But wait, there is more.  We added the OffsetToPEHeader to the Example section, but we didn't have a step to test the value.  Ok, we need to add a step.  Add a new line to the Scenario Outline, right click in the feature file again and choose Generate Step Definitions again and then Copy methods to clipboard.  If you try to Generate again, it will attempt to replace the file you have been working in.  By copying it to the clipboard, you are able to paste it in yourself.  When you paste in the method, notice that the value comes in as a string.  We are going to have to convert it to a uint before we can properly compare. We will just use a Convert.ToUInt16 and test the converted value.

That is our start.  Tons more to do, but I won't bore you with the mundane.  If something else interesting comes up I will talk about it.  I will check in what I have, passing test and all. ;-)

Keep testing!