Tester Plugin
Started by: av 11.25.1997
Last updated: 04.1.2002

Table of contents

- Summary
- Description
- GUI

- Windows

- UNIX

- MAC

- Full Page Mode
- Log File Format
- Script File Format
- Current Version and Known Issues
- Enhancements and To-Do List
- Appendix A: Plugin Architecture (port notes)
- Appendix B: Preferences File

Summary

Tester plugin (preliminary file name npapi.dll) is designed to add a level of convenience and automation to testing Netscape Plugin API. The plugin has implementation of all existing NPAPI calls and allows to execute them in any desirable pattern as well as log all NPAPI activity. Plugin handles MIME type `'application/npapi-test'` and file extension `'pts'` (which could stand for 'plugin test script').

Description

The plugin is capable of issuing NPAPI calls to the Navigator and logging them along with callbacks from the Navigator with argument and return values. Time stamps are also logged so some timing issues can be investigated. All NPAPI calls, both `NPN_*` and `NPP_*`, are getting logged during operation of the plugin. User can see log in the dedicated frame in the Navigator window or dump log into file.

The plugin has two modes of operation: manual and automatic (script execution). The manual mode is thought to be convenient for taking a quick look at the API and get some initial feeling. It allows user (developer, tester) to call any NPAPI function with any valid arguments from the plugin GUI and see the either the visual result of this call or the log if the call results in callbacks from the Navigator or returns some values.

Automatic mode is for more sophisticated testing with preset pattern of calls. Before starting a test plugin reads a specified script file which contains all necessary info on what calls to execute, what parameters to pass and how to conduct the test: how many times, with what time delay after specific calls or after repetition cycle.

From plugin GUI user can choose log file name and script file name (or URL - see Enhancements if this is in to-do list).

Special case is full page mode, when the plugin is invoked on file extension association. In this case there is no GUI at all, and `*.pts` file is treated as a script file to execute.

GUI

Platform implementations of the plugin GUI can differ since the plugin was originally developed for Windows NT, and GUI is the only non-crossplatform part of it. See Enhancements section for the latest on platform implementations.

Windows GUI

In its original configuration the main Navigator window is divided into three frames: Control frame, Display frame and Log frame, although this configuration is not required.

Plugin controls (the actual GUI) occupy the Control frame, while Display frame can be used as a target for calls like `NPN_GetURL` and Log frame displays the log.

User chooses desired mode of operation: Auto or Manual, and using check boxes and editable fields enables/disables logging to frame/file chooses log and script file names. User can hold log output if timing is important by disabling "Flush immediately" feature and flush the log when it is appropriate by hand ("Flush" button).

Fig. 1. Screen shots of Auto and Manual modes

In Manual mode, only valid arguments are possible to pass. If, say, we pass a string pointer to `NPN_GetURL` as a URL to get, user cannot specify the actual pointer value, he rather types the string itself. Plugin instance value can not be modified too. In Auto mode, on the contrary, one can pass any values in place of any argument using script file format features. This option is particularly useful if invalid parameters are to be tested.

UNIX GUI

MAC GUI

Full Page Mode

This is a useful mode when no user interaction is needed at all. The plugin is launched in full page mode when it encounters file of `'pts'` type. It saves this file in a local cache and then treats it as a normal script file. It automatically constructes log file with the name of cache file and `'log'` extension. This can overridden in preferences profile. See Appendix B on how to specify log file name in fully automatic mode .

Log File Format

Every NPAPI call is logged into a log item which normally consists of three lines:

`NPN_GetURL(0x00cfa7a0, 0x00a2f8c0("http://mozilla.org"), 0x00a2f840("_npapi_Dis..."))`
`Returns: NPERR_NO_ERROR`
`Time: 9478 9554 (76)`

Actual argument values are shown. There are some exceptions:

- In case argument is a pointer to some data like a string the beginning of the data is also shown in parenthesis right after the value. First ten characters are shown by default. See Appendix B on how to set wrap .
- If argument is enumerated parameter or `#define'e`d value its identificator is shown rather than numeric value, e.g. (`..., NPRES_DONE)`.
- If argument is a pointer which is supposed to be filled with data after function returns, the data is also shown in parenthesis.

Time stamp allows user to estimate timing. Numbers are some relative time counts in milliseconds. First is a time when a specific function is entered, second - when it is about to return, and third is just a subtract of those two.

Symbolic separator can be inserted after each log record. This could be useful for automated log file readers. See Appendix B on how to specify separator .

Script File Format

Script file is in a form of standard Windows profile (`.INI` file).
Private profile is a text file consisting of sections and keys with their values, comments can be made with a semicolon in first position

`[SectionName1]`
`key1=value1`
`key2=value2`

In tester plugin script file every call to be executed should form a section with numeric name (this number will define an order of call sequence) and `APICall` key which specifies a function to call, `arg1`, `arg2` etc. keys specifying arguments, and also the section can contain a `delay` key which would insert a time delay in milliseconds placed after the call. For example:

`[10]`
`APICall=NPN_MemAlloc`
`arg1=1024`

The file can also contain `Options` section which would specifies how to conduct the script. For instance:

`[Options]`
`first=1`
`last=23`
`repetitions=1`
`delay=0`

`first` key says from where to start the script (default 1). `last` key MUST be present, and it says when to stop the script cycle. All sections with numbers less than that of specified in the `first` key and more than that of specified in the `last` key are ignored by the script reading code. So you can pick any part of existing long script for executing. Two other keys are optional, `repetitions` being the number of times the script cycle is going to be executed (default 1), and `delay` being the time delay in milliseconds between cycle repetitions (default 0).

'Numeric' sections are the actual script, each representing one NPAPI call. Calls will be executed in their numeric order, some numbers could be missing, so you can start to write your script with numbers multiple by say ten in order to be able to insert new script items later.

Important feature:
`arg*` keys represent NPAPI function call arguments and they depend on actual call. In some cases you may not specify an argument if you have no control over it. E.g. pointer to the plugin instance or pointer to a stream created by `NPN_NewStream`. Scripter code will pick up right ones for you. If you specify them explicitly it will take them but be prepared for boom. This is appropriate if you want to test an API call with invalid arguments. String should can be represented by actual string. Arguments which are parameters of enumerated type or `#define`'ed like `NPPVariable`, `NPReason` etc. should be represented by their enum type identificator from npapi.h like `NPPVpluginNameString` or `NPRES_DONE`. Also `NPBool` should read `TRUE` or `FALSE`. If you want to test real numeric values in place of this args, say `0x00000000` for string pointers, use a special prefix '`_true_numeric_value_`' . The following example will execute `NPN_GetURL(instance, -1, "some_target")` with real instance, invalid URL and valid target:

`[1]`
`APICall=NPN_GetURL`
`arg2=true_numeric_value_-1`
`arg3=some_target`

The special 'numeric' prefix also works for `NPBool`, `NPReason`, `NPNVariable` and `NPPVariable`.
In case of `NPRect*` or `NPSize*` type of argument you can use either `arg*=0x0000` (or any other value) or not using `arg*` use `top=`..., `left=`...,`right=`..., `bottom=`... or `width=`..., `height=`...

Current Version and Known Issues

Current version is 1.5.

Issues:

- In Auto mode log is not shown in frame until script execution is complete
- Log in window does not scroll automatically
- There is no way to stop script execution during execution. In fact the whole thing is "frozen"
- Log file name and script file name must not contain path. Files are always supposed to sit in the Navigator Plugins directory
- In Manual mode multiple `NPN_MemAlloc`'s are not allowed, every `NPN_MemAlloc` should be followed by `NPN_MemFree` (not necessary immediately though)
- The same applies to `NPN_NewStream`

Enhancements and To-Do List

- Implement for UNIX
- Implement for MAC
- URL for scripts, not only local files
- Introduce options for log. Like "short" - with no return value or arguments (e.g. useful for timing investigations), "errors only" - when all 'normal' activity is not logged. 'Normal' here needs to be defined

Appendix A: Plugin Architecture (port notes)

The core of the plugin consists of three self-contained objects of types `CPlugin`, `CLogger` and `CScripter`, and there is also a set of various helpers like string converters and formatters, and INI style profile handlers (on Windows, standard built-in profile stuff can be used).
The three objects are global for the project, `CPlugin` being a "coordinating center". `CLogger` and `CScripter` are not members of `CPlugin` simply because there is no need to keep several instances of e.g. `CLogger` for every instance of `CPlugin`. One should be enough for all.

Part of `CPlugin` C++ interface and some functions in `xp.cpp` are the only things which should be implemented for every platform separately since this object handles GUI. Therefore, to make things easier, an abstract class `CPluginBase` has been written. This base class methods contain cross-platform code, and object of `CPlugin` type can be derived from this base class. With this approach no changes need to be made in any project file, only a new source file with implementation of `CPlugin::CPluginBase` with appropriate GUI handling can be added.

As a first step, one can try just to build a project with no additional coding, thus with no GUI, so it will work only with scripts.

`CLogger` and `CScripter` do not use any Windows specific API thus are cross-platform. C++ interface for these objects is self-explaining and straight forward. The objects handle file operations, string scanning and formatting, and also information output.

Some Windows specific calls which are used throughout the project are put together in file xp.cpp where they are `#ifdef`'ed and need to be implemented for every non-Windows platform. At the moment they are: `XP_GetTickCount(...), XP_Sleep(...)`.

So, the steps for successful porting are:

1. Include all files from `Tester/Common` subdir to the project
2. Derive `CPlugin` class from `CPluginBase`
3. Implement all pure virtual methods
4. Implement any platform specific methods for handling GUI, if needed
5. Name `CPlugin` class header file`Plugin.h` (it is used in dependencies)
6. Look through `xp.cpp` and implement missing for your platform `XP_*` calls

Appendix B: Preferences File

`Npapi.ini` is located in the same directory the plugin is located. Currently it stores GUI preferences (state of check boxes etc.) in `Preferences` section, and normally this section does not need to be edited directly. Exception is `LoadWindowStatus`.

`In Preferences` section you can manually add an option to show a small window in the left top corner of the screen which tells you that the plugin dll is loaded in memory. This may be useful after calling `NPN_SetValue` setting `NPPVpluginKeepLibraryInMemory TRUE`.

`[Preferences]`
`LoadWindowStatus=Yes`

`In Log` section the form of separator can be specified using key `RecordSeparator`. For example:

`[Log]`
`RecordSeparator=$`

If no separator is specified, blank line is used instead.

In this section you can also specify when to cut off long data strings in the log as follows:

`[Log]`
`StringDataWrap=20`

The default value is 10.

The log file name for full page mode can be specified with key `FileName`. Note that full path should be given too:

`[Log]`
`FileName=y:\x86Dbg\plugins\test.log`