/*------------------------------------------------------------------------
   File        : TestStaticFwdReader
   Purpose     : Theme 9 reader.  Confirms or refutes the one anomaly from theme
                 8: a STATIC DEFINE QUERY with no SCROLLING keyword showed ZERO
                 stale records, while FOR EACH, static SCROLLING, and dynamic
                 queries (with FORWARD-ONLY either way) all prefetched.
   Notes       : Pairs with TestStaticFwdWriter on harness port 8899.

                 A single 0 is weak evidence, so each scenario removes one
                 candidate explanation:

                   SF01  exact repeat of the theme 8 case          reproducible?
                   SF02  pause at K=1                              pause position?
                   SF03  pause at K=17                              pause position?
                   SF04  wider records (fat profile)                record width?
                   SF05  loop idiom: GET NEXT at top of REPEAT      loop shape?
                   SF06  same idiom, but SCROLLING                  control for SF05
                   SF07  a query object used ONLY here              residual state
                                                                    from reuse?
                   SF08  static SCROLLING, standard idiom           control

                 SF01 also logs what OpenEdge reports for the query's own
                 FORWARD-ONLY attribute, for both the plain and the SCROLLING
                 static query - because "forward-only by default" is an
                 assumption about static queries that has not been verified from
                 the runtime itself.
 ----------------------------------------------------------------------*/

using Progress.Lang.*.
using OpenEdge.Core.Assert.
using support.harness.msgbuf.* from propath.

block-level on error undo, throw.

class support.harness.msgbuf.TestStaticFwdReader:

    define variable sync    as MsgBufSync    no-undo.
    define variable fixture as MsgBufFixture no-undo.
    define variable log     as MsgBufLog     no-undo.
    define variable tgt     as MsgBufTargets no-undo.

    /* qPlain is reused across scenarios exactly as theme 8 did; qVirgin is
       touched by one scenario only, so residual state can be ruled out. */
    define query qPlain     for Book.
    define query qVirgin    for Book.
    define query qScrolling for Book scrolling.

    @Before.
    method public void BeforeAll ():
        if valid-object(sync) then
            return.

        session:suppress-warnings = false no-error.

        fixture = new MsgBufFixture().
        fixture:AssertClientServer().
        tgt = new MsgBufTargets().

        log = new MsgBufLog("staticfwd").
        log:StartRun(fixture:ConnectionInfo()).

        sync = new MsgBufSync().
        sync:Connect(8899, "TestStaticFwdReader").
    end method.

    @After.
    method public void AfterAll ():
        if valid-object(log) then
            log:Dump("support/harness/msgbuf/results").
    end method.

    /* ---------------- reproducibility, and what OE says about the query ------- */

    @Test.
    method public void SF01PlainRepeat ():
        define variable iter as integer no-undo.

        Begin("SF01", fixture:PROFILE_SLIM, tgt:PauseAt,
              "static DEFINE QUERY (no SCROLLING) + GET NEXT - exact theme 8 repeat").

        open query qPlain for each Book no-lock.
        ReportAttrs("qPlain after open").

        get next qPlain.
        do while available Book:
            iter = iter + 1.
            log:Row(iter, Book.book-id, Book.new_dt).
            if iter eq tgt:PauseAt then
                sync:ReaderPause("SF01").
            get next qPlain.
        end.
        close query qPlain.

        Finish("SF01").
    end method.

    @Test.
    method public void SF02PlainPauseAt1 ():
        Probe("SF02", fixture:PROFILE_SLIM, 1,
              "static DEFINE QUERY, pause at iteration 1").
    end method.

    @Test.
    method public void SF03PlainPauseAt17 ():
        Probe("SF03", fixture:PROFILE_SLIM, 17,
              "static DEFINE QUERY, pause at iteration 17").
    end method.

    @Test.
    method public void SF04PlainFatRecords ():
        Probe("SF04", fixture:PROFILE_FAT, tgt:PauseAt,
              "static DEFINE QUERY, fat profile (wider records)").
    end method.

    /* ---------------- loop shape ---------------- */

    @Test.
    method public void SF05PlainGetNextAtTop ():
        define variable iter as integer no-undo.

        /* GET NEXT at the top of a REPEAT instead of the bottom of a DO WHILE, in
           case the loop shape - not the query kind - explains the earlier zero. */
        Begin("SF05", fixture:PROFILE_SLIM, tgt:PauseAt,
              "static DEFINE QUERY, GET NEXT at top of REPEAT").

        open query qPlain for each Book no-lock.
        repeat:
            get next qPlain.
            if not available Book then
                leave.
            iter = iter + 1.
            log:Row(iter, Book.book-id, Book.new_dt).
            if iter eq tgt:PauseAt then
                sync:ReaderPause("SF05").
        end.
        close query qPlain.

        Finish("SF05").
    end method.

    @Test.
    method public void SF06ScrollingGetNextAtTop ():
        define variable iter as integer no-undo.

        /* Control for SF05: identical loop shape, SCROLLING query. */
        Begin("SF06", fixture:PROFILE_SLIM, tgt:PauseAt,
              "static DEFINE QUERY SCROLLING, GET NEXT at top of REPEAT").

        open query qScrolling for each Book no-lock.
        repeat:
            get next qScrolling.
            if not available Book then
                leave.
            iter = iter + 1.
            log:Row(iter, Book.book-id, Book.new_dt).
            if iter eq tgt:PauseAt then
                sync:ReaderPause("SF06").
        end.
        close query qScrolling.

        Finish("SF06").
    end method.

    /* ---------------- query-object reuse ---------------- */

    @Test.
    method public void SF07VirginQuery ():
        define variable iter as integer no-undo.

        /* qVirgin is opened here and nowhere else, so nothing this class did
           earlier can be carried in it. */
        Begin("SF07", fixture:PROFILE_SLIM, tgt:PauseAt,
              "static DEFINE QUERY never used by another scenario").

        open query qVirgin for each Book no-lock.
        ReportAttrs("qVirgin after open").

        get next qVirgin.
        do while available Book:
            iter = iter + 1.
            log:Row(iter, Book.book-id, Book.new_dt).
            if iter eq tgt:PauseAt then
                sync:ReaderPause("SF07").
            get next qVirgin.
        end.
        close query qVirgin.

        Finish("SF07").
    end method.

    @Test.
    method public void SF08ScrollingControl ():
        define variable iter as integer no-undo.

        Begin("SF08", fixture:PROFILE_SLIM, tgt:PauseAt,
              "static DEFINE QUERY SCROLLING + GET NEXT (control)").

        open query qScrolling for each Book no-lock.
        get next qScrolling.
        do while available Book:
            iter = iter + 1.
            log:Row(iter, Book.book-id, Book.new_dt).
            if iter eq tgt:PauseAt then
                sync:ReaderPause("SF08").
            get next qScrolling.
        end.
        close query qScrolling.

        Finish("SF08").
    end method.

    /* ---------------- shared ---------------- */

    /* What the runtime itself reports, rather than what the manual implies about
       defaults.  Guarded with NO-ERROR because attribute access on a static query
       name is not uniformly supported. */
    method private void ReportAttrs (atPoint as character):
        define variable fo as logical no-undo.

        fo = query qPlain:forward-only no-error.
        if not error-status:error then
            log:Note(substitute("&1: qPlain FORWARD-ONLY=&2", atPoint, fo)).
        else
            log:Note(substitute("&1: qPlain FORWARD-ONLY unreadable (&2)",
                                atPoint, error-status:get-message(1))).

        fo = query qScrolling:forward-only no-error.
        if not error-status:error then
            log:Note(substitute("&1: qScrolling FORWARD-ONLY=&2", atPoint, fo)).
    end method.

    method private void Probe (scn as character, profile as character,
                              pauseAt as integer, queryText as character):
        define variable iter as integer no-undo.

        Begin(scn, profile, pauseAt, queryText).

        open query qPlain for each Book no-lock.
        get next qPlain.
        do while available Book:
            iter = iter + 1.
            log:Row(iter, Book.book-id, Book.new_dt).
            if iter eq pauseAt then
                sync:ReaderPause(scn).
            get next qPlain.
        end.
        close query qPlain.

        Finish(scn).
    end method.

    method private void Begin (scn as character, profile as character,
                              pauseAt as integer, queryText as character):
        fixture:Build(profile).
        log:StartScenario(scn, fixture:Profile, fixture:SampleRecordLength(),
                          pauseAt, queryText, "blanket update Book -> V2").
        sync:ReaderReady(scn).
    end method.

    method private void Finish (scn as character):
        sync:ReaderDone(scn).
        log:EndScenario().
        Assert:Equals(fixture:RecordCount, log:ScenarioRows).
    end method.

end class.
