# 2007 May 10
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is generating semi-random strings of SQL
# (a.k.a. "fuzz") and sending it into the parser to try to generate
# errors.
#
# $Id: fuzz.test,v 1.4 2007/05/11 00:20:08 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

proc fuzz {TemplateList} {
  set n [llength $TemplateList]
  set i [expr {int(rand()*$n)}]
  return [subst -novar [lindex $TemplateList $i]]
}

# Returns a string representing an SQL literal.
#
proc Literal {} {
  set TemplateList {
    456 0 -456 1 -1 
    2147483648 2147483647 2147483649 -2147483647 -2147483648 -2147483649
    'The' 'first' 'experiments' 'in' 'hardware' 'fault' 'injection'
    zeroblob(1000)
    NULL
    56.1 -56.1
    123456789.1234567899
  }
  fuzz $TemplateList
}

proc UnaryOp {} {
  set TemplateList {+ - NOT}
  fuzz $TemplateList
}

proc BinaryOp {} {
  set TemplateList {+ - % * / AND OR LIKE GLOB}
  fuzz $TemplateList
}

set ::ExprDepth 0
proc Expr {} {
  incr ::ExprDepth

  set TemplateList {[Literal]}
  if {$::ExprDepth < 100} {
    lappend TemplateList \
      {[Expr] [BinaryOp] [Expr]}   \
      {[UnaryOp] [Expr]}
  }
  if {$::SelectDepth < 10} {
    lappend TemplateList {([Select 1])}
  } 
  set res [fuzz $TemplateList]
  incr ::ExprDepth -1
  return $res
}

set ::TableList [list]
proc Table {} {
  set TemplateList [concat sqlite_master $::TableList]
  fuzz $TemplateList
}

set ::SelectDepth 0
proc Select {{isExpr 0}} {
  incr ::SelectDepth
  set TemplateList {
      {SELECT [Expr]}
  }
  if {$::SelectDepth < 5} {
    lappend TemplateList \
        {SELECT [Expr] FROM ([Select])}                \
        {SELECT [Expr] FROM [Table]}                  

    if {0 == $isExpr} {
      lappend TemplateList                                         \
          {SELECT [Expr], [Expr] FROM ([Select]) ORDER BY [Expr]}  \
          {SELECT * FROM ([Select]) ORDER BY [Expr]}               \
    }
  } 
  set res [fuzz $TemplateList]
  incr ::SelectDepth -1
  set res
}

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

#----------------------------------------------------------------
# These tests caused errors that were first caught by the tests
# in this file. They are still here.
do_test fuzz-1.1 {
  execsql {
    SELECT 'abc' LIKE X'ABCD';
  }
} {0}
do_test fuzz-1.2 {
  execsql {
    SELECT 'abc' LIKE zeroblob(10);
  }
} {0}
do_test fuzz-1.3 {
  execsql {
    SELECT zeroblob(10) LIKE 'abc';
  }
} {0}
do_test fuzz-1.4 {
  execsql {
    SELECT (- -21) % NOT (456 LIKE zeroblob(10));
  }
} {0}
do_test fuzz-1.5 {
  execsql {
    SELECT (SELECT (
        SELECT (SELECT -2147483648) FROM (SELECT 1) ORDER BY 1
    ))
  }
} {-2147483648}
do_test fuzz-1.6 {
  execsql {
    SELECT 'abc', zeroblob(1) FROM (SELECT 1) ORDER BY 1
  }
} [execsql {SELECT 'abc', zeroblob(1)}]

do_test fuzz-1.7 {
  execsql {
    SELECT (
      SELECT zeroblob(1000) FROM (
        SELECT * FROM (SELECT 'first') ORDER BY NOT 'in'
      )
    )
  }
} {}

#----------------------------------------------------------------
# Test some fuzzily generated expressions.
#
for {set ii 0} {$ii < 2000} {incr ii} {
  do_test fuzz-2.1.$ii {
    set ::expr [Expr]
    set rc [catch {execsql "SELECT $::expr"} msg]
    set e [expr {
      $rc == 0 || 
      $msg eq "parser stack overflow" ||
      0 == [string first "ORDER BY column number" $msg]
    }]
    if {$e == 0} {
      puts ""
      puts "SELECT $::expr"
      puts $msg
    }
    set e
  } {1}
} 

do_test fuzz-3.1 {
  execsql {
    CREATE TABLE abc(a, b, c);
    CREATE TABLE def(d, e, f);
    CREATE TABLE ghi(g, h, i);
  }
} {}
set ::TableList [list abc def ghi]

#----------------------------------------------------------------
# Test some fuzzily generated SELECT statements.
#
for {set ii 0} {$ii < 2000} {incr ii} {
  do_test fuzz-2.2.$ii {
    set ::select [Select]
    set rc [catch {execsql $::select} msg]
    set e [expr {$rc == 0 || $msg eq "parser stack overflow"}]
    set e [expr {
      $rc == 0 || 
      $msg eq "parser stack overflow" ||
      0 == [string first "ORDER BY column number" $msg]
    }]
    if {$e == 0} {
      puts ""
      puts $::select
      puts $msg
    }
    set e
  } {1}
} 

finish_test
