context: /*-
   * See the file LICENSE for redistribution information.
   *
   * Copyright (c) 2009 Oracle.  All rights reserved.
   *
   */
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.IO;
  using System.Text;
  using System.Threading;
  using System.Xml;
  using NUnit.Framework;
  using BerkeleyDB;
  
  namespace CsharpAPITest
  {
      [TestFixture]
      public class DatabaseEnvironmentTest
      {
          private string testFixtureHome;
          private string testFixtureName;
          private string testName;
          private string testHome;
  
          private DatabaseEnvironment testBeginTransactionEnv;
          private BTreeDatabase testBeginTransactionDB;
  
          private DatabaseEnvironment testCheckpointEnv;
          private BTreeDatabase testCheckpointDB;
  
          private DatabaseEnvironment testDetectDeadlocksEnv;
          private BTreeDatabase testDetectDeadlocksDB;
  
          private DatabaseEnvironment testFailCheckEnv;
  
          private EventWaitHandle signal;
  
          [TestFixtureSetUp]
          public void SetUp()
          {
              testFixtureName = "DatabaseEnvironmentTest";
              testFixtureHome = "./TestOut/" + testFixtureName;
              try
              {
                  Configuration.ClearDir(testFixtureHome);
              }
              catch (Exception)
              {
                  throw new TestException(
                      "Please clean the directory");
              }
          }
  
          [Test]
          public void TestArchivableDatabaseFiles()
          {
              testName = "TestArchivableDatabaseFiles";
              testHome = testFixtureHome + "/" + testName;
              string dbFileName1 = testName + "1.db";
              string dbFileName2 = testName + "2.db";
  
              Configuration.ClearDir(testHome);
  
              // Open an environment.
              DatabaseEnvironmentConfig envConfig =
                  new DatabaseEnvironmentConfig();
              envConfig.AutoCommit = true;
              envConfig.Create = true;
              envConfig.UseMPool = true;
              envConfig.UseLogging = true;
              envConfig.UseTxns = true;
              DatabaseEnvironment env = DatabaseEnvironment.Open(
                  testHome, envConfig);
  
              // Open two databases.
              BTreeDatabaseConfig dbConfig =
                  new BTreeDatabaseConfig();
              dbConfig.Creation = CreatePolicy.IF_NEEDED;
              dbConfig.Env = env;
              BTreeDatabase db1 = BTreeDatabase.Open(dbFileName1, dbConfig);
              db1.Close();
              BTreeDatabase db2 = BTreeDatabase.Open(dbFileName2, dbConfig);
              db2.Close();
  
              /*
               * Get all database files name in the environment.
               * Two database file name should be returned and
               * the same as the ones when opening the databases.
               */
              List<string> dbFiles = env.ArchivableDatabaseFiles(false);
              Assert.AreEqual(2, dbFiles.Count);
              Assert.IsTrue(dbFiles.Contains(dbFileName1));
              Assert.IsTrue(dbFiles.Contains(dbFileName2));
  
              /*
               * Get all database file's abosolute path in the
               * environment. Confirm those files exist.
               */
              List<string> dbFilesPath = env.ArchivableDatabaseFiles(true);
              Assert.IsTrue(File.Exists(dbFilesPath[0]));
              Assert.IsTrue(File.Exists(dbFilesPath[1]));
  
              env.Close();
          }
  
          [Test]
          public void TestArchivableLogFiles()
          {
              testName = "TestArchivableLogFiles";
              testHome = testFixtureHome + "/" + testName;
              string dbFileName = testName + ".db";
  
              Configuration.ClearDir(testHome);
  
              // Open an environment.
              DatabaseEnvironmentConfig envConfig =
                  new DatabaseEnvironmentConfig();
              envConfig.AutoCommit = true;
              envConfig.Create = true;
              envConfig.UseMPool = true;
              envConfig.UseLogging = true;
              envConfig.UseTxns = true;
              DatabaseEnvironment env = DatabaseEnvironment.Open(
                  testHome, envConfig);
  
              // Open a databases.
              BTreeDatabaseConfig dbConfig =
                  new BTreeDatabaseConfig();
              dbConfig.Creation = CreatePolicy.IF_NEEDED;
              dbConfig.Env = env;
              BTreeDatabase db = BTreeDatabase.Open(
                  dbFileName, dbConfig);
  
              /*
               * Put 1000 records into the database to generate
               * more than one log files.
               */
              byte[] byteArr = new byte[1024];
              for (int i = 0; i < 1000; i++)
                  db.Put(new DatabaseEntry(
                      BitConverter.GetBytes(i)),
                      new DatabaseEntry(byteArr));
  
              db.Close();
  
              List<string> logFiles = env.ArchivableLogFiles(false);
  
              List<string> logFilesPath =
                  env.ArchivableLogFiles(true);
              for (int i = 0; i < logFilesPath.Count; i++)
                  Assert.IsTrue(File.Exists(logFilesPath[i]));
  
              env.Close();
          }
  
          [Test]
          public void TestBeginCDSGroup()
          {
              testName = "TestBeginCDSGroup";
              testHome = testFixtureHome + "/" + testName;
  
              Configuration.ClearDir(testHome);
  
              DatabaseEnvironmentConfig cfg =
                  new DatabaseEnvironmentConfig();
              cfg.Create = true;
              cfg.UseCDB = true;
              cfg.UseMPool = true;
              DatabaseEnvironment env = DatabaseEnvironment.Open(testHome, cfg);
              Transaction txn = env.BeginCDSGroup();
              BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
              dbConfig.Creation = CreatePolicy.IF_NEEDED;
              dbConfig.Env = env;
              BTreeDatabase db = BTreeDatabase.Open(
                  testName + ".db", dbConfig, txn);
              db.Put(new DatabaseEntry(
                  ASCIIEncoding.ASCII.GetBytes("key")),
                  new DatabaseEntry(
                  ASCIIEncoding.ASCII.GetBytes("data")), txn);
              db.Close();
              txn.Commit();
              env.Close();
          }
  
          [Test]
          public void TestBeginTransaction()
          {
              testName = "TestBeginTransaction";
              testHome = testFixtureHome + "/" + testName;
  
              Configuration.ClearDir(testHome);
  
              // Open an environment.
              DatabaseEnvironmentConfig cfg =
                  new DatabaseEnvironmentConfig();
              cfg.Create = true;
              cfg.UseTxns = true;
              cfg.UseMPool = true;
              cfg.UseLogging = true;
              cfg.UseLocking = true;
              cfg.NoLocking = false;
              cfg.FreeThreaded = true;
              testBeginTransactionEnv = DatabaseEnvironment.Open(testHome, cfg);
              testBeginTransactionEnv.DeadlockResolution = DeadlockPolicy.OLDEST;
  
              // Open btree database.
              BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
              dbConfig.AutoCommit = true;
              dbConfig.Creation = CreatePolicy.IF_NEEDED;
              dbConfig.Env = testBeginTransactionEnv;
              dbConfig.Duplicates = DuplicatesPolicy.NONE;
              dbConfig.FreeThreaded = true;
              testBeginTransactionDB = BTreeDatabase.Open(
                  testName + ".db", dbConfig);
  
              testBeginTransactionDB.Put(
                  new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")),
                  new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data")));
  
              // Begin two threads to run dead lock detection.
              Thread thread1 = new Thread(new ThreadStart(
                  DeadLockThreadWithLockTimeOut));
              Thread thread2 = new Thread(new ThreadStart(
                  DeadLockThreadWithTxnTimeout));
              signal = new EventWaitHandle(false, EventResetMode.ManualReset);
              thread1.Start();
              thread2.Start();
              Thread.Sleep(1000);
              signal.Set();
              thread1.Join();
              thread2.Join();
  
              // Close all.
              testBeginTransactionDB.Close();
              testBeginTransactionEnv.Close();
          }
  
          public void DeadLockThreadWithLockTimeOut()
          {
              // Configure and begin a transaction.
              TransactionConfig txnConfig = new TransactionConfig();
              txnConfig.LockTimeout = 5000;
              txnConfig.Name = "DeadLockThreadWithLockTimeOut";
              Transaction txn = 
                  testBeginTransactionEnv.BeginTransaction(txnConfig, null);
              try
              {
                  testBeginTransactionDB.Put(
                      new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")),
                      new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data")));
                  signal.WaitOne();
                  testBeginTransactionDB.Put(
                      new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("newkey")),
                      new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("newdata")),
                      txn);
                  txn.Commit();
              }
              catch (DeadlockException)
              {
                  try
                  {
                      txn.Abort();
                  }
                  catch (DatabaseException)
                  {
                      throw new TestException();
                  }
              }
              catch (DatabaseException)
              {
                  try
                  {
                      txn.Abort();
                  }
                  catch (DatabaseException)
                  {
                      throw new TestException();
                  }
              }
          }
  
          public void DeadLockThreadWithTxnTimeout()
          {
              // Configure and begin a transaction.
              TransactionConfig txnConfig = new TransactionConfig();
              txnConfig.TxnTimeout = 5000;
              txnConfig.Name = "DeadLockThreadWithTxnTimeout";
              Transaction txn =
                  testBeginTransactionEnv.BeginTransaction(txnConfig, null);
              try
              {
                  testBeginTransactionDB.Put(
                      new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")),
                      new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data")));
                  signal.WaitOne();
                  testBeginTransactionDB.Put(
                      new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("newkey")),
                      new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("newdata")),
                      txn);
                  txn.Commit();
              }
              catch (DeadlockException)
              {
                  try
                  {
                      txn.Abort();
                  }
                  catch (DatabaseException)
                  {
                      throw new TestException();
                  }
              }
              catch (DatabaseException)
              {
                  try
                  {
                      txn.Abort();
                  }
                  catch (DatabaseException)
                  {
                      throw new TestException();
                  }
              }
          }
  
          [Test]
          public void TestCheckpoint()
          {
              testName = "TestCheckpoint";
              testHome = testFixtureHome + "/" + testName;
  
              Configuration.ClearDir(testHome);
  
              // Open an environment.
              DatabaseEnvironmentConfig cfg =
                  new DatabaseEnvironmentConfig();
              cfg.Create = true;
              cfg.UseTxns = true;
              cfg.UseMPool = true;
              cfg.UseLogging = true;
              cfg.UseLocking = true;
              cfg.NoLocking = false;
              cfg.FreeThreaded = true;
              testCheckpointEnv = DatabaseEnvironment.Open(testHome, cfg);
  
              // Open btree database.
              BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
              dbConfig.AutoCommit = true;
              dbConfig.Creation = CreatePolicy.IF_NEEDED;
              dbConfig.Env = testCheckpointEnv;
              dbConfig.FreeThreaded = true;
              testCheckpointDB = BTreeDatabase.Open(testName + ".db", dbConfig);
  
  
              // Run a thread to put records into database.
              Thread thread1 = new Thread(new ThreadStart(PutRecordsThread));
  
              /*
               * Run a thread to do checkpoint periodically and
               * finally do a checkpoint to flush all in memory pool
               * to log files.
               */
              Thread thread2 = new Thread(new ThreadStart(CheckpointThread));
  
              thread1.Start();
              thread2.Start();
              thread1.Join();
              thread2.Join();
  
              // Close all.
              testCheckpointDB.Close();
              testCheckpointEnv.Close();
          }
  
          public void PutRecordsThread()
          {
              Transaction txn = testCheckpointEnv.BeginTransaction();
              byte[] byteArr = new byte[1024];
              for (int i = 0; i < 1000; i++)
                  testCheckpointDB.Put(
                      new DatabaseEntry(BitConverter.GetBytes(i)),
                      new DatabaseEntry(byteArr), txn);
              txn.Commit();
          }
  
          public void CheckpointThread()
          {
              uint bytes = 64;
              uint minutes = 1;
              uint count = 1;
              while (count < 3)
              {
                  testCheckpointEnv.Checkpoint(bytes, minutes);
                  count++;
              }
              Thread.Sleep(500);
              testCheckpointEnv.Checkpoint();
          }
  
          [Test]
          public void TestClose()
          {
              testName = "TestClose";
              testHome = testFixtureHome + "/" + testName;
  
              Configuration.ClearDir(testHome);
  
              DatabaseEnvironmentConfig cfg =
                  new DatabaseEnvironmentConfig();
              cfg.Create = true;
              DatabaseEnvironment env = DatabaseEnvironment.Open(
                  testHome, cfg);
              env.Close();
          }
  
          [Test]
          public void TestConfigAll()
          {
              testName = "TestConfigAll";
              testHome = testFixtureHome + "/" + testName;
  
              Configuration.ClearDir(testHome);
  
              XmlElement xmlElem = Configuration.TestSetUp(
                  testFixtureName, testName);
  
              /*
               * Open a new environment with all properties,
               * fields and subsystems configured.
               */
              DatabaseEnvironmentConfig envConig =
                  new DatabaseEnvironmentConfig();
              Config(xmlElem, ref envConig, true, true, true,
                  true, true, true);
  
              // Configure with methods.
              ReplicationHostAddress address =
                  new ReplicationHostAddress("127.0.0.0", 11111);
              envConig.RepSystemCfg.Clockskew(102, 100);
              envConig.RepSystemCfg.RetransmissionRequest(10, 100);
              envConig.RepSystemCfg.TransmitLimit(1, 1024);
  
              // Open the environment.
              DatabaseEnvironment env = DatabaseEnvironment.Open(
                  testHome, envConig);
  
              // Confirm environment status with its configuration.
              Confirm(xmlElem, env, true, true, true, true, true, true);
  
              // Print statistics of the current environment.
              env.PrintStats(true, true);
  
              // Print statistics of all subsytems.
              env.PrintSubsystemStats(true, true);
  
              env.Close();
          }
  
          [Test]
          public void TestDeadlockPolicy()
          {
              testName = "TestDeadlockPolicy";
              testHome = testFixtureHome + "/" + testName;
  
              DetectDeadlockPolicy(testHome + "_DEFAULT",
                  DeadlockPolicy.DEFAULT);
  
              DetectDeadlockPolicy(testHome + "_EXPIRE",
                  DeadlockPolicy.EXPIRE);
              DetectDeadlockPolicy(testHome + "_MAX_LOCKS",
                  DeadlockPolicy.MAX_LOCKS);
              DetectDeadlockPolicy(testHome + "_MAX_WRITE",
                  DeadlockPolicy.MAX_WRITE);
              DetectDeadlockPolicy(testHome + "_MIN_LOCKS",
                  DeadlockPolicy.MIN_LOCKS);
              DetectDeadlockPolicy(testHome + "_MIN_WRITE",
                  DeadlockPolicy.MIN_WRITE);
              DetectDeadlockPolicy(testHome + "_OLDEST",
                  DeadlockPolicy.OLDEST);
              DetectDeadlockPolicy(testHome + "_RANDOM",
                  DeadlockPolicy.RANDOM);
              DetectDeadlockPolicy(testHome + "_YOUNGEST",
                  DeadlockPolicy.YOUNGEST);
          }
  
          public void DetectDeadlockPolicy(
              string home, DeadlockPolicy deadlock)
          {
              Configuration.ClearDir(home);
              DatabaseEnvironmentConfig envConfig =
                  new DatabaseEnvironmentConfig();
              envConfig.Create = true;
              envConfig.UseLocking = true;
              envConfig.UseLogging = true;
              envConfig.UseMPool = true;
              envConfig.UseTxns = true;
              DatabaseEnvironment env = DatabaseEnvironment.Open(
                  home, envConfig);
              env.DeadlockResolution = deadlock;
              Assert.AreEqual(deadlock, env.DeadlockResolution);
              env.DetectDeadlocks(deadlock);
              env.Close();
          }
  
          [Test]
          public void TestDetectDeadlocks()
          {
              testName = "TestDetectDeadlocks";
  
gt:             testHome = testFixtureHome + "/" + testName;