Project

General

Profile

Bug #11631

fix issues with persistent UI triggers

Added by Constantin Asofiei 3 months ago. Updated 1 day ago.

Status:
Review
Priority:
Urgent
Target version:
-
Start date:
Due date:
% Done:

100%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:
production:
No
env_name:
topics:

History

#2 Updated by Constantin Asofiei 3 months ago

In #11580 there were fixed urgent problems (leaks) related to persistent UI triggers. This task is meant to fix other remaining issues:
  • an APPLY <event> TO <handle> can't find an ON ANY-KEY [ANYWHERE] trigger
  • in batch mode, ENTRY event in 4GL is not fired via APPLY; in interactive mode, the widget must be SENSITIVE (and attached to frame) for APPLY to find it
  • other issues found from testing

#3 Updated by Razvan-Nicolae Chichirau 3 months ago

  • Assignee set to Razvan-Nicolae Chichirau
  • Status changed from New to WIP

Constantin Asofiei wrote:

  • an APPLY <event> TO <handle> can't find an ON ANY-KEY [ANYWHERE] trigger

Testcase:

def var fi as char view-as fill-in.
def button btn.
def frame f fi skip btn.

def var hFi as handle.
hFi = fi:handle.

on ANY-KEY of hFi do:
    message "ANY-KEY".
end.

on choose of btn do:
    apply "1" to hFi.
end.

enable all with frame f.
apply "entry" to btn.
wait-for window-close of current-window.

  • in batch mode, ENTRY event in 4GL is not fired via APPLY; in interactive mode, the widget must be SENSITIVE (and attached to frame) for APPLY to find it

Please post the relevant testcases here. I wasn't able to reproduce the issue.

  • other issues found from testing

Can you describe them?

#4 Updated by Constantin Asofiei 3 months ago

The tests are in testcases rev 1854. They should be ran in both standalone and test-suite mode. They pass in OE, but needs to be double-checked. If ran individually, these are meant as 'batch programs'.

All checks I did until now were in batch mode (non-interactive).

About ENTRY, LEAVE, etc: the point is to have the widget not attached to a frame, not realized, etc. ENTRY from my experimenting is sent only if the widget is realized and SENSITIVE.

IMO, we will need to remove the EventList.lookup(long resourceId, boolean trigExit, TriggerMatch result) and use the main event lookup method. For this, we will need to be able to resolve easily the widget ID (if any) for a certain resource ID. I propose a maintained map in the WidgetRegistry, for (resourceID, widgetID) pairs. So that when we have a apply "x" to <resource>." we can resolve fast the widget ID and call the event lookup method. The problem: when destroying a widget, this widget needs to be removed from this map - so we need also (widgetID->resourceID) mapping.

Please thing about the above and lets find best way to do this. What I'm pretty sure we can't short-circuit the lookup when we have a resource ID as target.

Events in 4GL can be applied to any resource, but not all resources will execute them. For example, looks like ENTRY to a proc-handle will be a no-op, even if the proc-handle has a ON ENTRY OF proc-handle PERSISTENT RUN.
  • user-defined events like U1, U2, etc
  • key events like a, b, c
  • widget-level events like ENTRY, LEAVE, CHOOSE, VALUE-CHANGED, etc
  • anything else I'm missing
  • widget state can be not-realized, realized, visible, sensitive.

Automation can be tested in batch mode. In interactive mode, is more complex.

#6 Updated by Razvan-Nicolae Chichirau 20 days ago

Can confirm the suite commited in testcases rev. 1854 passes fully in OE when executed in standalone or suite mode. Ran the same suite in FWD with trunk rev. 16765, this is the JUnit output:

#7 Updated by Razvan-Nicolae Chichirau 20 days ago

First problem that I will analyze is in TestValidVersusInvalidResource.p:

   /* (a) VALID resource: register + apply. hFill is not attached to a frame, so
      ENTRY (a focus event) must not fire */
   create fill-in hFill.

   on 'ENTRY':U of hFill persistent run onEntry.

   apply 'ENTRY':U to hFill.

   Assert:Equals(0, iEntry). // FWD: 1, OE: 0

I checked in OpenEdge 12.8 (GUI + ChUI) whether PERSISTENT/NON-PERSISTENT triggers run when an event is sent with APPLY to a STATIC/DYNAMIC widget that has no frame (only for dynamic widgets), is not realized, is hidden, is not sensitive or is not enabled.

The testcase ran every combination of widget type x state x event: 8 x 7 x 39 = 2184 cases. Each case uses a fresh widget and frame, registers a trigger for every event in the list, sends one event with APPLY and records which triggers ran.
  • Widget types: FILL-IN, BUTTON, TOGGLE-BOX, EDITOR, COMBO-BOX, SELECTION-LIST, RADIO-SET, SLIDER.
  • States:
    • CONTROL: visible and sensitive.
    • NO-FRAME: FRAME never set.
    • NOT-REALIZED: FRAME set, frame never viewed.
    • HIDDEN: realized, then HIDDEN = TRUE.
    • NOT-SENSITIVE: SENSITIVE set to TRUE, then FALSE.
    • NOT-ENABLED: visible, SENSITIVE never set.
    • FRAME-NOT-SENSITIVE: like CONTROL, but the frame's SENSITIVE = FALSE.
  • Events (39): ENTRY, LEAVE, VALUE-CHANGED, CHOOSE, DEFAULT-ACTION, GO, RETURN, TAB, BACK-TAB, HELP, END-ERROR, ENDKEY, ERROR, the cursor keys, HOME, END, PAGE-UP, PAGE-DOWN, BACKSPACE, DELETE-CHARACTER, a, b (no own trigger, so it should reach ANY-PRINTABLE), ANY-PRINTABLE, 10 mouse events and U1, U2, U10.

All 8 widget types were run to completion. 2107 cases produced a result and 77 crashed the session in OE.

Results

The same for PERSISTENT/NON-PERSISTENT triggers, STATIC/DYNAMIC widgets, GUI/ChUI:

State ENTRY trigger Other 38 events
No frame not run (0/8 types) run
Frame set, not realized not run (0/8) run
Realized but hidden FILL-IN: not run. Other 7 types: run, and the hidden widget gets focus run
Not sensitive not run (0/8) run
Not enabled not run (0/8) run
frame not sensitive, widget sensitive run (8/8), gets focus run
CONTROL run (8/8), gets focus run

Conclusion

Based on the provided results, the ENTRY trigger runs only when the widget can take focus:

  1. The widget must belong to a frame, and that frame must be realized. APPLY "ENTRY" does not realize the widget.
  2. The widget itself must have SENSITIVE = TRUE. It makes no difference whether it was disabled after being enabled or never enabled.
  3. HIDDEN = TRUE blocks ENTRY only for FILL-IN. For the other 7 types a hidden (but realized and sensitive) widget still runs the ENTRY trigger and receives focus.
  4. SENSITIVE = FALSE on the parent frame does not block ENTRY; only the widget's own SENSITIVE matters.

Note: The crashes from OE were happening when calling APPLY with certain events. The crash reason was an ACCESS_VIOLATION from the .exe file, so it is a 4GL bug.

#8 Updated by Razvan-Nicolae Chichirau 20 days ago

P.S.: I've done the same testing for frames, and there is one single difference from the results posted in #11631-7. An APPLY "ENTRY" for a frame with SENSITIVE = false invokes the entry trigger on ChUI, but not in GUI.

#9 Updated by Razvan-Nicolae Chichirau 19 days ago

I was wondering whether APPLY "ENTRY" was only reduced to the trigger not being executed, while other side effects, such as a focus change, could still occur.

During my testing, I observed the following behavior when APPLY "ENTRY" did not execute the associated trigger:
  • The FOCUS handle didn't change at all.
  • The VISIBLE, HIDDEN, and SENSITIVE attributes of the widget / frame didn't changed either
  • There are no other triggers that run on the widget, like a LEAVE or VALUE-CHANGED
  • There are no errors or conditions raised.

With these in mind, I think it's safe to say that an APPLY "ENTRY" is basically just a NO-OP in these conditions and the call can be stopped before reaching the client.

#10 Updated by Razvan-Nicolae Chichirau 13 days ago

As Constantin pointed out in #11631-4, events in 4GL can be applied to any resource, but not all resources will execute them. I made an OE testcase which applies 56 events (focus, widget, window, procedure, socket/query, universal keys, navigation keys, printable keys, mouse, developer events U1..U10) to most of the non-widget resources, and also to windows, frames, menus in different states. This was executed in GUI and ChUI interactive + batch mode.

1. Non-widget resources (procedure, buffer, query, temp-table, dataset, data-source, socket, XML, call, etc...)

With a specific trigger (ON ev OF h), in GUI and ChUI, every event executes the trigger, except ENTRY:

  • ENTRY is a silent no-op: the trigger is not executed and no error is raised.
  • LEAVE does execute the trigger, on THIS-PROCEDURE and on any persistent procedure handle.
  • SELF inside the trigger is the resource itself, procedure handles included.

2. Catch-all triggers (the same for all the resource types, windows, frames and menus included)

Trigger What it catches via APPLY
ANY-KEY OF h only key labels (a, 1, b, F5) and U1..U10; never key functions (GO, RETURN, TAB, CURSOR-*, HELP, END-ERROR, ...) and never widget events
ANY-PRINTABLE OF h only a, 1, b
ON ev ANYWHERE the same as the specific trigger: everything except ENTRY, non-widget resources included
ON ANY-KEY ANYWHERE, ON ANY-PRINTABLE ANYWHERE the same as ANY-KEY OF h / ANY-PRINTABLE OF h

So APPLY <event> TO <handle> must find the ANY-KEY, ANY-PRINTABLE and ANYWHERE triggers for all the resource types, not only for widgets.

3. ChUI batch vs ChUI interactive

Regarding the trigger of the applied event itself, the only difference between ChUI batch and interactive is APPLY "ENTRY" to a widget:

Case (APPLY "ENTRY") ChUI interactive ChUI batch
FILL-IN, sensitive, frame made visible the FILL-IN is visible: executes the trigger, gets the focus the FILL-IN is not visible: never executes the trigger (the same as a hidden FILL-IN in interactive ChUI)
other field-level widgets (button, toggle-box, combo-box, selection-list, radio-set, slider, browse), sensitive, frame made visible executes the trigger, gets the focus executes the trigger, gets the focus (the same as a hidden widget in interactive ChUI)
widget in a frame which is not realized, with SENSITIVE = TRUE (button, toggle-box, combo-box, selection-list, radio-set, slider, browse) does not execute the trigger executes the trigger and gets the focus
frame not realized does not execute the trigger executes the trigger

4. APPLY "ENTRY", other cases

  • a window executes the ENTRY trigger only when it is SENSITIVE (VISIBLE and HIDDEN do not matter).
  • a frame must be realized; in GUI it must also be SENSITIVE, in ChUI a not sensitive frame still executes the trigger.
  • menus (menu-bar, sub-menu, menu-item, popup) and dynamic widgets which are not attached to a frame: silent no-op.
  • IMAGE, RECTANGLE and TEXT never execute the ENTRY trigger, even when SENSITIVE = TRUE.
  • OCXs such as TreeView, PSTimer or an empty CONTROL-FRAME: the APPLY statement can only be made for handles, not com-handles, so you can only apply events to the CONTROL-FRAME. Testing revealed that only the widget SENSITIVE attribute matters.

Important note: The implemented code logic will not apply to third-party (pluggable) widgets. Thus, the server will not suppress the ENTRY event in this case.

Results with the tests from testcases rev. 1854:
  • trunk: 12 failed, 5 successful
  • 11631a: 6 failed, 11 successful

I'll consider the APPLY investigation complete. Moving to the issues with ON ANY-KEY [ANYWHERE] triggers.

#11 Updated by Razvan-Nicolae Chichirau 6 days ago

  • Status changed from WIP to Review
  • % Done changed from 0 to 100
  • reviewer Constantin Asofiei added

Constantin: Please review 11631a/rev. 16767.

I found out that an ENTRY event on widgets that can't process it directly can have side-effects, as opposed to the claims made in #11631-9. For example an entry on a non-sensitive widget can raise the entry for its frame, or even its window.

As such, I had to re-evaluate my research and implementation. Tested APPLY "ENTRY" on 15 widget and frame types in 20 states (sensitive, hidden, frame never realized, attributes changed right before the APPLY, etc.). The suite also covered nested frames, resources, menus and windows, implicit APPLY, APPLY from inside triggers and repeated APPLYs. Ran the suite in OE, FWD trunk and FWD 11631a, in GUI, ChUI and batch.

In addition, the suite tests the catch-all triggers, in GUI and ChUI: APPLY <ev> TO T with 22 trigger sets (ON ev OF T, ANY-KEY / ANY-PRINTABLE OF T, ON ev ANYWHERE, ANY-KEY / ANY-PRINTABLE ANYWHERE, ON ev / ANY-KEY of the frame and of the window, with and without ANYWHERE; all of them together, then without the winning trigger one at a time, to get the precedence order), 12 targets (query, buffer, procedure, X-document, popup menu-item, frameless button, button and fill-in in a frame, not sensitive button, button in a never realized frame, frame, window) and 17 events (a, 1, F5, CTRL-A, DEL, RETURN, TAB, U1, U10, GO, CHOOSE, ENTRY, INS, BACKSPACE, HOME, ,CURSOR-UP, DELETE-CHARACTER).

In OE, 3533 APPLY cases were run (mostly ENTRY); 3205 could be compared in all three runs, the rest use widgets the FWD ChUI client does not support (e.g. SLIDER).

Class Meaning
OK branch = OE, trunk = OE
FIXED branch = OE, trunk โ‰  OE
REGRESSION branch โ‰  OE, trunk = OE
STILL-DIFF branch = trunk โ‰  OE (older FWD difference)
CHANGED branch โ‰  trunk, both โ‰  OE

APPLY "ENTRY"

Mode OK FIXED REGRESSION CHANGED STILL-DIFF
GUI 309 204 0 13 853
ChUI interactive 10 0 0 131 847
ChUI batch 145 107 10 154 422

In batch, the regressions and most CHANGED cases run the same triggers as OE; they differ only in the frame's VISIBLE read before the APPLY: yes (the server value), OE: no. The suite's APPLY "ENTRY" to a SENSITIVE fill-in before each case is now dropped, so no client call refreshes it first (an older FWD difference). Comparing only the triggers which ran: 159 / 107 / 0 / 4 / 568.

ANY-KEY / ANY-PRINTABLE / ANYWHERE

For resources the lookup stopped at exact matches, and an applied key was not treated as a key event; both are fixed. In OE:
  • precedence: ON ev OF h > ANY-PRINTABLE / ANY-KEY OF h > ON ev ANYWHERE > ANY-PRINTABLE / ANY-KEY ANYWHERE, for every target;
  • ANY-KEY catches an applied a, 1, F5, CTRL-A, INS, U1, U10, not DEL, BACKSPACE, HOME, CURSOR-UP, DELETE-CHARACTER, RETURN, TAB, GO, CHOOSE, ENTRY;
  • ChUI: no window trigger runs for a widget in a never realized frame (GUI: it runs).
Mode Compared OK FIXED REGRESSION CHANGED STILL-DIFF
GUI 3419 1985 931 0 86 417
ChUI interactive 4366 2773 1487 0 2 104

No batch row: in batch, a trigger defined by an ON statement inside a DO ON ERROR UNDO, LEAVE block with a CATCH stays active after its procedure ends (trunk too; in OE it ends with the procedure), so leftover triggers fire in later cases.

With these 2 solutions in place, I ran the suite from rev. 1854:

I've also investigated the remaining 3 failed tests. When applying the event, getWidget() on client returns null because the widget is not in the registry, so the event ends up being rerouted to the default window. This is another, separate bug from 11631a. I will treat it in 11631b.

#12 Updated by Alexandru Lungu 1 day ago

  • Priority changed from Normal to Urgent

Constantin: Please review 11631a/rev. 16767.

Constantin, please review with high priority. The related #11731 from the customer is meant to reach production soon and this fix is required.
Otherwise, do you think somebody from UI side is more fit to review?

#13 Updated by Constantin Asofiei 1 day ago

Razvan, please rebase. Also, please point me where the tests you've created are.

#14 Updated by Razvan-Nicolae Chichirau 1 day ago

  • File regress_11631.p added

Rebased 11631a to trunk rev. 16796. Attached the tests. The results were gathered by comparing the log file of OE and FWD, GUI, ChUI + batch, with a script.

#15 Updated by Constantin Asofiei 1 day ago

  • File deleted (regress_11631.p)

#16 Updated by Constantin Asofiei 1 day ago

Razvan, please clean these up as unit-tests and commit them to testcases project.

#17 Updated by Razvan-Nicolae Chichirau 1 day ago

Unit tests are executed in batch mode, right? There are over 6k tests in this suite, and the behavior is different between gui/chui interactive and batch, so executing only in batch mode will tell half the story. Is any reliable way of testing the 3 modes with unit tests?

#18 Updated by Constantin Asofiei 1 day ago

Then this raises another question: what modes did you run the scenarios in the test? We need GUI, ChUI and batch.

The batch version of the tests can be re-written as unit-tests, at least.

#19 Updated by Razvan-Nicolae Chichirau 1 day ago

I ran the suite in all 3 modes. You can see the test results being listed for all modes.

#20 Updated by Greg Shah 1 day ago

Razvan-Nicolae Chichirau wrote:

Unit tests are executed in batch mode, right? There are over 6k tests in this suite, and the behavior is different between gui/chui interactive and batch, so executing only in batch mode will tell half the story. Is any reliable way of testing the 3 modes with unit tests?

Using Sikuli to Automate Interactive ABLUnit Tests

We are moving to Lyra which is even better for this but for which we need some work to implement/integrate the approach. See Testing.

#21 Updated by Razvan-Nicolae Chichirau 1 day ago

Greg, Sikuli is not needed here as the suite just applies some events like ENTRY or LEAVE and checks what triggers were executed and the current values of some attributes.

Constantin, I managed to run the unit tests as GUI and ChUI batch mode directly from Developer Studio. For ChUI, I had to run the unit tests from cmd:

"%DLC%\bin\_progres.exe" -p run_ablunit.p -param "CFG=ablunit.json"

All @Test methods will pass 3 expected results to one common endpoint which will execute the actual test and decide with what we should compare the result with, based on the current mode. In this way, we can have all 3 modes covered.

Also available in: Atom PDF