How it works
FormsRefresh has four parts: a runner that drives the original Forms client, a collector that records what happens, a coverage loop that decides when there are enough scenarios, and an adapter that replays the same scenarios against the replacement and classifies every difference. The example below is the retail scenario order-two-lines: create an order with two lines, change a quantity, save.
1. The runner drives Forms by name
A Java agent loaded into the Forms client (the Forms Standalone Launcher) walks the client's component tree.
With the Forms Services setting record=names, every item in that tree carries its BLOCK.ITEM name, and the
agent reports each element's name, screen region, displayed text, enabled state and focus. The runner sends real
X input events (keystrokes, clicks) to the region of the element it found by name.
Because the input is real, it passes through everything a user's input passes through: format masks, case restriction, KEY- triggers, disabled items, lists of values and alerts. A scenario never contains a coordinate:
steps:
- label: header
type:
ORDERS.CUSTOMER_ID: 1
ORDERS.EMPLOYEE_ID: 1
ORDERS.TABLE_NUMBER: 5
- label: line 1 product
lov: {item: 'ORDER_ITEMS.PRODUCT_ID_FK[0]', select: {Name: Latte}}
- label: save
click: ORDERS.PUSH_BUTTON10
- label: acknowledge commit
alert: OK
2. The golden record
Each scenario starts from a restored copy of the test schema. After every step the collector records:
- every item on the canvas with its displayed value, per record row;
- the focus, the message line and the record indicator;
- the open windows, any alert (title, text, buttons) and any list of values (title, columns, rows).
At the end it records the database change set: rows inserted, updated and deleted per table, and each sequence
value drawn, with the generated keys traced to the sequence draw that produced them. In the example, opening
the form draws order number 16 and shows it; saving draws 17 and stores that; ORDER_DATA is stored as NULL
although the column has a default. A replacement has to do the same, or the customer has to waive it.
Values that depend on the clock (SYSDATE written by the database) or on the run date (a date item that
defaults to today) are masked. Every scenario is run three times from the restored baseline, and the three
records must be byte-identical. The masks are checked by recording on another day and with the Forms runtime's
clock moved ten days ahead.
3. Coverage decides when there are enough scenarios
An agent reads the XML export of each module (frmf2xml) and the schema's PL/SQL and proposes scenarios for
whatever is not yet covered. Each scenario runs a second time under Forms Runtime Diagnostics, replaying the
same input events; the diagnostics log shows every trigger and program unit that fired and the built-ins each
one called, which identifies the branch taken. DBMS_PLSQL_CODE_COVERAGE records the database PL/SQL blocks.
The coverage report lists every trigger, program unit and branch as covered, unreachable or blocked, each with its reason. Unreachable code is a list of retirement candidates: code nobody needs to convert. The loop ends when nothing is left unclassified and every scenario passes on the original.
4. Replay and classified diff
An adapter replays the scenario against the replacement. The one used here drives a web application in
headless Chromium and reads its PostgreSQL database, each run in a fresh copy of the demo database. A mapping
file names, for each BLOCK.ITEM, the target's element and column; it is drafted by an agent from the two
sources.
The diff compares the two records value by value and classifies each one:
| Class | Meaning |
|---|---|
| match | The same value. |
| tolerated | Different only within a named rule of the tolerance policy: money to two decimals (15000 and 15000.00), quantities, timestamps to the second, generated keys paired by the order they were drawn in, blank detail rows that Forms shows and a web page does not, a date item's default of today. |
| waived | A source behaviour the customer has decided not to replicate, under a named waiver. |
| defect | A difference to repair. |
| unmapped | An item the mapping does not name (for example the Forms record indicator on a page that has none). |
The tolerance policy is agreed with the customer before the migration starts. A diff with no defect means: no difference in the behaviour the scenarios exercise, under that policy.
5. Repair loop
A repair agent gets the classified diff, the golden records, the scenarios, the mapping, the policy, the waivers and the target's source. It does not get the Forms source. It changes the target and the scenarios run again, until no defect is left. On the two applications on this site, one round was enough (evidence).
6. Waivers
Some source behaviour is not worth keeping: a debugging message a developer left in a trigger, a placeholder alert, an error message on start-up, a link that fails on the Forms client. Each becomes a waiver with an id, a title and the reason, pinned to the exact source and target values it covers. The diff lists every waiver with the number of values it applied to, and a different difference at the same place stays a defect. Waivers are proposed by the agent and decided by the customer.
What runs where
At a customer, the runner, the collector and the coverage tooling run on a client host next to the
customer's own licensed Forms Services and database. They need one test schema that can be restored, a runner
account with object grants only (a DBA creates it once), and the record=names and diagnostics settings in a Forms Services
configuration section. Scenarios, golden records and diffs are plain text and JSON files. What leaves the
customer's site is decided with the customer.