FormsRefresh

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:

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.