Welcome to Atalasoft Community Sign in | Join | Help

Question: 

Wouldn't it be really nice if Atalasoft had a TWAIN device certification program?

Answer:

Sure would.

checkmark

Yep.  That's right.  Pretty soon we'll have a certification program in place for manufacturers of scanners, and other capture devices to certify their hardware  as "Plays Nice with Atalasoft Products"; although, the actual marketing message probably will be a bit more elegant than that. 

So, what is it that we're currently thinking that should be part of the certification?  This is what we've got so far all of which comes right out of the Atalasoft.DotTwain assembly,

Methods:  Acquire, BackgroundDetection, CanOpen, Close, Disable, Dispose, Enable, Finalize, GetConditionCode, GetSupportedBarCode, GetSupportedBitDepths, GetSupportedBrightnessValues, GetSupportedCapabilities, GetSupportedCompressionModules, GetSupportedContrastValues, GetSupportedImageFormats, GetSupportedJpegPixelTypes, GetSupportedJpegPixelQualityTypes, GetSupportedNativeResolutions, GetSupportedOverscanModes, GetSupportedPixelTypes, GetSupportedResolution, GetSupportedTransferMethods, GetSupportedUnitTypes, Open, LoadParameters, LoadXmlParameters, SaveParameters, SaveXmlParameters, ShowUserInterface

 Properties:  Alarms,  AutoBrightness, AutoDiscardBlankPages, AutomaticBorderDetection, AutomaticDeskew, AutomaticRotate, BarCode, Bitdepth, BitDepthReduction, Brightness, Compression, Contrast, Controller, CustomDataSupported, DeviceEvents, DisplayProgressIndicator, DocumentFeeder, Duplex, DuplexEnabled, EnableInterfaceOnly, FileFormat, FileSystem, Frame, FrameSize, InterfaceControllable, JpegPixelType, JpegQuality, LampState, Lastreturncode, MinimumHeight, MinimumWidth, NativeResolution, ModalAquire, Online, Overscan, PaperDetectable, PhysicalHeight, PhysicalWidth, PixelFlavor, PixelType, Resolution, State, ThreadingEnabled, Threshold, TransferCount, TransferMethod, Units, ReacquireAllowed

That's quite a bit, even with out getting into each of the details of each one of those methods or propeties, but before you can start, you need to find out what the attached device reports it can do.  I'm going to do this with DotTwain as it is very straightforward in a WinForm application.  

The first thing you need to do is find the attached device list for your system:

 

private void LoadDeviceNames()

{

     // Never assume that a system has any acquisition devices.

     if (!this.acquisition1.SystemHasTwain || this.acquisition1.Devices.Count == 0)          

           return;

     string defaultDevice = this.acquisition1.Devices.Default.Identity.ProductName;

     // Add a menu item for each device.

     foreach (Device dev in this.acquisition1.Devices)

     {

          if (dev.Identity.ProductName == defaultDevice)

          {

               this.device = dev;

         }

        this.comboBox1.Items.Add(dev.Identity.ProductName);

     }

     try

     {

     this.device.Open();

     TwainResolution res = this.device.Resolution;

     }

     catch (Exception ex)

     {

     MessageBox.Show("There was an exception trying to open the system      default device.\r\n" + ex.Message, "Default Device Failed");

     CertFailed();

     }

     finally

     {

     this.device.Close();

     }

}

 

In the WinForm app, I have a combobox (combobox1) that gets populated with the names of the attached TWAIN devices. When I select one of them, it fires the following bit of code:

 

 

private void button1_Click(object sender, EventArgs e)

{

     //Get the selected device's capabilities.

     string deviceName = this.comboBox1.SelectedItem.ToString();

     try

     {

          device = acquisition1.Devices.GetDevice(deviceName);

          device.Open();

          capabilities = device.GetSupportedCapabilities();

          CertPassed();

      }

 

     catch (Exception openingException)

     {

          string error = openingException.ToString();

          MessageBox.Show(error);

          CertFailed();

     }

     finally

     {

          device.Close();

     }

}

 

..Which doesn't do much yet, but is a start in comparing the list of supported capabilites reported by the device to the list of things that I think it should do; which is exactly what I'll write about next time.

A post on CRAp4dotNet is long over due -- my sincerest apologies for those of you that have been holding your breath. 

From last time I mentioned that one of the two hurdles to get this working for .Net was computing the code complexity.  There aren't any good existing open source, and free options out there in the tubes to do this so I had to code it up all on my own.  I took a bag of words approach, and decided to just parse the source files with regular expressions.  There's probably a better way to do this, but at least this way its pretty trivial to extend the complexity class to parse any language you might want... as long as you compile the complexity class in .Net -- for now anyways. 

 A quick review of what cyclomatic complexity for those that don't want to click through.

CC = E - N + p

Where E is the number of edges in the graph, N is the number of nodes (verticies) in the graph, and p is the number of connected components.  In the implementation of this you just end up counting decision points in the source.  So, every if, then, else, foreach, do, etc... you encounter you add 1 to the method's complexity value. 

The next thing that needs to be determined is the class' aggregate complexity.  To get this just divide the sum of each method's complexity value by the number of methods.

Here it is free to use under the MIT license,

/***

 

 * Copyright (c) 2008 Adam Scarborough

 

 * All rights reserved.

 

 *

 

 * Redistribution and use in source and binary forms, with or without

 

 * modification, are permitted provided that the following conditions

 

 * are met:

 

 * 1. Redistributions of source code must retain the above copyright

 

 *    notice, this list of conditions and the following disclaimer.

 

 * 2. Redistributions in binary form must reproduce the above copyright

 

 *    notice, this list of conditions and the following disclaimer in the

 

 *    documentation and/or other materials provided with the distribution.

 

 * 3. Neither the name of the copyright holders nor the names of its

 

 *    contributors may be used to endorse or promote products derived from

 

 *    this software without specific prior written permission.

 

 *

 

 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"

 

 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

 

 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

 

 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE

 

 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR

 

 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF

 

 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS

 

 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN

 

 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)

 

 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF

 

 * THE POSSIBILITY OF SUCH DAMAGE.

 

 */

 

 

 

using System;

using System.Collections.Generic;

using System.Text;

using System.IO;

using System.Collections;

using System.Text.RegularExpressions;

 

 

 

namespace complexityTest

{

    class CyclomaticComplexity

    {

        public static decimal fileComplexity = 1;

        public static decimal methodCount = 0;

 

        public static void Complexity(string path)

        {

            try

            {

                if (path.EndsWith(".cs") || path.EndsWith(".cpp") || path.EndsWith(".c"))

                {

                    ComplexityCalculator(path);

                }

                else

                {

                    DirectoryParser(path);

                }

            }

            catch (Exception e)

            {

                Console.WriteLine("Path not found:  " + path);

                Console.WriteLine(e);

            }

        }

 

        private static void DirectoryParser(string projectDirectoryPath)

        {

            // Process the list of files found in the directory.

            string[] fileEntries = Directory.GetFiles(projectDirectoryPath);

            foreach (string fileName in fileEntries)

                ComplexityCalculator(fileName);

              

          

            // Recurse into subdirectories of this directory.

            string[] subdirectoryEntries = Directory.GetDirectories(projectDirectoryPath);

            foreach (string subdirectory in subdirectoryEntries)

                DirectoryParser(subdirectory);

        }

 

        private static decimal ComplexityCalculator(string fileName)

        {

           

            if (fileName.EndsWith(".cs") || fileName.EndsWith(".cpp") || fileName.EndsWith(".c"))

            {

                try

                {

                    using (StreamReader sourceCode = new StreamReader(fileName))

                    {

                        String linesOfSource = sourceCode.ReadToEnd();

                       

                        methodCount = methodCount + FindNumberOfMethodsInClasses(linesOfSource);

                        fileComplexity = fileComplexity + Counter(linesOfSource);

                        return fileComplexity;

                    }

                  

                }

                catch (Exception e)

                {

                    Console.WriteLine("There was an error reading the source file:  " + fileName);

                    Console.WriteLine(e);

                    return 0;

                }

            }

            else

            {

                Console.WriteLine("Unsupported file type:  " + fileName);

                return 0;

            }

        }

 

        

        private static decimal Counter(string theSourceFileTurnedIntoABigString)

        {

            decimal complexityCount = 0;

            String[] matchString = { "(if)(?(\\s)\\(|\\()", "else", "(&&)", "(\\|\\|)", "(foreach)",

                                       "(\\(for)", "(do)", "(while)", "(switch)", "(case)(?(\\s)\\(|\\()",

                                       "(try)(?(\\r\\n)\\{|\\{)", "(catch)(?(\\s)\\(|\\()", "(\\?\\:)",

                                       "(else if)(?(\\s)\\(|\\()" };

           

            foreach (string regexSelectorString in matchString)

            {

                Regex selector = new Regex(regexSelectorString);

                MatchCollection selectorMatch = selector.Matches(theSourceFileTurnedIntoABigString);

                complexityCount += selectorMatch.Count;

            }

           

            return complexityCount;

        }

    }

}

 

The next step is to add a couple of methods to compute the aggregate complexity.  For that there will need to be two methods added. One to find the number, and start/end locations of the classes, and one to count the methods in the classes.

Over the weekend I took time away from family to go geek out Microsoft style at Code Camp 9 in Waltham, MA. 

map-o-waltham 

The talks were pretty much all geared towards introductory business application development.  It certainly would be nice if Microsoft could convince more of the local academic community that they're funding research for to come and make an appearance like they did for remix07. 

I was also surprised that there was not a single talk on F#.  With all the hype I've been seeing on it over the past few months I would've expected to see one session about it, guess it hasn't come up on the big business application developer's radar yet -- guess I'll have to pony up, and give a talk next time.  

One thing that should be a no brainer at any conference -- wireless access.  Please make it available to everyone.  The only way I could get it was to ask a presenter to give me their key.  Which would be against the rules, and morally wrong.  So, no internet for us.    

 

That said, I would definitely go again. 

 

The talks I went to:

Day One --  

1.   Index Optimization and Performance Tuning --

     The crux of the talk was indexing speeds things up...  However in the discussion of when to normalize/denormalize it struck me through that it all might be best handled with a stochastic model. Looks like others have had the same idea some time ago.  A small bit of searching dug up 1 & 2 (you'll need a JSTOR account to read these).  

2.  Real World httpModules and httpHandlers --

     My original plan had been to go to ASP.Net performance, and optimization as load and performace testing here at Atalasoft is a big deal, but for what ever reason the speaker didn't show up.  I ended up at Chris Love's talk instead.  I generally liked his format, start with a simple example to show its basic stuff, start extending, make a few mistakes along the way to show pitfalls, then end it all with a useful example.  Well done indeed.

3. An introduction to game development with XNA --

    A complete Pong in 40 minutes.  Yep, you heard me.  Just about went out and bought an xbox 360 after it was all over with.  Not to mention that Chris Bowen is a great, informative, and enteraining speaker. 

 4. WPF means business II --

     Found out about a nice little dubugger for wpf apps called mole debugger.

5.  Object relational mapping with nHibernate --

     This brough back memories of last summer for me.  Spent three months writing a whole bunch of xml to connect all the data layers of this project I was working on.  But really, this is good stuff... Really... I mean it.

6.  Big data: Managing terabytes with MS sql server --

     Throw hardware at it, and you problems magically disappear.

Day Two --

1.  Building a state machine workflow --

     I was really hoping for some talk of formal methods here, but it ended up being all about MS workflow.  Which is kinda neat.

2.  TDD with testdriven and nUnit --  

    I was totally amazed that when the speaker asked "Who is agile, and uses test driven development?"  that the only three people in the room to raise their hands were from Atalasoft -- shocking stuff.  The talk was good though, but then again, he was certainly preaching to the choir.

2 Comments
Filed under:

Attachment(s): map-o-waltham.jpg

If you can't measure it, you can't make it better, right?  Lots of folks have talked about software metrics, just go and pick up a copy of technometrics, or head on over to Cem's site for links to prove it to your self.  The problem with most of them is that many of the existing tried , and "true" methods is that they're CRAZY hard to implement for small Agile companies, and in many cases will cause more harm than good.  That's where CRAP comes in (I leave it to the reader to research further).  It's a simple metric that looks at two things,

                        1. Cyclomatic code complexity

                        2. Code path coverage   

 

to come up with the "CRAP factor" of your code.  More unit tests lower the CRAP factor, and less complexity also lowers the CRAP factor.   What's nice about this is that it's really hard for all those smart developers to game as making the CRAP factor go down involves good things happening in the development process.
 

The only problem, for me, with all the CRAP'py goodness is that its all written in Java, and runs in eclipse -- which is great...  Except,  Atalasoft is 100% .Net, and Microsoft Gold partner to boot. So, all Java solutions are no good for me.

Looks like its time to starting porting.  For those interested in helping me out let me know, and I'll put everything up on CodePlex ahead of it mostly working. There are two big issues that need to be solved to port CRAP4j to .Net.  You need an assembly that can give you path coverage statistics, and code to calculate the cyclomatic complexity. 

 
The easier issue to start getting into was figuring what to use for calculating code coverage as CRAP4j uses Agitar's coverage library; which, does all sorts of nice wonderful things that I can't make use of in .Net.  I chose PartCover as it's i) open source, ii)  does path coverage and pretty much everything else nCover does, but is open source, iii) works with .Net -- is open source, and iv) others are using it as well, did i mention it was open source.  There are plenty of problems with PartCover as well most notably that it doesn't purport to support ASP.Net at least according to the the forums, and needs to be massaged a bit more to get the needed information out of it to make the code complexity class work.

 

Next Week:  What I did to PartCover to make it work...

 

 

 


 

The Western Mass. developer group held a talk last night with Rich Hickey the creator of Clojure.  A video will be is available on blip.tv soon, or so I'm told by Lou

 

Some of the highlights for me were,

  •            Tight integartion with java.  If it can be written in java, you can wirte in far less code in clojure.  If you prefer you can write it all in java, and still get all the immutable goodness.
  • Lisp like syntax.
  • it's novel use of persistent data structures (more on this later). 

My apologies for this being a bit off topic, but it was just so darn cool.

 

 

 

Hello there! 

 

"Insert Quality Here"  is meant to be a bit of a poke at the idea that quality software can just happen magically.  Sprinkle on a few QA engineers, and all problem will go away, right?  Not exactly.  It takes an incredible amount of infrastructure, a bit of time, and smart people to build quality software that people actually want to use.  

 

So, what this blog going to be about?  The guts of testing and quality here at Atalasoft, and the tales of the hale and hearty men and women in the testing pit.  I hope you'll find it as interesting as I do.  

 

Cheers,

Adam Scarborough

QA Lead

Atalasoft
 

0 Comments
Filed under: