/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2005
*      Sleepycat Software.  All rights reserved.
*
* $Id: LogManagerTest.java,v 1.53 2004/12/22 14:11:45 linda Exp $
*/

package com.sleepycat.je.log;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import junit.framework.TestCase;

import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DbInternal;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.utilint.DbLsn;
import com.sleepycat.je.util.TestUtils;
import com.sleepycat.je.utilint.Tracer;
  
/**
 * Test basic log management.
 */
public class LogManagerTest extends TestCase {

    static private final boolean DEBUG = false;

    private FileManager fileManager;
    private LogManager logManager;
    private File envHome;
    private EnvironmentImpl env;

    public LogManagerTest() {
        super();
        envHome = new File(System.getProperty(TestUtils.DEST_DIR));
    }

    public void setUp() throws DatabaseException, IOException  {
        TestUtils.removeFiles("Setup", envHome, FileManager.JE_SUFFIX);
        TestUtils.removeFiles("Setup", envHome, FileManager.DEL_SUFFIX);
    }
    
    public void tearDown() throws IOException, DatabaseException {
        TestUtils.removeFiles("TearDown", envHome, FileManager.JE_SUFFIX);
    }

    /**
     * Log and retrieve objects, with log in memory
     */
    public void testBasicInMemory() throws DatabaseException {
        EnvironmentConfig envConfig = new EnvironmentConfig();
        envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(), "6");
        envConfig.setConfigParam(
                            EnvironmentParams.LOG_FILE_MAX.getName(), "1000");

        envConfig.setAllowCreate(true);
        env = new EnvironmentImpl(envHome, envConfig);
        fileManager = env.getFileManager();
        logManager = env.getLogManager();

        logAndRetrieve();
        env.close();
    }

    /**
     * Log and retrieve objects, with log completely flushed to disk
     */
    public void testBasicOnDisk() throws Throwable {
        try {
            /* 
             * Force the buffers and files to be small. The log buffer
             * is actually too small, will have to grow dynamically. Each file
             * only holds one test item (each test item is 50 bytes)
             */
            EnvironmentConfig envConfig = new EnvironmentConfig();
            envConfig.setConfigParam(
                            EnvironmentParams.LOG_MEM_SIZE.getName(),"80");
            envConfig.setConfigParam(
                            EnvironmentParams.NUM_LOG_BUFFERS.getName(), "2");
            envConfig.setConfigParam(
                            EnvironmentParams.LOG_FILE_MAX.getName(), "79");
            envConfig.setConfigParam(
                            EnvironmentParams.NODE_MAX.getName(), "6");
            
            /* Don't checkpoint utilization info for this test. */
            DbInternal.setCheckpointUP(envConfig, false);

            /* 
             * Don't run any daemons, those emit trace messages and
             * other log entries and mess up our accounting.
             */
            envConfig.setConfigParam(
                     EnvironmentParams.ENV_RUN_CLEANER.getName(),
                     "false");
            envConfig.setConfigParam(
                      EnvironmentParams.ENV_RUN_CHECKPOINTER.getName(),
                      "false");
            envConfig.setConfigParam(
                      EnvironmentParams.ENV_RUN_EVICTOR.getName(),
                      "false");
            envConfig.setConfigParam(
                      EnvironmentParams.ENV_RUN_INCOMPRESSOR.getName(),
                      "false");
            envConfig.setAllowCreate(true);
            // Recreate the file manager and log manager w/different configs
            env = new EnvironmentImpl(envHome, envConfig);
            fileManager = env.getFileManager();
            logManager = env.getLogManager();

            logAndRetrieve();

            /*
             * expect 13 je files, 7 to hold logged records, 1 to hold
             * root, 3 to hold recovery messages, 2 for checkpoint records
             */
            String [] names = fileManager.listFiles(FileManager.JE_SUFFIXES);
            assertEquals("Should be 13 files on disk", 13, names.length);
        } catch (Throwable t) {
            t.printStackTrace();
            throw t;
        } finally {
            env.close();
        }
    }

    /**
     * Log and retrieve objects, with some of log flushed to disk, some
     * of log in memory
     */
    public void testComboDiskMemory() throws Throwable {
        try {
            /* 
             * Force the buffers and files to be small. The log buffer
             * is actually too small, will have to grow dynamically. Each file
             * only holds one test item (each test item is 50 bytes)
             */
            EnvironmentConfig envConfig = new EnvironmentConfig();
            envConfig.setConfigParam(EnvironmentParams.LOG_MEM_SIZE.getName(),
                                     "220");
            envConfig.setConfigParam(EnvironmentParams.NUM_LOG_BUFFERS.getName(),
                                     "2");
            envConfig.setConfigParam(EnvironmentParams.LOG_FILE_MAX.getName(),
                                     "129");
            envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(),
                                     "6");
            
            /* Don't checkpoint utilization info for this test. */
            DbInternal.setCheckpointUP(envConfig, false);

            /* 
             * Don't run the cleaner or the checkpointer daemons, those
             * create more log entries and mess up our accounting
             */
            envConfig.setConfigParam(EnvironmentParams.ENV_RUN_CLEANER.getName(),
                                     "false");
            envConfig.setConfigParam(EnvironmentParams.ENV_RUN_CHECKPOINTER.getName(),
                                     "false");

            envConfig.setAllowCreate(true);
            env = new EnvironmentImpl(envHome, envConfig);
            fileManager = env.getFileManager();
            logManager = env.getLogManager();

            logAndRetrieve();

            // expect 8 je files, 3 for records, 1 for root, 2 for recovery
            // message, 2 for checkpoints
            String [] names = fileManager.listFiles(FileManager.JE_SUFFIXES);
            assertEquals("Should be 8 files on disk", 8, names.length);
        } catch (Throwable t) {
            t.printStackTrace();
            throw t;
        } finally {
            env.close();
        }
    }

    /**
     * Log and retrieve objects, with some of log flushed to disk, some
     * of log in memory. Force the read buffer to be very small
     */
    public void testFaultingIn() throws Throwable {
        try {
            /* 
             * Force the buffers and files to be small. The log buffer
             * is actually too small, will have to grow dynamically. We read
             * in 32 byte chunks, will have to re-read
             * only holds one test item (each test item is 50 bytes)
             */
            EnvironmentConfig envConfig = new EnvironmentConfig();
            envConfig.setConfigParam(EnvironmentParams.LOG_MEM_SIZE.getName(), "220");
            envConfig.setConfigParam(EnvironmentParams.NUM_LOG_BUFFERS.getName(), "2");
            envConfig.setConfigParam(EnvironmentParams.LOG_FILE_MAX.getName(), "200");
            envConfig.setConfigParam(EnvironmentParams.LOG_FAULT_READ_SIZE.getName(),
                                     "32");
            envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(), "6");
            envConfig.setAllowCreate(true);
            env = new EnvironmentImpl(envHome, envConfig);
            fileManager = env.getFileManager();
            logManager = env.getLogManager();

            logAndRetrieve();
        } catch (Throwable t) {
            t.printStackTrace();
            throw t;
        } finally {
            env.close();
        }
    }

    /**
     * Log several objects, retrieve them
     */
    private void logAndRetrieve() throws DatabaseException {
        // Make test loggable objects

        List testRecs = new ArrayList();
        for (int i = 0; i < 10; i++) {
            testRecs.add(new Tracer("Hello there, rec " + (i+1)));
        }

        // Log three of them, remember their LSNs
        List testLsns = new ArrayList();

        for (int i = 0; i < 3; i++) {
            long lsn = logManager.log((Tracer)testRecs.get(i));
            if (DEBUG) {
                System.out.println("i = " + i + " test LSN: file = " +
                                   DbLsn.getFileNumber(lsn) +
                                   " offset = " + 
                                   DbLsn.getFileOffset(lsn));
            }
            testLsns.add(new Long(lsn));
        }

        // Ask for them back, out of order
        assertEquals((Tracer) testRecs.get(2),
                     (Tracer) logManager.get
		     (DbLsn.longToLsn((Long) testLsns.get(2))));
        assertEquals((Tracer) testRecs.get(0),
                     (Tracer) logManager.get
		     (DbLsn.longToLsn((Long) testLsns.get(0))));
        assertEquals((Tracer) testRecs.get(1),
                     (Tracer) logManager.get
		     (DbLsn.longToLsn((Long) testLsns.get(1))));

        // Intersperse logging and getting
        testLsns.add(new Long(logManager.log((Tracer)testRecs.get(3))));
        testLsns.add(new Long(logManager.log((Tracer)testRecs.get(4))));

        assertEquals((Tracer) testRecs.get(2),
                     (Tracer) logManager.get
		     (DbLsn.longToLsn((Long) testLsns.get(2))));
        assertEquals((Tracer) testRecs.get(4),
                     (Tracer) logManager.get
		     (DbLsn.longToLsn((Long) testLsns.get(4))));

        // Intersperse logging and getting
        testLsns.add(new Long(logManager.log((Tracer) testRecs.get(5))));
        testLsns.add(new Long(logManager.log((Tracer) testRecs.get(6))));
        testLsns.add(new Long(logManager.log((Tracer) testRecs.get(7))));

        assertEquals((Tracer) testRecs.get(7),
                     (Tracer) logManager.get
		     (DbLsn.longToLsn((Long) testLsns.get(7))));
        assertEquals((Tracer) testRecs.get(0),
                     (Tracer) logManager.get
		     (DbLsn.longToLsn((Long) testLsns.get(0))));
        assertEquals((Tracer) testRecs.get(6),
                     (Tracer) logManager.get
		     (DbLsn.longToLsn((Long) testLsns.get(6))));
    }
}
