Feeds:
Posts
Comments

Archive for the ‘.NET (C#/F#)’ Category

What are Coded-UI Tests?

Coded-UI tests provide a mechanism to automatically execute and validate a test case or workflow that operates at user-interface layer. These tests “drive” the application like a normal user with a keyboard and mouse.

The tests are created in Visual Studio Ultimate or Premium edition and have been supported since VS 2010. A good description of these tests is detailed here.

In this article we’ll look at creating a Coded-UI test for a common S3D task – Refreshing a session file. It will be a step-by-step walkthrough, so I would suggest having VS 2010+ and S3D ready so that you can follow along.

Phase 1

Start SmartPlant-3d and define your workspace using your filter. Then click on File->Save, and save your session file to a desired location. Here I’m just saving it to the Documents folder.

 

image

 

Phase 2

Start Visual Studio with admin option and choose to create a Class library project of type “Test” and then select “Coded UI Test Project” as the template to use.

image

At this stage you will be prompted with a dialog asking about how to create the UI test. As we will be recording actions, choose the “Record action” option.

image

This will start the “UIMap – Coded UI Test Builder” and it will show up as a small tool pallet in the lower right corner of the screen. This tool allows you to record actions like clicking buttons, typing text, etc. and for identifying control and their properties.

image

Phase 3

Now click the red record button and the tool will change the button to indicate that it’s recording actions. Do note that you should be careful to record only actions that are absolutely required and not pollute the recording with unneeded actions/gestures/etc.

Now click on Start (Windows Start) menu and then click on SmartPlant 3D link as shown below:

image

At this stage S3D will load up and “New” template dialog will show up as shown below:

image

Move the mouse to the UI Builder and click on the first icon from left to pause the recording. Next click on “Recorded Actions” icon and you should see something like this:

image

If you see any extra recorded steps, click on it and then press the Delete key to remove the action. Let’s go ahead and create a method for these set of actions and call it “LaunchS3D”.

Phase 4

Click on the “Generate Code” icon and type in the name, “LaunchS3D”, along with a  method description and then click on “Add and Generate” button:

image

Visual Studio will generate the action method and switch back to UI Builder so that you can start recording the next set of actions.

Phase 5

So at this stage we have a method to launch S3D and show the “New” template dialog. So now we need to click on the top-right “X” in this dialog to close it.

In other words, we need a way to identify the “X” control. The way you do in Coded-UI test is to “add” the “X” to it’s “UI Map”, so that VS can create a representation of that control in code.

So go ahead and move your mouse over the “X” to highlight it and then press CTRL + I and you should see this:

image

You can see that the UICLoseButton control has been identified by VS and added to it’s UI MAP. Also you can see that UINewWindow is the parent of UICloseButton. So if we need to close the UICloseButton, we need to make sure it “it” exists or it’s parent, UINewWindow exists.

We can add such a check in VS by using an assert. So click on the UINewWindow->UINewTitle control and then click on the Exists property. Then click on “Add Assertion”, so you should have something like this:

image

image

Click on Ok and the click on “Generate Code” link and type in a name for the assert method, say “CheckNewTemplateWin”:

image

This will generate the code for our assertion, “CheckNewTemplateWin”.

Now click on the first button to start recording and then move mouse over the new template dialog and click on the Close “X” button.

Then click on “Recorded Steps” and you should see this:

image

Go ahead and click on the “Generate Code” and then create a method called “CloseNewTemWin”

image

So at the end of Phase 5, we have created method to start S3D, check for new template dialog, and finally, closes the dialog.

Phase 6

Now within S3D, press the keystrokes CTRL + O which should bring the “File Open” dialog and then click on “Recorded Actions” in UI Builder and you should see this:

image

In S3D, press ALT + N to focus in the “File name” dialog and type in the path to you saved session file. Press “Enter” to start loading the session file.

At this stage, click on “Recorded Actions” in the UI Builder and you should have something like this. Again if you see an extraneous actions in the recorder, go ahead and delete them:

image

After your session file loads up, go back to the UI Builder and pause the recording. Then click on “Generate Code” and create a method called “LoadSessionFile” for the set of recorded actions. Make sure to click on “Add and Generate” to have Visual Studio create the method within your project.

Phase 7

So let’s recap what we have now. We have a method to start S3D, one to check the new-template dialog and close it and lastly one to load the session file. So what we need now is to record the actions to refresh the session file and then save it.

So start recording and click on the “Select Command” arrow in S3D and then press F5 key to start refreshing the file.

You should see these set of actions recorded:

image

Now switch back to S3D after making sure that the recording is in recording state image, and then click on the top-right “X” in S3D to close it. When the save session file dialog comes up, don’t dismiss it by clicking on the “Yes” button.

Click on the “Show Recorded Steps”, icon in UI Builder and you should see the following, again delete any extraneous actions:

image

At this stage, let’s generate a method for these set of actions which is – clicking on the top-right “X” or close button in S3D window. So click on “Recorded Actions” first, then fill in the method name and next click on “Add and Generate” to generate code for the method.

image

Phase 8

Now we need to code the method to click on “Yes” button to save the refreshed session file. So we need to capture the “Yes” button in VS UI Map. So like before, first pause the recording, then hover your mouse over the “Yes” button and press CTRL + I to get something like this:

image

Since we want the “Yes” button to be visible before we click on it, let’s add an assertion for that check. So click on the “Exists” property and then click on “Add Assertion”

image

Then close the UI Map and click on “Generate Code” button and finally generate the code for the assertion.

image

Now we will add the code for the click action on the “Yes” button. Make sure recording is on and then click on the “Yes” button which will cause S3D to save the session file and exit.

Now click on “Recorded Actions” and you should see this:

image

At this stage go ahead and click on “Generate Code”, then give the method a name and create it within your project:

image

This should complete our action recording for this use case. You can now close the UI Builder and switch to Visual Studio.

Phase 9

If you switch to Visual Studio, you should see something like this. The generated “TestMethod” lists all the asserts and methods that we created during this exercise.

clip_image002

The file, CodedUITest1.cs, which contains these methods is also highlighted in the Solution Explorer. There is another file called, UIMap.uitest, in the Solution Explorer that contains the UI Map that we worked with so far.

Do this, double-click on UIMap.uitest and you should see something like this:

clip_image004

On the left you can see all the actions and asserts and on the right you can see the UI Map that you worked with.

Go ahead and start S3DHost by double-clicking on the session file. Then press Alt+F4, to bring the “Save Session File” dialog. At this stage, shift focus to Visual Studio, right-click on “UIYesButton” and choose the menu, “Locate UI Control”.

clip_image006

You should see focus shift to S3DHost window with the “Yes” button highlighted as shown below:

clip_image008

So this confirms that our UI Map is correct and we are able to locate the control that we stored a reference to during our recording session.

Phase 10

Let’s now try to play or test our recorded actions. So double-click on CodedUITest1.cs to bring the code window to front. We now need to build the solution, so click on Build->Build Solution. Alternatively, you can use the F6 key which is the shortcut key to build your solution.

Once that’s done and you get a message in the status bar saying “Build succeeded”, click on the menu Test->Windows->Test Explorer, to show the test window.

clip_image002

You should now see something like this:

image

You should now be able to, either click on one of the “Run” menus, or right-click on your test method and choose “Run Selected Tests” to invoke the actions that you recorded.

So go ahead and choose one of the options and you see S3DHost start up and go through the recorded actions. If all the actions succeed, you should get a green check next to your test as shown below.

image

To make sure that the actions do work, you can try to place some objects in S3D, run the tests and finally open the session file to see if the new objects do show up.

Testing From Command Line

You can use MSTest.exe and VSTest.Console.exe (for VS 2012 plus) to run coded ui test. These are located under Visual Studio installation folder under the following path:

clip_image002[6]

So go ahead type in cmd in the windows start dialog. Then press the Shift key and right-click on cmd to choose “Run as administrator”

clip_image004[5]

Navigate to the MSTest.exe folder location and then type in MSTest /? ,to list the options available with the tool.

We will use: /testcontainer:path_to_our_dll /resultsfile:path_to_results_file

The dll file is the Dll created by your project and resides in the Debug (or Release) folder. You can see that location by right-clicking the project in Visual Studio and choosing “Open Folder in File Explorer”

clip_image002[6]

Then you can navigate to the bin\Debug folder and copy the path to the directory.

clip_image004[7]

Once you run the test, it will create the results file and output the end result onto the DOS window, as shown below.

clip_image006[4]

 

If you stayed with me and made it this far Smile, then you should have a fairly good idea about the capabilities of Coded-UI Test. Hopefully you can leverage it’s abilities and use in creating automated test for your functional testing workflows.

Advertisements

Read Full Post »

As you all know, with the current influx of iPads, iPhones, Galaxy SIII, or in short, mobile devices, into our life & work, we have become accustomed to the use of an “app”, to help us fulfill our daily activities.  As such expecting a mobile interface or app for a desktop application is the expected norm in these days.

With that in mind, I have been working on a mobile interface to SCA and have finished the first set of requirements, that I had in mind. I have opted for an HTML5/JavaScript framework since that frees me from worrying about app submission and learning the details of each SDK (iOs/Java). Additionally, I can host the files on a Web Server so all updates are in place and the user just refreshes the web-app’s URL, in the browser (Safari/Chrome), to get the latest changes.

The app makes use of existing SCA CmdLets like, Get-SCAComputerInfo, Get-SCADbServerInfo and Get-SCAPerfCounters, which execute on the web-server and return data in JSON format which is consumed by the app. It consists of 2 layers:

  1. Web/Data Layer – Done in ASP.NET MVC 3. This executes the SCA CmdLets and returns data in JSON format
  2. UI Layer – Done in JavaScript framework, Sencha Touch 2.2. This consumes the data and displays it.

Something like this:

IISSetup

This is how the current screens looks when run on an emulator:

  1. Start Screen – Lists all SCA servers registered on the IIS serverSCAServers
  2. Choose Info Screen – Allows choosing Server or Plant info

           choosePanel

  3. OS Info Screen – Loaded after clicking the disclose link for a server recordLoading

    OSInfo

  4. User Info Screen – Shows a grouped list of active users on the SCA server

    userinfo 

  5. Performance Info Screen – Shows data on OS & MSSQL counters with visual indicators on the counter state – normal or critical. Tapping on an entry shows detailed description of the counter

    perfinfo

  6. Input Plant Info – On selecting Plant Info  

    inputPlantInfo

  7. Plant Details – Symbols share, dates on when reports & catalog views were last regenerated, etc.

plantDetails

  1. IFC information – State of IFC service, % completion, etc..

    plantIfc

  2. Database information  – Grouped by DbType and listing size, version and name-gen server information

    plantDbs

     

  3. Check Name-Generator – This tab allows you to test the name-generator associated with the current plant. If successful, you get a naming-count back otherwise you get an error message as shown in cases below:

nameValid

nameInvalid

There maybe some modifications as we try to finish this. Depending on user feedback and scope, we may add additional features/screens, so your comments will be helpful here.

Read Full Post »

In this post, I want to show a quick and easy way to parse an MDR log using LINQ. If you haven’t heard of LINQ, then most likely you are still stuck in the ancient world of .NET 2.0 world and missing all the fun (:- Basically it’s  set of features added in .NET 3.5, that add very cool, query capabilites to the underlying languages – C# and VB.NET. You can now filter data using standard query operators like Select, Where, OrderBy, etc., which your already are used to, when writing SQL statements.

Refer to the file, linqMDRSource, at the end of the post, for sample data.

As such using LINQ, I was able to quickly aggregate data in an MDR log. This is what the final output looks like:



If you look into an MDR log, there is an XML element called Partitions, which has a bunch of child elements, Partitions. These store a few important child elements, like Object, which points to the entity type, and ElapsedTime, which stores the time taken to copy/clone the entity. These are the fields we are interested in:



So what we want to do here is:

  • Group all the similar classes, like ‘Pipe Run’, ‘Pipe Spool’, together
  • Aggregate the ElapsedTime attribute of each group
  • Finally dump out a list that showed the class Name, Count, ElapsedTime & Avg.

So taking the above e.g., the output would be something like:

Class Count Elapsed Time Avg. Time
Pipe Run 3 2.12 0.71
Pipe Spool 1 0.094 0.094

So let’s see how we can quickly query such XML data. We will be using LINQPAD, so get the .NET 3.5 version from here. Now open LINQPAD and under the menu Query->Properties, make these changes:

  • Add this under Additional References – System.XML.dll, Click on Add on this form.
  • Add this under Additional Namespace Import – System.Xml.Linq, System.Linq. Click on “Set default for new queries” on this form

Then Click on File->New Query and set the Language to C# Statements.
Now lets take the xml fragment and make it a variable called data. We need to escape the double-quotes in attributes with another “, as the double-quote is used to specify strings in C#.
Once we have the string-xml, we can get the XML type from it by using the XElement.Parse() methodw which returns an XElement object. To get all the Partition elements, we use the Descendants method, that takes an element name to search for. We can then use the LINQPAD method, Dump, to dump out our object obtained so far. This is a nice builtin method to obtain a string representation of any object.

var xmlDoc = XElement.Parse(data);
var partitions = xmlDoc.Descendants(“Partition”);
partitions.Dump();

So we have been able to obtain the XML elements we are interested in. Now let’s tackle the next part – getting all the class names.

//First get the all the attributes
var attributes = partitions.Descendants("Object").Attributes("Class");
attributes.Dump();
//Then get the distinct values of the attributes
var classNames = attributes.Select (a => a.Value).Distinct();
classNames.Dump();

So we get the Attributes of name “Class”, then we select the value of those attributes and finally we get the Distinct values from the the list.

So we have a list of all class names. We now need to get all the related partitions for each class names:


foreach (var cl in classNames)
{
// Find all the Partition Elements Where Any of it's Descendants
// "Object" elements->"Class" Attribute value is
// Equal to the current class
var relatedPartitions = partitions.Where(p=> p.Descendants("Object").Any (x =>x.Attribute("Class").Value==cl));
//The Dump method can take a title. Here we use the class name as title
relatedPartitions.Dump(cl);
}

So we now are able to get each class and the partitions under which it exists. Now we need a count of each class. So for our e.g. it should be 3 for Pipe Run and 1 for Pipe Spool. So add this code to the existing code:


// Find the count of each class - Should be 3 for Pipe Run and 1 for Pipe Spool
// So we need to select all the "Object" elements using SelectMany since
// each partition gives a List, all partitions will give List of Lists.
// We can use the LINQ SelectMany operator to flatten the list
int clCount = partitions.SelectMany(p => p.Descendants("Object"))
.Where (p => p.Attribute("Class").Value==cl).Count();
clCount.Dump(cl);

We can confirm the output at this stage:

We have one more element to select now, ElapsedTime. Since we already have the “related partitions”, we can just select the ElapsedTime->Attribute(“Seconds”).Value. So add this code


// We select a partition, get the value of ElapsedTime attribute and
//convert it to decimal.
// So we get a List since a class, like Pipe Run
// can occur in multiple partitions.

var elapsedTimes = relatedPartitions.Select (p => decimal.Parse(p.Element("ElapsedTime").Attribute("Seconds").Value));
elapsedTimes.Dump(cl);
// We use the Sum() method of LINQ to
//sum the values in each list
var elapsedTime = elapsedTimes.Sum();
elapsedTime.Dump(cl);

So the output at this stage:

Adding average is simple, since we already have the count and total time.

var avg = Math.Round(elapsedTime/clCount, 2);
avg.Dump(cl);

Since our test xml data works fine, you can now comment the first set of lines by selecting and using the right-click menu option “Comment selected lines”. Then add the this line to load data from an actual file


//Change path to your file
var xmlDoc = XElement.Load(@"D:\Temp\Mdr\Mdr.log");

Everything else should remain the same. The final output is shown below.
As you can see, LINQ (with LINQPAD), provide us with a simple & elegant way to query XML data using familiar SQL keywords that we all have used one time or other. Use the file below (linqMDRSource) for source code.

linqMDRSource

Read Full Post »

The other day I was trying to update some data in Oracle in a  transacted fashion and I kept running into this error:

Unable to load OraMts

Googling later gave me some links with workarounds that I tried to no avail. Finally I did find out the solution which took a lot of trial-error and is presented here so that someone else doesn’t go thru. the pain & hassle that I had to endure.

  1. Check your ODP.NET and OracleClient installation. In my case, my initial OracleClient installation was in 10.2.04 while the ODP.Net->Oracle.DataAccess.dll (2.111.7.20), I was using was from 11.1.0.7 package.
  2. Check if you \bin folder contains the oramts.dll or is in the %PATH%. Since I was using 11.1.0.7 Oracle.DataAccess.dll, I had to copy the oramts11.dll (not the oramts.dll) from under ODP.Net installation path (specifically under ..ODP.Net\11.1.0.7\oramts\bin) to \bin folder of my app and then rename it to oramts.dll

Hope this proves helpful to someone else.

Read Full Post »

I recently ran into the issue with synchronization log where it was reporting messages on missing reference files, something like this:

Error Desc : Unable to open file: ABC-3D.dgn (\\SomeServer\SomeShare\CAD Reference\DGN\PIPE\ABC-3D.dgn)

There were quite a few instances of this error and I needed to build a list of the filenames for further processing. Since I recently started learning F#, I thought it would be a good exercise to do this in it. One really good part of  F# is – the interactive window, which lets you test code snippets without creating a full-blown .NET program, and that alone is a time saver.

This problem had basically two parts:

  • First would be to find a way to parse the file so that we could get the line of interest (as shown above)
  • Second would be to output the list – either write to a file or show on screen

Since I needed to parse the file, I resorted to using a regular expression as that was the easiest way in this case. So testing with Expresso, I came up with this expression:

Error\s+Desc\s+[:]\sUnable\s+to\s+open\s+file[:]\s+(?<filename>.*)\s+\((?<filepath>.*)\)

And as you can see, it gives me the values I need in “filename” and “filepath” groups.

I needed to now return a list of such matches, so enter – GetParsedLines:

open System
open System.IO
open System.Text
open System.Text.RegularExpressions
open System.Diagnostics

let SyncLog_Missing_RefFile =
@"Error\s+Desc\s+[:]\sUnable\s+to\s+open\s+file[:]\s+(?<filename>.*)\s+\((?<filepath>.*)\)"
let GetParsedLines (fileToParse:string) (regExp:string) =
  seq {
    use reader:StreamReader = new StreamReader(fileToParse)
    while not reader.EndOfStream do
      let curLine = reader.ReadLine().Trim()
      let result = Regex.Match(curLine, regExp)

      if result.Success then
        match (List.tail [for g in result.Groups -> g.Value]) with
          | [] -> yield ["No groups found"]
          | lst -> yield lst
      else
        ()
  }

What this does is, it first tests if the current line matches the regular expression, if so,  it puts the group’s value in a list ([ for g in result.Groups -> g.value]) , then it omits the first entry in the list by taking the “tail” of the list since the first or zeroth group represent the entire match. After that if there are values (| lst -> yield lst), it yields or returns it.

Let’s test this: so fire up the Interactive window, from View->Other Windows->F# Interactive. Then highlight everything with the ALT key pressed, from the line, “open System“, till the last “}” in GetParsedLines, so you have something like this (the background color is light grey in my case):

Then right-click and select “Send to Interactive” and you will get the output in the Interactive window:

To test it, you only need to supply the path to a synch log which has errors related to missing reference files and use the regular expression, SyncLog_Missing_RefFile, that we defined above:

>GetParsedLines @”C:\Users\Public\Documents\synch.log” SyncLog_Missing_RefFile;;

And here’s the output:

> GetParsedLines @”C:\Users\Public\Documents\synch.log” SyncLog_Missing_RefFile;;val it : seq<string list> =

seq

[[“trenches-405.dgn”;

“\\SomeServer\SymbolShares\CBI\PROJECT\3D CAD Reference\3DDGN\CIV\trenches-405.dgn”];

[“trenches-300.dgn”;

“\\SomeServer\SymbolShares\CBI\PROJECT\3D CAD Reference\3DDGN\CIV\trenches-300.dgn”];

[“trenches-400.dgn”;

“\\SomeServer\SymbolShares\CBI\PROJECT\3D CAD Reference\3DDGN\CIV\trenches-400.dgn”];

[“230-3D.dgn”;

“\\CustomerServer-ap1\ACBD\PROJECT\3D CAD Reference\3DDGN\PIPE\230-3D.dgn”];

…]

>

With this in place, the only thing remaining was is to add code to return a list of such matches and open it in notepad for review. Here’s that part:

let ParseFile (fileToParse:string) (regExp:string)  = 
  if not (File.Exists(fileToParse)) then failwith (sprintf "Invalid filename: %s" fileToParse)
  let results = Path.Combine(System.Environment.GetEnvironmentVariable("TEMP"), "ParseFileResults.log")
  use swr = new StreamWriter(results)
  (sprintf "Parsed lines from file %s\r\n" fileToParse) |> swr.WriteLine  
  GetParsedLines fileToParse regExp 
    |> Seq.iter (fun e ->  String.Join(",", List.toArray(e)) |> swr.WriteLine )

  Process.Start(@"C:\Program Files (x86)\Programmer's Notepad\pn.exe", results) |> ignore

To test this, just ALT-Copy the section above, right-click and “Send to Interactive”. Then use it by supplying a synch log and the regular expression to the ParseFile function:

In next part we will look at how to search for these files on the file server share and later on how to update the database with the new paths.

Read Full Post »