VFP project coverage
VFP coverage analysis is not, by nature, a project-centric activity. A single log may include information from multiple apps and multiple projects. However, sometimes you are interested primarily in the analysis of one project and its source files. For this reason, the Coverage Profiler Statistics dialog gives you an opportunity to pick a project to analyze. The engine evaluates all the files listed in that project with appropriate source code types and lets you know which files you've covered, and to what degree you've covered them.
To do this job properly, you will notice that the Profiler quickly goes through all the target workfile records to make sure they have already been marked and that all relevant statistics are available, before going on to report on Project coverage.
Yes, I said " report" , and I meant " report" <g>. The default Profiler display of project statistics uses an FRX. This report form is quite rudimentary, and it's designed for you to edit to your liking. You can either edit COV_PJX.FRX directly or change the COV_TUNE.H #DEFINE that indicates the appropriate FRX, and design a completely new report.
Project statistics are gathered in a separate workfile from other Coverage results, in this format:
CREATE CURSOR (cProject) ; (Hostfile c(COV_LEN_HOSTFILE), ; FileType c(4), ; Coverable n(8), ; Covered n(8), ; ObjTotal n(8), ; ObjHits n(8))
You'll find this line in the engine's GetProjectStatistics() method. Here is the sequence of events that takes place in the engine's ShowProjectStatistics() method:
In the engine class, DisplayProjectStatistics() is abstract (empty). In the standard User Interface engine subclass, DisplayProjectStatistics() issues the REPORT FORM statement with appropriate WINDOW clauses and other behavior for this report to appear within the standard Coverage Profiler interface.
This is a perfect example of the relationship between the Coverage engine's role and the responsibilities of the display or interface subclasses. It also gives you an idea of your opportunities to create behavior that you might prefer. As you can see, along with simply modifying the report form, your Coverage subclass might:
Coverage in VFP 5
Can you do all this good stuff in VFP 5? The short answer is " yes" !
The standard COVERAGE.APP will run in VFP 5. Although I haven't tested extensively, I would expect most or all of the sample subclasses of both the engine and the shipping interface class, plus all the AddIns, to work in VFP 5 as well as VFP 6. (In fact, GR_STATS.SCX originally used an ActiveX control, but I re-wrote its graphical interface in pure VFP code, simply to avoid versioning problems <g>.)
To use the Coverage Profiler in VFP 5, you will need to follow these steps, first:
COVERAGE.APP will now run in VFP 5. The VFP 5 log has a few limitations. Accordingly, the app will not show you information about FRX or LBX files in VFP 5, because code for reporting objects is mis-attributed in the VFP 5 log. You may find that a few other statistics are mis-represented. (Primarily, VFP 5 logs did not handle DODEFAULT() items very well, and will mis-attribute the execution of parentclass lines invoked by a DODEFAULT() to the child class that contained the DODEFAULT() statement. You may also notice some problems with leaf objects -- option buttons, pages, command buttons in command groups -- for which you've written code.)
However, in my experience, the inaccuracies and omissions will be minor. They do not detract from the general usefulness of the analyzing tool (or even the generated log) within VFP 5.
Although VFP 6 can run VFP 5-compiled code, you should probably BUILD … RECOMPILE separate versions of the Coverage app for VFP 5 and VFP 6 on general principles.
Also, you will only be able to analyze VFP 5-generated logs in VFP 5 and VF 6-generated logs in VFP 6. This distinction is made at runtime, not compiled time, and is intended to make sure that you know exactly what you are testing. (If you executed the code and generated the log under VFP 6, in other words, you should analyze its coverage under VFP 6 -- whether that code was compiled in VFP 5 or VFP 6 is irrelevant.)
One problem people might have with the Coverage classes under VFP 5 involves the use of the common file dialog ActiveX control for file- and font- picking. You may have different versions of this control in your system.
In most cases, whether under VFP 5 or any other product version, the Coverage classes will detect any OCX error and switch smoothly over to use of GETFILE(), PUTFILE(), and GETFONT() after the first such error. However, you can permanently dispose of the problem (or you can take care of it in any case where the OCX error is not trapped and the switch isn't automatic), by changing a #DEFINE in the COV_TUNE.H file, discussed below.
VFP Coverage and Coverage Profiler Internals
But wait -- there's more. This section will tell you quite a bit more about how the Coverage Profiler works, and also about how Coverage works within VFP. Skip this entire section, if you've had enough of these details for now, or read on to see more ways you can manipulate the Coverage Profiler features and more deeply understand the internally-produced Coverage log.
If you do skip the rest of this section, I recommend you come back and read the material on the Coverage workfiles at a later date. Understanding these cursors, and especially understanding the engine's ability to manage multiple sets of these cursors, is critical to getting maximum value from the Coverage engine.
Understand and use the COVERAGE.VCX source
The COV*.H files and the COVERAGE.VCX/VCT contain all the source you need. The wrapper program that instantiates the shipping class in COVERAGE.APP is not necessary.
You can instantiate cov_Engine (the engine class), cov_Standard (the shipping UI subclass), or additional subclasses, from the Command window, with a simple call:
* SET PATH TO the location of COVERAGE.VCX NEWOBJECT(" cov_standard" ," coverage.vcx" )
Notice I said that you should SET PATH to COVERAGE.VCX's location. This is because the coverage classes will need to instantiate other items from this VCX later on.
If you build the above NEWOBJECT( ) line into an .APP of your own, the SET PATH will not be necessary. Not only will COVERAGE.VCX be pulled into the project, and therefore be available to the APP, but EXTERNAL statements within COVERAGE.VCX will make sure that additional pieces (such as ICO files) will come into the APP as well.
Beyond cov_Engine and cov_Standard, the COVERAGE.VCX classes can be roughly divided into these groups:
Tune the Coverage header files
The Coverage header file set has a master header file (COVERAGE.H) used by all the Coverage classes. This COVERAGE.H is simply a series of #INCLUDE statements that breaks the necessary #DEFINEs into categories.
If you are interested in localizing Coverage for use in different languages, you will want to look at the strings in COV_LOCS.H. If you are interested in the internals of the Coverage engine, you may want to take a brief look at COV_SPECS.H. However, most developers will be more interested in COV_TUNE.H, where all the constants you can reasonably and safely alter are located.
In this file, you can alter the Coverage Profiler's debugging behavior, its default code marks, the classes it instantiates, the report form used to report on projects, and a whole lot more. If you are planning to subclass the engine, I'd like to draw your attention, especially, to the use of the COV_TOPSPEED #DEFINE, which switches code marking in the engine's MarkTargetCoverage method between a method call to the MarkCodeLine() method and an in-line expression.
Additionally, you'll see the _oCoverage default public reference stored here. The Coverage engine maintains this reference itself (you don't need a wrapper program to do it). A future version of the Profiler, or a subclass that you write, might prefer to place the string that becomes the public reference to the Profiler in a new Coverage registry key.
Take a look at the Cov_SaveState class in COV_SUBS.VCX for an example of a Coverage Profiler subclass that creates new registry keys. Cov_SaveState, and the little custom object it attaches to all the forms in the Coverage formset, creates these keys to save its window positions (for the Coverage frame and each window in the frame). You may want to drop the custom object cus_SaveSate to forms in a completely different type of Coverage interface.
What's really in the log
Have you looked at what happens when you SET COVERAGE to create a log file? The result is a comma-delimited text file, looking something like this:
The lines above are not consecutive lines from any one coverage log. They are just representative lines from different types of source code, in a VFP 6 log. In VFP 5 they'd look similar but the last column would be missing. In both versions, they ordinarily have some pathing information in the column containing the filename.
Here's what you get:
For object-oriented code, things get a little more complicated. Since an SCX, LBX, or FRX is considered to have one object instantiated (the DataEnvironment, in a report or label), you'll find the entire hierarchy here, even though the code-in-question may be in one of several records of the SCT. For example, in the frmorderentry.cbocustomer_id.click SCT record above, if cboCustomer_ID was a member of a frmOrderEntry class in a VCX, before this form class was added to the SCX, you may have typed code into this combobox's Click() method in the Form Designer. In this case, its method code is in the frmOrderEntry record of the SCX (identified there in a PROCEDURE cbocustomer_id.click section). On the other hand, if this combobox was dropped onto the form directly in the Form Designer, then the code is in the combobox's own record in the SCX (its code would be identified as PROCEDURE Click in that record.)
What's not in the log (uncoverable lines) or unattributable
There are some lines of code that VFP deliberately excludes from the coverage log. This is why the number of lines you'll see in Coverage Profiler statistics rarely matches the total number of lines in the PRG file or method. These lines must be distinguished from " coverable" lines, so they are not counted against your line totals for statistical analysis or show up with erroneous 0 times in profiling figures.
Lines that never appear in the log include:
Additionally there are other lines the log includes, without attributing to a particular source code file. These are primarily " interrupts" such as ON SELECTION and ON ERROR that can trigger at any point in your code. When you DEFINE a POPUP and issue ON SELECTION statements, these lines of code execute within the context of your PRG, method, or MPR, and they are coverable. The Profiler will show these lines as executing once.
Later, however, you may select that popup bar many times, executing its attached command. Each time, the log will note the execution of an " on selection" interrupt, but it will not attach that information to any source code (because, at that point, VFP has no idea in what source file the ON SELECTION statement exists).
The log and the Profiler cannot show you how many times this code executed. They will, of course, show you the code that executed in response to the trigger. For example, if you say ON SELECTION Bar 1 OF MyPop DO MyFunc, all the lines in MyFunc will be properly logged, and the Profiler will properly account for them in its marks and statistics.
But this issue of " unattributable lines" points to a second problem with the concept of " 100% dependency coverage" statistics. Although in this case you can analyze your code to see in what ways your code might be called, to include such triggers in your " total path" figure, you cannot properly attribute any single execution to these particular paths, so you can never know if these paths were covered. .