Session E-CODE

Writing Solid Visual FoxPro Code

Rod Paddock
Dash Point Software, Inc


Introduction

One of the most crucial aspects of software development is that of testing and debugging. This session will demonstrate techniques for writing code that prevents bugs in the first place and will illustrate techniques for routing out bugs during the testing process.

In this session you will learn:

  • What bugs should be really called.
  • How to write code that prevents bugs.
  • How to add ASSERTIONS to your code.
  • How to add Pseudo-Strong-Typing to your applications.
  • How to find out what code needs to be tested.
  • How to find out what modules have been tested.  

What Bugs Should Be Called
and How to Prevent Them

In the book "No Bugs" the author says that we should not call bugs bugs. He says that bugs should be called Massive Foul Ups or MFU's (hey, he was a MSFT employee). He goes on to talk about where bugs come from and came to a rather large conclusion:

Programmers put bugs in programs.

Yes programmers put bugs in code. They do not grow through genetic manipulation of magnetic material. They are put into code. The easiest way to eliminate bugs is not to put them in to begin with. We all know that bugs cannobe entirely prevented but they can sure be reduced greatly by following proper programming techniques.

Ways to prevent coding bugs include:

  • Inclusion of ASSERTIONS into your code.
  • Testing for all conditions on IF/ENDIF and DO CASE/ENDCASE blocks.
  • Strong typing of variables.

Adding Assertions

One of the ways to write robust code is to test assumptions you make in your code. This process is known as adding assertions to your code. What assertions to is allows you to insert code that tests to make sure an assumption is making is true. If it is not then it will throw an error.

Visual FoxPro includes two commands that facilitate the addition of assertions to your code. They are:

ASSERT

SET ASSERTS ON/OFF

 

The assert command has the following syntax.

ASSERT <logical condition> [MESSAGE message text]

 

If your logical condition evaluates to false then either a generic message is thrown. If you specify the message your message will be displayed in an error window. The advantages of the ASSERT command is that you can turn the assertions off at a global level by including the SET ASSERTS off command in your code. The following code demonstrates testing parameters passed to a function with assertions.

Function myFunc(pcTitle)ASSERT TYPE("pcTitle") = "C" MESSAGE "Invalid data type passed “ ;

                                     + “to this function

 

Another method of adding assertions to your applications is to add IF blocks throughout your code testing for specific conditions. The problem with this is that it eats up clock cycles even if you turn your testing functions off at a global level. SET ASSERTS OFF adds no overhead to your programs.

Testing All Conditions

How many time have you written an IF statement and omitted the ELSE statement ? In all of your branching code you should always test for the ELSE or OTHERWISE conditions. What type of problems could the following code cause if it failed ?

gcCommonPrgPath = "common\prgs\"

 

IF File(gcCommonPrgPath + "LIBRARY.PRG")

  SET PROCEDURE TO (gcCommonPrgPath + "LIBRARY.PRG") ADDITIVE

ENDIF

 

If this code fails for whatever reason your library of functions would not be opened resulting in numerous errors. This code should be written as:

gcCommonPrgPath = "common\prgs\"

IF File(gcCommonPrgPath + "LIBRARY.PRG")

  SET PROCEDURE TO (gcCommonPrgPath + "LIBRARY.PRG") ADDITIVE

ELSE

  Messagebox("File [" + gcCommonPrgPath + ;

      "LIBRARY.PRG] Not found. Please call admin",32,"Error")

ENDIF

Strong Typing of Variables.

a variable on the fly with no data type. This could introduce code problems if a programmer were to change a variable to another data type inadvertently. While Visual FoxPro does not support strong typing you can create pseudo-strong-typing with objects.

One of the new features of Visual FoxPro 6.0 is something known as Access and Assign methods. You can add code to your applications that executes whenever a property is read (accessed) or changed (assigned). To do this you simply add methods with the name of your property and the word _access or _assign appended to the name. Your assign method must also support a single parameter. This is the value you want to change a property to. The following code demonstrates testing for a data type using an assign method.

x = createobject("myObj")

x.cCharProperty = 12345  && should throw an error

 

Define Class myObj as Custom

 

lLogicalProperty  ""

cCharProperty = ""

 

Function cCharProperty_assign(pxValue)

 

If Type("pxValue") = "C"

  This cCharProperty = pxValue

Else

  Messagebox("Invalid data type entered.",32,"Invalid Data Type")

Endif

 

EndDefine

 

These are just a few coding techniques you can follow to prevent adding bugs into your code. Others include:

  • Adding comments to tell other programmers what you intend to do at a particular line of code.
  • Testing of number of parameters passed into functions.

Testing Techniques

As stated earlier in this section you learn what code needs to be debugged by testing your code. Testing is a very important aspect of development and this section will demonstrate some techniques and tools you can use in your development process and in your ongoing production environment.

How Can You Test Code ?

One of the most common method of testing code is to try to test all possible combinations your code can encounter.. While this sounds good in theory it does not work in real life. You CANNOT test all possible combinations of code. Guess how many possible combinations the following code could gennerate.

Function pCombinations( p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,;

                        p11,p12,p13,p14,p15,p16)

 

lcRetVal = ""

 

For lnKount= 1 to Pcount()

  lxValue = EVAL("p" + transform(lnKount))

  lcRetVal = lcRetVal + IIF(lcValue = 0,"0","1")

EndFor

 

Return lcRetVal

 

How many possible permutations could this 7 line program generate assuming all parameters are passed in ?

Write your answer here _______________________________-

Each parameter doubles the number of permutations.

So what's a programmer to do ? While each permutation cannot be tested each line of code can be executed at least one time. This is known as coverage. The goal of every tester should be to make sure each line of code has been executed. If you do not test for this you run the potential of having hidden bugs find their ways into your application. Visual FoxPro now includes a tool called the Coverage profiler. This tool helps programmers track what lines of code have and have not been executed.

Using the coverage analyzer is a two step process. The first is to run your application and log which lines of code have been run. You do this with the SET COVERAGE TO command. After running your application you can then open the coverage analyzer with your log file. The coverage analyzer will then process your log file showing you what lines of code have and have not been executed.

The most important aspect of this tool is the Statistics window. This window gives you a measurement of how well your coverage has been performed. You should strive for a number in the high 90% range. One thing to note about this tool is that it requires you to perform "regression" style testing. This means creating a full set of tests that you run over and over again.

Instrumenting Your Applications

How many time have you installed beta versions of your software for clients to test and come to find out they have not even run the application. How about features that do not get used ? One method of insuring that all portions of an application are being run is to instrument that application. Included on your CD-ROM is a white paper on instrumenting applications. Take a look at this document.

Conclusion

Testing and debugging applications is a very important aspect of development. In this session you learned techniques for writing bug-resistant code and techniques for routing out bugs. You should have learned:

  • What bugs should be really called.
  • How to write code that prevents bugs.
  • How to add ASSERTIONS to your code.
  • How to add Pseudo-Strong-Typing to your applications.
  • How to find out what code needs to be tested.
  • How to find out what modules have been tested.

 

About the author

Rod Paddock is president and founder of Dash Point Software, Inc. DPSI is an award winning software development company based in Seattle, WA, specializing in application development and software training. DPSI specializes in Visual FoxPro, Visual Basic, Cold Fusion, Visual Interdev SQL Server. DPSI's clients include: ADTC, SBT Accounting, The US Coast Guard, The US Navy, Pinnacle Publishing, Intel and Azalea Software. Dash Point Software, Inc. was a finalist in the 1998 Visual FoxPro Excellence Awards and 1996 Windows World Open. Rod has been a featured speaker at the 1996-1998 German FoxPro DevCons, the 1995 and 1998,1999 FoxPro Developers Conferences, and the 1995-1997 FoxTeach conferences. Rod writes for several database publications including: Data Based Advisor, Foxtalk and Dbase Advisor. Rod's books include: Visual FoxPro 6.0 Enterprise Development, and VB6 for Web Development. http://www.dashpoint.com


Session E-CODE

Writing Solid Visual FoxPro Code: Addendum

Rod Paddock
Dash Point Software, Inc


Introduction

There have been numbers presented that say 80% of the cost and time spent developing software consists of primarily maintenance. It should be the goal of all developers to reduce these costs to the lowest point possible. You can reduce your development costs in a number of ways: using OOP development techniques, having standards and documenting your designs well. There is on often overlooked way to lower the costs: How about removing functionality ??? In this session you will learn how you can lower your development costs by instrumenting your applications and removing unused system functionality.

In this session you will learn:

  • What instrumenting applications is.
  • How to instrument your applications
  • Code techniques for instrumentation.
  • How to properly analyze your data after the instrumentation process
  • Other thoughts on instrumentation

What is Instrumenting

Instrumentation is a method of adding code to your software to analyze its usage. Instrumentation is commonly used in the debugging process. Developers use instrumentation to make sure all aspects of their software are being tested. In development instrumentation can also be called Profiling or Coverage Analysis. Commonly this is where the analysis of usage stops. Once the application reaches production its usage is not examines except where there are enhancements being made of bugs being fixed.

Instrumentation is the process of analyzing production or runtime usage of software. This technique utilizes code that tracks how the system is being used at specific application junctures. This code is much like profiling code in that it records information such as the time a function was run, who was running that function and what was being run. Where it differs is that profiling tracks each and every line of code being run, instrumentation works at a higher level looking for application functionality being used. It wants to know things like: what forms are being run, what menus options are being hit and what buttons are being pressed. With this information developers can make educated decisions on what functionality to keep and upgrade and what functionality to remove.

How to Instrument your Applications

The instrumentation process begins with determining what types of things you want to track. The easiest place to look for the answer to this problem is the Windows 95 interface guidelines. The Windows 95 guidelines contain 7 basic rules of user interface design. The first rule says:

"Applications should follow the object-action paradigm."

What this rule means is when people activate menu bars something should happen or when a user presses a button some action should occur. With this in mind you can see that you should at least instrument your application from its menus and its command buttons. However in a Windows environment other actions that might occur include: form switching when multiple forms are active, selection of options from a list box, screen changes from a combo or list box, or even activating screens from check boxes (Remember 2.6?).

With this in mind you can go to work instrumenting your applications. For this session you will look at instrumenting command buttons, menus and forms.

Instrumenting Menus

The first items to look at instrumenting are menus. Most application components are launched from some type of system menu so this is a good place to begin your instrumentation. When you instrument your menus you have a few options as to where you stick your instrumentation code. You can stick the code in your menu bars directly or you can stick the code in your global application object that is responsible for launching forms and programs. In either case your code will track the same information, what menu option was pressed. The following code represents a function that will track the proper information for instrumenting menus:

*-- This function instruments menus.

Function g_insmnu

Lparameters pcPrompt, pcPad

 

*-- Open instrumentation file

If Not Used("Instrument")

  Use Instrument In 0

Endif

 

*-- Get the text from the menu pad

Local lcPadPrompt

lcPadPrompt = prmpad("_msysmenu",pcPad)

 

Insert Into Instrument (calling_source, ;

  program_name, other_name, call_date, call_time) ;

Values ;

  ("MENU", pcPrompt, lcPadPrompt, date(), time())

 

Return 

 

Upon completing this function you can insert the calls to this function into your menu bars with the following command:

g_insmnu(Prompt(), Pad())

 

This call will insert a record into the instrumentation data table. Other useful information that you might want to track might include: user information (who is running this function) or maybe profiling statistics.

Instrumenting Command Buttons

The next item on the list is code to track command button hits. Instrumenting command buttons should be rather simple. All you need to do is place code into your command button base class and all of your buttons will instrumented. The code for instrumenting command buttons is as follows:

*-- This function instruments a command button

Function g_inscmd

Lparameters poCommandButton, poForm

 

*-- Open instrumentation file

If Not Used("Instrument")

  Use Instrument In 0

Endif

 

Insert Into Instrument (calling_source, ;

  program_name, other_name, call_date, call_time) ;

Values ;

  ("CMD", poCommandButton.caption, poform.Caption, date(), time())

 

Return

 

This code accepts two parameters. The object itself and a handle to the form the object is on. To call this function place the following call into your command buttons base class:

g_inscmd(This,Thisform)

 

Another item that you might want to consider tracking in this instance is the picture property of the button. If you use graphical buttons with no text you will need to track this additional property.

Instrumenting Forms

The last item to instrument are forms. Forms pose a unique problem in that they can be called from so many different paths. You can open forms as part of a form set, you can create multiple instances of a form or you can switch between multiple open forms. The following code show how you can instrument forms. Pay attention to the SYS(16) function. This function moves up the calling stack to see where this form was called from.

*-- This function instruments forms

Function g_insfrm

Lparameters poForm

 

*-- Open instrumentation file

If Not Used("Instrument")

  Use Instrument In 0

Endif

 

Insert Into Instrument (calling_source, ;

  program_name, other_name, call_date, call_time) ;

Values ;

  ("FRM", poForm.caption, SYS(16,1), ;

    date(), time())

 

Return

 

To run this code stick the following call into the Activate, Deactivate and Init methods of your form base class:

g_insfrm(Thisform)

 

Other useful information you might want to call from here may include: The number of forms opened (from the application.forms property), the event that was actually called and time open information for a specific form.

Analyzing The Results

After tracking this information you need to look at it with a critical eye. This critical eye should be looking for the following items: What are the most used application components and how many times are they being run.

At the highest level you can begin looking at the menus. This is where the wealth of your information can be derived from. Menus tell you what application components are being used at the highest level. More granular data comes from forms, command buttons and other lower level components. The following query looks at menu usage:

Select program_name as prompt, other_name as pad , sum(1) as menucount ;

  From Instrument ;

Where calling_source = "MENU" ;

Group By 2,1 ;

Order By 3 Descending ;

Into Cursor c_menucalls

 

The next query looks at command button usage.

Select program_name as button, other_name as form , sum(1) as menucount;

  From Instrument ;

Where calling_source = "CMD" ;

Group By 2,1 ;

Order By 3 Descending ;

Into Cursor c_buttoncalls

 

Now that you have this information what do you do with it ? The first thing you need to consider is: Was my instrumentation period adequate. You want to make sure that you have a "real" representation of how your application is used over time. Make sure you have instrumented your application with enough time for periodic functions to be run, you know: Month-End…

Upon finding functionality that is not being used you should begin by placing "stub" code in your application to temporarily disable functionality. This code should tell users how to contact IS to re-enable this functionality.

After a time you can remove any functionality that has not been specifically requested by a user.

Other Thoughts on Instrumentation

So far you have looked at the functionality that was called. Another important item you would like to know is what didn’t the users run. How do you do this ? To accomplish this you need to create code that will create the universe of options that are available to users. The simplest place to look is menus. As a future exercise you can create a program that will take a MNX file and dissect into its various components that you can then use to coordinate with your Instrumentation data.

Where it gets rather difficult is when it comes for forms and command buttons. It could be rather hard to create a universe of data from these files without creating a parsing program for projects. The simplest method for tacking these files is to put a button on your base form class and to regressively test your application. On each form the tester should press this button which will then register all objects on a form with a universe table.

This is the current set of problems that this author is working on. For future information on this topic look at www.dashpoint.com

Conclusion

As you can see there is real value in instrumenting your applications. You can find out what people are really using and remove functionality that is not being used. In this session you learned:

  • What instrumenting applications is.
  • How to instrument your applications.
  • Code techniques for instrumentation.
  • How to properly analyze your data after the instrumentation process.
  • Other thoughts on instrumentation.