/*

 *  $Id: TestStress.java,v 1.5 2003/09/21 15:49:02 boisvert Exp $

 *

 *  Package stress test

 *

 *  Simple db toolkit

 *  Copyright (C) 1999, 2000 Cees de Groot <cg@cdegroot.com>

 *

 *  This library is free software; you can redistribute it and/or

 *  modify it under the terms of the GNU Library General Public License

 *  as published by the Free Software Foundation; either version 2

 *  of the License, or (at your option) any later version.

 *

 *  This library is distributed in the hope that it will be useful,

 *  but WITHOUT ANY WARRANTY; without even the implied warranty of

 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

 *  Library General Public License for more details.

 *

 *  You should have received a copy of the GNU Library General Public License

 *  along with this library; if not, write to the Free Software Foundation,

 *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA

 */

package jdbm.recman;



import jdbm.RecordManager;

import jdbm.RecordManagerFactory;



import jdbm.helper.ByteArraySerializer;



import junit.framework.*;

import java.util.Random;



/**

 *  This class contains stress tests for this package.

 */

public class TestStress extends TestCase {



    public TestStress(String name) {

      super(name);

    }



    public void setUp() {

        TestRecordFile.deleteTestFile();

    }



    public void tearDown() {

        TestRecordFile.deleteTestFile();

    }



    // test parameters

    final int RECORDS = 10000;

    final int MAXSIZE = 500;

    final int ROUNDS = 1 * 1000 * 1000;



    final int RPPROMILLE = ROUNDS / 1000;



    Random rnd = new Random(42);





    // holder for record data so we can compare

    class RecordData {

        long rowid;

        int size;

        byte b;



        RecordData(long rowid, int size, byte b) {

            this.rowid = rowid;

            this.size = size;

            this.b = b;

        }



        public String toString() {

            return "slot(" + rowid + ",sz=" + size + ",b=" + b + ")";

        }

    }



    private int getRandomAllocatedSlot(RecordData[] d) {

        int slot = rnd.nextInt(RECORDS);

        while (d[slot] == null) {

            slot++;

            if (slot == RECORDS)

                slot = 0; // wrap

        }

        return slot;

    }



    // holder for root records

    long[] roots = new long[FileHeader.NROOTS];



    private int getRandomAllocatedRoot() {

        int slot = rnd.nextInt(FileHeader.NROOTS);

        while (roots[slot] == 0) {

            slot++;

            if (slot == FileHeader.NROOTS)

                slot = 0; // wrap

        }

        return slot;

    }



    /**

     *  Test basics

     */

    public void testBasics() throws Exception {

        RecordManager recman;



        recman = RecordManagerFactory.createRecordManager( TestRecordFile.testFileName );



        // as this code is meant to test data structure calculcations

        // and stuff like that, we may want to disable transactions

        // that just slow us down.

        //  mgr.disableTransactions();



        RecordData[] d = new RecordData[RECORDS];

        int recordCount = 0, rootCount = 0;

        int inserts = 0, updates = 0, deletes = 0, fetches = 0;

        int rootgets = 0, rootsets = 0;

        int slot = -1;



        try {



            for (int i = 0; i < ROUNDS; i++) {

                if ((i % RPPROMILLE) == 0)

                    System.out.print("\rComplete: "

                        + i/RPPROMILLE + "/1000th");



                // close and re-open a couple of times during the

                // test, in order to check flushing etcetera.

                if ((i % (ROUNDS / 5)) == 0) {

                    System.out.print(" (reopened at round "

                    + i/RPPROMILLE + ")");

                    recman.close();

                    recman = RecordManagerFactory.createRecordManager( TestRecordFile.testFileName );

                    //        recman.disableTransactions();

                }



                // generate a random number and assign ranges to operations:

                // 0-10 = insert, 20 = delete, 30-50 = update, 51 = set root,

                // 52 = get root, rest = fetch.

                int op = rnd.nextInt(100);

                if (op <= 10) {

                    // INSERT RECORD

                    if (recordCount == RECORDS) {

                        i -= 1;

                        continue;

                    }



                    slot = 0;

                    while (d[slot] != null)

                        slot++;



                    d[slot] = new RecordData(0, rnd.nextInt(MAXSIZE),

                    (byte) rnd.nextInt());

                    d[slot].rowid =

                        recman.insert(TestUtil.makeRecord(d[slot].size,

                    d[slot].b));

                    recordCount++;

                    inserts++;

                }

                else if (op == 20) {

                    // DELETE RECORD

                    if (recordCount == 0) {

                        i -= 1;

                        continue;

                    }



                    slot = getRandomAllocatedSlot(d);

                    recman.delete(d[slot].rowid);

                    d[slot] = null;

                    recordCount--;

                    deletes++;

                }

                else if (op <= 50) {

                    // UPDATE RECORD

                    if (recordCount == 0) {

                        i -= 1;

                        continue;

                    }



                    slot = getRandomAllocatedSlot(d);

                    d[slot].size = rnd.nextInt(MAXSIZE);

                    d[slot].b = (byte) rnd.nextInt();

                    recman.update(d[slot].rowid,

                    TestUtil.makeRecord(d[slot].size,

                    d[slot].b));

                    updates++;

                }

                else if (op == 51) {

                    // SET ROOT

                    int root = rnd.nextInt(FileHeader.NROOTS);

                    roots[root] = rnd.nextLong();

                    recman.setRoot(root, roots[root]);

                    rootsets++;

                }

                else if (op == 52) {

                    // GET ROOT

                    if (rootCount == 0) {

                        i -= 1;

                        continue;

                    }



                    int root = getRandomAllocatedRoot();

                    assertEquals("root", roots[root], recman.getRoot(root));

                    rootgets++;

                }

                else {

                    // FETCH RECORD

                    if (recordCount == 0) {

                        i -= 1;

                        continue;

                    }



                    slot = getRandomAllocatedSlot(d);

                    byte[] data = (byte[]) recman.fetch(d[slot].rowid, ByteArraySerializer.INSTANCE );

                    assertTrue("fetch round=" + i + ", slot=" + slot

                    + ", " + d[slot],

                    TestUtil.checkRecord(data, d[slot].size, d[slot].b));

                    fetches++;

                }

            }

            recman.close();

        } catch (Throwable e) {

            e.printStackTrace();

            fail("aborting test at slot " + slot + ": " + e);

        } finally {

            System.out.println("records : " + recordCount);

            System.out.println("deletes : " + deletes);

            System.out.println("inserts : " + inserts);

            System.out.println("updates : " + updates);

            System.out.println("fetches : " + fetches);

            System.out.println("rootget : " + rootgets);

            System.out.println("rootset : " + rootsets);

            int totalSize = 0;

            for (int i = 0; i < RECORDS; i++)

            if (d[i] != null)

                totalSize += d[i].size;

            System.out.println("total outstanding size: " + totalSize);



            //System.out.println("---");

            //for (int i = 0; i < RECORDS; i++)

            //    if (d[i] != null)

            //        System.out.println("slot " + i + ": " + d[i]);

        }



    }



    /**

     *  Runs all tests in this class

     */

    public static void main(String[] args) {

        junit.textui.TestRunner.run(new TestSuite(TestStress.class));

    }

}

