###############################################################################
#
# stress.eagle --
#
# Written by Joe Mistachkin.
# Released to the public domain, use at your own risk!
#
###############################################################################

package require Eagle
package require Eagle.Library
package require Eagle.Test

runTestPrologue

###############################################################################

package require System.Data.SQLite.Test
runSQLiteTestPrologue

###############################################################################

runTest {test stress-1.1 {multithreaded stress testing} -setup {
  proc showTest { indicator } {
    puts -nonewline stdout $indicator; flush stdout
    after [expr {int(rand() * 1000)}]
  }

  #############################################################################

  proc failThisTest { error } {
    set level [expr {[info level] - 1}]

    puts stdout ""; flush stdout
    puts stdout [appendArgs [info level $level] ": " $error]; flush stdout
    puts stdout ""; flush stdout

    exit Failure; # halt all testing now.
  }

  #############################################################################

  set count(0) 1
  set count(1) 10

  if {[info exists argv] && [llength $argv] > 0} then {
    parse options [list \
        [list null MustHaveIntegerValue -1 -1 -count0 $count(0)] \
        [list null MustHaveIntegerValue -1 -1 -count1 $count(1)]] $argv

    set count(0) $options(-count0,value)
    set count(1) $options(-count1,value)
  }

  #############################################################################

  tputs $test_channel [appendArgs \
      "---- workloads will repeat " $count(0) " time(s)...\n"]

  tputs $test_channel [appendArgs \
      "---- workloads will have " $count(1) " iteration(s)...\n"]

  #############################################################################

  set fileName(1) "file::memory:?cache=shared"
  set fileName(2) [file join [getDatabaseDirectory] stress.db]

  #############################################################################

  setupDb $fileName(1) "" "" "" "" "" false true srcDb
  setupDb $fileName(2)

  #############################################################################

  set workload(1) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #1, CREATE TABLE statements.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      for {set index 2} {$index <= $count} {incr index} {
        sql execute $db [appendArgs \
            "CREATE TABLE IF NOT EXISTS t" $index "(x PRIMARY KEY, y, z);"]
        showTest 1
      }
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(2) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #2, DROP TABLE statements.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      for {set index 2} {$index <= $count} {incr index} {
        sql execute $db [appendArgs \
            "DROP TABLE IF EXISTS t" $index \;]
        showTest 2
      }
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(3) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #3, "small" SELECT statements.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      for {set index 1} {$index <= $count} {incr index} {
        sql execute -execute reader $db [appendArgs \
            "SELECT x, y FROM " $table " WHERE z = 'small';"]
        showTest 3
      }
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(4) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #4, "big" SELECT statements.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      for {set index 1} {$index <= $count} {incr index} {
        sql execute -execute reader $db [appendArgs \
            "SELECT x, y FROM " $table " WHERE z = 'big';"]
        showTest 4
      }
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(5) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #5, "small" INSERT statements.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      for {set index 1} {$index <= $count} {incr index} {
        sql execute $db [appendArgs \
            "INSERT INTO " $table "(x, y, z) VALUES('" \
            [format %lX [expr {random()}]] "', '" \
            [base64 encode -- [expr {randstr(10000)}]] \
            "', 'small');"]
        showTest 5
      }
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(6) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #6, "big" INSERT statements.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      for {set index 1} {$index <= $count} {incr index} {
        sql execute $db [appendArgs \
            "INSERT INTO " $table "(x, y, z) VALUES('" \
            [format %lX [expr {random()}]] \
            "', RANDOMBLOB(10000000), 'big');"]
        showTest 6
      }
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(7) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #7, "small" UPDATE statements.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      for {set index 1} {$index <= $count} {incr index} {
        sql execute $db [appendArgs "UPDATE " $table \
            " SET y = '" [base64 encode -- [expr {randstr(10000)}]] \
            "' WHERE x LIKE '" [format %X $index] "%' AND z = 'small';"]
        showTest 7
      }
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(8) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #8, "big" UPDATE statements.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      for {set index 1} {$index <= $count} {incr index} {
        sql execute $db [appendArgs "UPDATE " $table \
            " SET y = RANDOMBLOB(10000000) WHERE x LIKE '" \
            [format %X $index] "%' AND z = 'big';"]
        showTest 8
      }
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(9) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #9, "small" DELETE statements.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      for {set index 1} {$index <= $count} {incr index} {
        sql execute $db [appendArgs "DELETE FROM " $table \
            " WHERE x LIKE '" [format %X $index] "%' AND z = 'small';"]
        showTest 9
      }
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(10) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #10, "big" DELETE statements.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      for {set index 1} {$index <= $count} {incr index} {
        sql execute $db [appendArgs "DELETE FROM " $table \
            " WHERE x LIKE '" [format %X $index] "%' AND z = 'big';"]
        showTest A
      }
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(11) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #11, VACUUM statement.
    #
    setupDb $dstFileName "" "" "" "" "" false
    if {[catch {
      sql execute $db "VACUUM;"
      showTest B
    } error]} then {
      if {[regexp -- {\sno such table: t1\s} $error]} then {
        showTest *
      } else {
        failThisTest $error
      }
    }
    cleanupDb $dstFileName db false false
  }]

  #############################################################################

  set workload(12) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #12, backup to in-memory database.
    #
    set id [object invoke Interpreter.GetActive NextId]

    set code [compileCSharpWith [subst {
      using System;
      using System.Data.SQLite;
      using System.Text;

      namespace _Dynamic${id}
      {
        public static class Test${id}
        {
          public static void BackupAndGetData()
          {
            using (SQLiteConnection source = new SQLiteConnection(
                "FullUri=${srcFileName};"))
            {
              source.Open();
              using (SQLiteConnection destination = new SQLiteConnection(
                  "FullUri=${dstFileName};"))
              {
                destination.Open();
                source.BackupDatabase(destination, "main", "main", -1, null, 0);
              }
            }
          }

          /////////////////////////////////////////////////////////////////////

          public static void Main()
          {
            // do nothing.
          }
        }
      }
    }] true true true results errors System.Data.SQLite.dll]
    if {$code eq "Ok"} then {
      object invoke _Dynamic${id}.Test${id} BackupAndGetData
    } else {
      error $errors
    }
    showTest C
  }]

  #############################################################################

  set workload(13) [list [list srcFileName dstFileName table count] {
    #
    # NOTE: Workload #13, backup from an in-memory database.
    #
    set id [object invoke Interpreter.GetActive NextId]

    set code [compileCSharpWith [subst {
      using System;
      using System.Data.SQLite;
      using System.Text;

      namespace _Dynamic${id}
      {
        public static class Test${id}
        {
          public static void BackupAndGetData()
          {
            using (SQLiteConnection source = new SQLiteConnection(
                "FullUri=${dstFileName};"))
            {
              source.Open();
              using (SQLiteConnection destination = new SQLiteConnection(
                  "FullUri=${srcFileName};"))
              {
                destination.Open();
                source.BackupDatabase(destination, "main", "main", -1, null, 0);
              }
            }
          }

          /////////////////////////////////////////////////////////////////////

          public static void Main()
          {
            // do nothing.
          }
        }
      }
    }] true true true results errors System.Data.SQLite.dll]
    if {$code eq "Ok"} then {
      object invoke _Dynamic${id}.Test${id} BackupAndGetData
    } else {
      error $errors
    }
    showTest D
  }]
} -body {
  for {set index(0) 0} {$index(0) < $count(0)} {incr index(0)} {
    sql execute $db "CREATE TABLE IF NOT EXISTS t1(x PRIMARY KEY, y, z);"

    unset -nocomplain thread

    foreach index(1) [array names workload] {
      set thread($index(1)) [object create -alias System.Threading.Thread \
          [list apply $workload($index(1)) $fileName(1) $fileName(2) t1 \
          $count(1)]]
    }

    foreach index(1) [array names thread] {
      $thread($index(1)) Start
    }

    foreach index(1) [array names thread] {
      $thread($index(1)) Join
    }
  }

  puts stdout ""
} -cleanup {
  rename failThisTest ""
  rename showTest ""

  cleanupDb $fileName(2)
  cleanupDb $fileName(1) srcDb

  unset -nocomplain thread index workload srcDb db fileName options count
} -constraints \
{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result {}}

###############################################################################

runSQLiteTestEpilogue
runTestEpilogue
