Login
Artifact [69408ead09]
Login

Artifact 69408ead09897ac9e1ffc07998702fa9a1935a2a:


/* Test/demo script for the Fossil v2 th1ish bindings. */
[import {test-common.th1ish}]

scope { /* Some generic test code... */
    const FI = Fossil.file
    assert [Fossil.file.isAccessible __FILE]
    assert ![Fossil.file.isAccessible __FILE + '.nope']
    assert [FI.isFile __FILE]
    assert ![FI.isFile __FILE + '.baz']
    assert 0 < [FI.size __FILE]
    [print "This script's name: " __FILE ' ==canonical==> ' [FI.canonicalName __FILE]]

    //[print __SRC ': mtime of this script: ' [strftime '%c' [FI.mtime __FILE]]]
    var repoDb = ARGV.flags.r
    const f = [Fossil.createContext object{
        traceSql: ARGV.flags.S,
        repoDb: repoDb,
        checkout: ARGV.flags.c ? [getenv 'PWD'] : undefined
    }]
    if(repoDb){
        [print __SRC ': Repo db: ' f.db.repo]
    }


    var x = [f.selectVal {
        SELECT value FROM ckout.vvar WHERE name='repository'
    }]
    [print 'selectVal = ' x]

    assert f inherits Fossil.Context
    [print __SRC ': f=' f ', f.db=' f.db]
    //[api.ob.push]
    if(1 && f.db){
        var list = array['repo', 'checkout']
        const n = [list.length]
        for{var k, name, i = 0}{i<n}{i+=1}{
            k = list.(i)
            name = f.db.(k)
            name || continue
            [print __SRC ": Db details: " k name]
            //name = db.name
            [print "\tDB file: " name]
            [print '\tSize:\t' [FI.size name]]
            [print '\tmtime:\t' [strftime '%c' [FI.mtime name]]]
        }
    }
    //[api.ob.pop]


    assert f.db
    if(f.db.repo){
        [print "Stash entries:" f.selectVal]
        [f.dumpQuery
            {SELECT stashid AS stashid,
                vid AS vid,
                datetime(ctime) AS ctime,
                comment AS comment
             FROM stash
             ORDER BY stashid DESC
            }
            undefined // bind parameter(s, array)
            '\t'  // record separator
        ]
        [f.closeRepo]
        assert !f.db.repo
        assert 'exception' === typename catch{[f.closeRepo]}
    }


    //[f.finalize] // optional (or let GC take care of it)

    /**
        [f.openDb ...] works slightly differently than
        [Fossil.openDb]. If a FossilContext is the 'this' of
        an openDb() call then the db connection gets the array
        of fossil-specific DB functions installed and it may
        (depending on how it's called) require that the db
        be a valid repo. The "extra features" include that the
        db will use the Fossil Context's traceSql setting.
    */

    /**
        openMode is a string which describes how to open/interpret a db file.
        The string can contain any of the characters 'r', 'w', 'c', 'v'.
        The default access mode is 'rwc' (largely for historical
        reasons). If the string contains 'w', write mode is added, and
        'c' means "create if not exists"
        and implies 'w'. The 'v' flag means to verify that the db is
        a repository. This validation currently only works when the db
        is opened via Context.openDb(), not Fossil.openDb() (they are
        the same function, they just interpret the 'this' argument
        differently).
    */
    var openMode = 'c'
    var db = [f.openDb
        //":memory:"
        __FILE+'.db'
        openMode
    ]
    [print __SRC ': db=' db]
    var ex = catch {
        [db.execMulti {
            BEGIN;
            DROP TABLE IF EXISTS t;
            CREATE TABLE t(a,b);
            COMMIT;
        }]

        var st = [db.prepare "INSERT INTO t (a,b) VALUES (?,?)"]
        [print __SRC ": Prepared statement: " st]
        assert 2 === st.parameterCount
        assert 0 === st.columnCount

        var buf = [api.Buffer 5]
        [buf.append 'bbb']
        [st.bind 1 'aaa']
        [st.bind 2 buf]
        [st.step]

        [st.reset]
        if(0){
            const FI = Fossil.file
            [buf.capacity [FI.size __FILE] + 2]
            [buf.length 0]
            [buf.readFile __FILE]
            var sz = [buf.length]
            assert sz > 0
            [buf.compress]
            [st.bind array['ccc',buf]]
        }else{
            [st.bind array['ccc','ddd']]
        }
        unset buf
        [st.step]
        [st.finalize]
        assert 304 === catch{[st.finalize]}.code

        st = [db.prepare "SELECT a a,b b FROM t WHERE ? ORDER BY ROWID"]
        assert 2 === st.columnCount
        assert 1 === st.parameterCount
        assert 2 === [st.columnNames.length]
        [st.bind 1 1]
        assert [st.step]
        assert 'aaa' === [st.get 0]
        var b = [st.get 1]
        ('buffer'===typename b) && (b = [b.toString])
        assert 'bbb' === b

        [st.reset]
        var a = [st.stepArray]
        assert 'array' === typename a
        assert 'aaa' === a.0
        b = a.1
        ('buffer'===typename b) && (b = [b.toString])
        assert 'bbb' === b

        [st.reset]
        var o = [st.stepObject]
        assert 'object' === typename o
        assert 'aaa' === o.a
        b = o.b
        ('buffer'===typename b) && (b = [b.toString])
        assert 'bbb' === b

        [st.finalize]        
    }
    ex && throw ex//[print __SRC ": EXCEPTION:" ex]

    const stringify = proc(v){
        var b = [api.Buffer]
        [b.appendf '%1$J' v]
        return [b.toString]
    }

    var list = array[]
    db.each(object{
        sql: {SELECT a a, b b FROM t WHERE ?},
        bind: true,
        mode: 1, //1==array (default), 0==object
        callback:proc(){
            assert 2 === [columnNames.length]
            assert 'a' === columnNames.0
            assert 'b' === columnNames.1
            assert rowNumber > 0
            /** BUG: when this callback is a string,
                the script name is unknown in assertion trace
                and exceptions here, and line/col are relative to
                this callback string. This is because that level
                of script bindings doesn't have the call location
                info and the script engine cannot figure it out
                because of how src locations/script names are
                figured out on demand. Not sure how to fix that
                (needs to be done (or enabled) at the th1ish
                level).
            */
            /* __SRC is also (of course) wrong here if callback
               is-a String/Buffer.
            */
            [list.push [clone this]]
            [print __SRC ': row #' rowNumber '=' this]
        }
    })
    [print 'Collected results:' list]

    [db.close]
    assert 304 === catch{[db.close]}.code
}

0 && scope {
    // Holy cow, zlib is a memory hog: it mallocs 64k
    // for each de/compression op.
    const FI = Fossil.file
    var b = [api.Buffer [FI.size __FILE] + 2]
    [b.readFile __FILE]
    assert b inherits api.prototypes.Buffer
    var sz = [b.length]
    assert sz > 0
    [b.compress]
    var szC = [b.length]
    assert szC < sz
    print("Compressed buffer: ",sz," ==> ",szC)
    var zFile = __FILE+'.z';
    [b.writeToFile zFile]
    print("Compressed copy written to ",zFile);
    assert szC === [FI.size zFile]
    [b.uncompress]
    assert sz === [b.length]
    print("Decompressed buffer: ",szC," ==> ",sz)
    //print(b.toString())
}

0 && scope {
    var b = [api.Buffer 100]
    [b.append {abcdefghijklm}]
    var bb = [clone b]
    assert [bb.length] === [b.length]
    //assert bb === b // fixme: Buffer.compare should === here.
    //print('compare',[b.compare bb])
    //assert 0 === [b.compare bb]
    for {var i = 0, sz=[bb.length]}{i<sz}{i+=1}{
        assert [b.byteAt i] === [bb.byteAt i]
    }
}