Wednesday, December 17, 2008

Command-Line Parsing APIs

 

Introduction to TestApi – Part 2: Command-Line Parsing APIs

Command-line parsers remind me of linked lists in C++: everybody has written several at various points in their careers. While everybody should write each of those at least once, I doubt that many people out there are particularly excited about writing and re-writing fundamental data structures on a regular basis – it gets old very quickly. Not to mention that doing so is error-prone and decreases the maintainability of a world that’s already hard to maintain.

That’s why modern-day frameworks such as .NET provide standard implementations of the common data structures. And that’s why TestApi 0.1 provides a reusable command-line parsing APIs via the CommandLineDictionary and CommandLineParser classes, the latter being a type-safe layer on top of the former. Obviously, these are not test APIs per se – they are general utility APIs that happen to be more often used when writing tests.

A few quick examples follow.

Simple Command-Line Parsing

As seen from the first example below, extracting command-line parameters that are primitives is easy. Primitive command-line parameters are either boolean (e.g. the “verbose” flag below), or a key-value pair, that one can extract with the indexer of the CommandLineDictionary instance (see the “testId” key below), just as one would expect from a Dictionary.

// 
// EXAMPLE #1: Parsing a command-line such as "RunTests.exe /verbose /testId=123"
//

using System;
using Microsoft.Test;

public class Program
{
public static void Main(string[] args)
{
CommandLineDictionary d = new CommandLineDictionary(args);

bool verbose = d.ContainsKey("verbose");
int testId = Int32.Parse(d["testId"]);

// use the parsed command-line parameters
}
}


By default flags/keys are indicated with the forward slash (“/”) character and values are indicated with the equals character (“=”), but the user can override that upon initialization of of the CommandLineDictionary object:



// 
// EXAMPLE #1b: Parsing a command-line such as "RunTests.exe –verbose –testId:123"
//

...
CommandLineDictionary d = new CommandLineDictionary(args, '-', ':');
...


Finally, you one can use the ToString method to get a string representation of the command-line arguments.



Command-Line Argument Structures



Another common pattern when dealing with command-line arguments is populating a structure which contains all parsed arguments. The CommandLineParser class makes this easy:



// EXAMPLE #2:
// Sample for parsing the following command-line:
// Test.exe /verbose /runId=10
// This sample declares a class in which the strongly typed arguments are populated
public class CommandLineArguments
{
bool? Verbose { get; set; }
int? RunId { get; set; }
}

CommandLineArguments a = new CommandLineArguments();
CommandLineParser.ParseArguments(args, a);


Type-Safe Commands



A third common approach is forming strongly-typed commands from the command-line parameters. This is common for cases when the command-line looks as follows:




some-exe  COMMAND  parameters-to-the-command




The parsing in this case is a little bit more involved:




  1. Create one class for every supported command, which derives from the Command abstract base class and implements an expected Execute method.



  2. Pass an array of all expected commands along with the command-line arguments to CommandLineParser.ParseCommand – the method will return a  strongly-typed Command instance that can be Execute()-d.





// EXAMPLE #3:
// Sample for parsing the following command-line:
// Test.exe run /runId=10 /verbose
// In this particular case we have an actual command on the command-line (“run”),
// which we want to effectively de-serialize and execute.
public class RunCommand : Command
{
bool? Verbose { get; set; }
int? RunId { get; set; }

public override void Execute()
{
// Implement your "run" execution logic here.
}
}

Command c = CommandLineParser.ParseCommand(args, new Command[] { new RunCommand() })
c.Execute();


Besides the parsing logic, CommandLineParser provides a few additional helper methods. One of them is CommandLineParser.PrintCommandUsage, which prints the usage for specific commands (or all supported commands) to the console.



In Conclusion



The command-line parsing APIs released with TestApi 0.1 provide a simple and “layered” access to the command-line. Strictly speaking these APIs are not test APIs, but have nevertheless been included in TestApi as tests often have a need of parsing parameters on the command-line.



Published Wednesday, December 17, 2008 2:15 PM by ivom




Ivo Manolov's Blog : Introduction to TestApi – Part 2: Command-Line Parsing APIs

No comments:

Blog Archive