Ask Reuben

Starting With Genero Ghost Client (GGC)

I can’t get the Genero Ghost Client to work?

How does the Genero Ghost Client work?

What is the Genero Ghost Client?

When people start using our newer products I often see them jumping in the deep end, trying to run before they can walk, biting off more they can chew etc.  and their first experiences are not as positive as they can be.  Over time I am going to do a series of articles entitled “Starting …” where I will explain what I think are the key concepts points you should understand with our products and try and make your initial experience a positive one.  Today I will look at Genero Ghost Client (or GGC).

Introduction

Lets start off with the name “Ghost”, where does that come from.  You have hopefully seen how we are able to render the same code to the various front-ends through the concept of the Dynamic User Interface, the  Abstract User Interface (AUI) tree, and the front-end protocol.  If you look at this diagram …

… you will see a rectangle  labelled Front-End.  This is the Genero Desktop Client, the Genero Browser Client, and inside a Genero Mobile app there is also a thread doing the same thing on the mobile device.  This front-end is communicating with the fglrun process (the runtime system), displaying the AUI tree on the front-end and sending back the user input.  The Genero Ghost Client is a program that is another instance of this rectangle labelled Front-End that is having the same communication with the fglrun process only instead of displaying to a screen, as a program it is checking the values in the AUI tree and sending back simulated user input  back to the fglrun process.  It is called Ghost because you can’t see it on the screen.

So in whiteboard sessions I like to draw …

fglrun <---> GDC

fglrun <---> GBC

fglrun <---> GMI

fglrun <---> GMA

fglrun <---> GGC

… to emphasise that it is receiving and communicating with the fglrun process the same as the other clients.

The Genero Ghost Client is shipped inside FGLDIR/testing_utilities so you won’t find it as a seperate download in the downloads area of the website, but you will find its documentation available separately in the documentation area of the website.  Like all documentation, an online copy is available with the latest version.  Included in FGLDIR/envcomp is the minimum environment necessarily to run Ghost Client but you will need to ensure JAVA_HOME is set as per the Install and Configure section of the documentation.

My First Test

To get an introduction what I suggest is that rather than testing one of your programs, is to first do a test using a simple little calculator program.

#! calculator.4gl
MAIN
DEFINE a,b,c INTEGER 

    DEFER INTERRUPT
    DEFER QUIT
    OPTIONS FIELD ORDER FORM
    OPTIONS INPUT WRAP
    CALL ui.Dialog.setDefaultUnbuffered(TRUE)

    CLOSE WINDOW SCREEN
    OPEN WINDOW w WITH FORM "calculator"

    -- DISPLAY ui.Interface.getFrontEndName()
    
    INPUT BY NAME a, b,c  ATTRIBUTES(WITHOUT DEFAULTS=TRUE, ACCEPT=FALSE, CANCEL=FALSE)
        ON ACTION add
            LET c = add(a,b)
           
        ON ACTION close
            EXIT INPUT
    END INPUT
END MAIN

FUNCTION add(a INTEGER,b INTEGER) RETURNS INTEGER
    RETURN a+b
END FUNCTION
#! calculator.per
LAYOUT (TEXT="Calculator", MINHEIGHT=10, MINWIDTH=20)
GRID
{
 [f01       ]
+[f02       ]
=[f03       ] [b01       ]

}
END
END
ATTRIBUTES
f01 = formonly.a TYPE INTEGER;
f02 = formonly.b TYPE INTEGER;
f03 = formonly.c TYPE INTEGER, NOENTRY;
BUTTON b01: add, TEXT="Add";

if you run this program, you can hopefully see that entering 1,2 into the two fields and pressing Add displays 3 in the bottom field.  What we are going to do is create a test program to test that this program does this correctly.

In Studio this will involve (similar steps to what is documented here https://4js.com/online_documentation/fjs-gst-manual-html/#gst-topics/t_gst_ggc.html)

  • Right-clicking on the application node of the application to test and selecting Create Test (pictured below).  This will start the application.
  • Enter 1,2 in the two fields and click Add.  You should see 3 appear.
  • Exit this program.
  • In the Code Editor this will create a new untitled 4gl that will look similar to the code pictured below (note the Generated Using comment at the top) …
  • You should then save this .4gl with a name like calculator_test.4gl

To get to the same point from the command line you will need to follow steps similar to the getting started page

  • Run the program generating a gui log
fglrun --start-guilog=calculator_test.log calculator.42r
  • Generate the 4gl from this log using the ggcgen command
ggcgen bdl calculator_test.log

This will generate a .4gl file calculator_test.4gl that is similar to that pictured for Studio above

We are now ready to run this test.

In Studio you need to

  • create an application node named calculator_test
  • add the file saved as calculator_test.4gl to this application
  • add as a command line argument the following (click on application node  and find Command Line Arguments in the properties view)
tcp --command-line "fglrun calculator"
  • build and execute the calculator_test application

To run a test from command line

  • you need to compile the test application
  • run it passing it some special arguments which tell it how to communicate with and launch the program under test
fglcomp calculator_test.4gl
fglrun calculator_test tcp --working-directory $(pwd) --command-line 'fglrun calculator'

In Studio you will see output in the Output panel, from the command line this will be written to stdout, info similar to …

BDL Scenario server is not started.
Starting BDL scenario server on port 6500.
Listening on port 6500
BDL Scenario server not yet started, retry - Retry ..
BDL Scenario server not yet started, retry - Retry ..
SessionManager 0.001 "SessionManager" Create.
SessionManager 0.030 "SessionManager" Create TCP session.
TCPSession close server socket
== Session statistics == Id: 440ae84b9d5b4d96974c64ca317cba78
Duration: 0:00:08.002
Bytes received: 7818
Bytes sent: 356
Scenario count: 1
Scenario failed: 0
Checks failed: 0
ScenarioServer 9.291 "Scenario server stop requested." -
ScenarioServer 9.293 "Scenario server exiting." -
Success

What has happened, is that the test program calculator_test.42r has run the program under test, calculator.42r where the test program calculator_test.42r has acted as the front end.  So instead of “fglrun calculator.42r” communicating with gdc.exe or gbc.js, it has communicated with “fglrun calculator_test.42r”.  The test program “fglrun calculator_test.42r” has received the AUI tree from the program under test  “fglrun calculator.42r”, tested that certain values in the AUI Tree are as expected, and has sent back some simulated user input to the program under test.  There are some bits in the middle making this happen, the Scenario Server as documented here  but that is all under the hood.

Expanding on that First Test

Having shown a successful test what I normally demonstrate then is one of three things to make a test fail.

In calculator.4gl, I change the plus (+) to a minus (-), recompile calculator.4gl and rerun the test.

In the output you should now see …

Checks failed: 1
Check failures:
/Volumes/Daily/github/fourjs-reuben/eap_3_20/trunk/ggc/calculator_test/calculator_test20.4gl:66:error:Field value is not valid, expected: '3', got: '-1'

That is, the test was expecting to see a 3 but instead saw a -1.

This demonstrates what you can expect to happen if the logic that is being tested is altered and returns an unexpected result.

Change the code in calculator.4gl back to a plus before you forget and this time modify calculator_test.4gl.

Now in the calculator_test.4gl, wherever you see a “2”, change that to a “4” (should be three places).  That is instead of testing 1+2, we will test 1+4.

Now when you run calculator_test.4gl you will see in the output …

Checks failed: 1
Check failures:
/Volumes/Daily/github/fourjs-reuben/eap_3_20/trunk/ggc/calculator_test/calculator_test20.4gl:66:error:Field value is not valid, expected: '3', got: '5'

We changed the inputs but we did not change the expected result, so change the one place you see a “3” to a “5” in calculator_test.4gl

Now  compile and run calculator_test again and it should succeed.

Checks failed: 0

What this has illustrated is how you can modify the test program to add more tests.

That is you are not limited to what you recorded, you can modify the test program to perform additional tests.   At an extreme you could record in a file or database table, a whole list of inputs and expected outputs, and modify this test program to iterate through this list of inputs and expected outputs.

A final test is to see what happens if the user input changes.  In calculator_test.4gl, change the values back to test 1+2=3.  In calculator.4gl, change the ON ACTION add to the following …

ON ACTION add
    IF b > 0 THEN
        CALL FGL_WINMESSAGE("Error","Sample Error", "error") 
        NEXT FIELD b
    END IF
    LET c = add(a,b)

… , rebuild the calculator application, and now when you run the test it fails with …

Checks failed: 2
Failures:
/Volumes/Daily/github/fourjs-reuben/eap_3_20/trunk/ggc/calculator_test/calculator_test20.4gl:67:error:(GGC-14) Not implemented. - Getting the value of a 'MenuAction' is not implemented.
Check failures:
/Volumes/Daily/github/fourjs-reuben/eap_3_20/trunk/ggc/calculator_test/calculator_test20.4gl:63:error:Focused field name is not valid, expected: 'formonly.b', got: 'ok'
/Volumes/Daily/github/fourjs-reuben/eap_3_20/trunk/ggc/calculator_test/calculator_test20.4gl:66:error:Field value is not valid, expected: '3', got: '0'

… the key line is the Focused field name is not valid.  The test program was not expecting the MENU inside FGL_WINMESSAGE to be there, it expected the cursor to be in field formonly.b and started reporting errors.  The key lesson is that if the user interface changes, you need to be prepared to amend the test program.  It isn’t going to know how to react if a different window is opened, the field with focus has changed, or the field name has changed since the test was recorded.

The Test Program Code

I didn’t look deeply at the code in the test program.  It should be fairly explanatory but what it consists of is code that is checking certain values in the AUI Tree

CALL ggc.checkWindowName("w")
CALL ggc.checkWindowTitle("Calculator")
CALL ggc.checkFormName("calculator")
CALL ggc.checkFormTitle("Calculator")
CALL ggc.checkFocus("formonly.a")
CALL ggc.checkFieldValue("formonly.a", "0") -- INTEGER
CALL ggc.checkValue("0")
CALL ggc.checkActionActive("add") -- add
CALL ggc.checkNoMessage()
CALL ggc.checkNoError()

and code that simulates user input e.g.

CALL ggc.setValue("1")
CALL ggc.key("Tab")
CALL ggc.action("add")

Hopefully you can see that you could if you wanted

  • modify the test program
  • code from scratch the test program yourself rather than using ggcgen
  • have minimal checks and simply send the code to simulate user input

If you are interested the source for ggc is in $FGLDIR/testing_utilities/ggc/src/ggclib/ggc.4gl

Digging Deeper

That is what I consider the first building block.  If you can get to this point where you can generate a simple test to test a simple program, can run the test, and modify it so you can see what happens, you can now try that on one of your own programs and you will always have this building block you can fallback to.

Some areas you can then look to explore further include…

  • In the tests above, the test program was a 4gl application.  If your testing team have Java skills or you want to get into some benchmark testing, then you should look at how to create the test program in Java.  If you are testing large numbers simultaneously, the test program being written in Java will have some advantages due to threading.
  • As the test program is emulating a front-end, you need to consider the result of Front-calls.  Your test program may need to have functions added to emulate any front-calls made by the program under test.  What was the responsibility of gdc.exe, gbc.js is now the responsibility of the test program.  Also note the –fename argument that can be passed to the test program.
  • The test program is generated using the code snippets inside $FGLDIR/testing_utilities/ggc/template.  Have a read on the section on customizing templates if you want to change what testing code is generated by ggcgen.
  • In the test program you will note the wait() calls.  These can be overridden in the playback or ignored by use of the speed ratio argument.

There is a lot you need to consider for a fully fledged test environment…

  • are you testing an absolute date or TODAY?
  • do you want to add tests for more values?
  • do you want to read test values and results from a database?
  • what state is the test database in at start and end of test? how is it restored to a known point?
  • do you always to test with the same values? e.g. if you always order the same product will it eventually run out of stock? in which case will another dialog appear?
  • is there circumstances where the user interface might follow a different path? e.g. change password every 90 days
  • is there any real world events you don’t want to occur in test environment e.g. Physical printing, SMS alerts, Web Service calls
  • how are you going to run and read the results of hundreds of tests?

There is a lot to setting up a testing suite, getting Genero Ghost Client to generate and run tests is only the start of something much bigger.  If you can get to the point where you can run and modify a simple calculator test, and then repeat what you have learned on one of your own programs, you are in a good position.