diff options
Diffstat (limited to 'ext/session/sessionrebase.test')
-rw-r--r-- | ext/session/sessionrebase.test | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/ext/session/sessionrebase.test b/ext/session/sessionrebase.test new file mode 100644 index 0000000..cdf3322 --- /dev/null +++ b/ext/session/sessionrebase.test @@ -0,0 +1,476 @@ +# 2018 March 14 +# +# 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. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] session_common.tcl] +source $testdir/tester.tcl +ifcapable !session {finish_test; return} + +set testprefix sessionrebase + +set ::lConflict [list] +proc xConflict {args} { + set res [lindex $::lConflict 0] + set ::lConflict [lrange $::lConflict 1 end] + return $res +} + +#------------------------------------------------------------------------- +# The following test cases - 1.* - test that the rebase blobs output by +# sqlite3_changeset_apply_v2 look correct in some simple cases. The blob +# is itself a changeset, containing records determined as follows: +# +# * For each conflict resolved with REPLACE, the rebase blob contains +# a DELETE record. All fields other than the PK fields are undefined. +# +# * For each conflict resolved with OMIT, the rebase blob contains an +# INSERT record. For an INSERT or UPDATE operation, the indirect flag +# is clear and all updated fields are defined. For a DELETE operation, +# the indirect flag is set and all non-PK fields left undefined. +# +proc do_apply_v2_test {tn sql modsql conflict_handler res} { + + execsql BEGIN + sqlite3session S db main + S attach * + execsql $sql + set changeset [S changeset] + S delete + execsql ROLLBACK + + execsql BEGIN + execsql $modsql + set ::lConflict $conflict_handler + set blob [sqlite3changeset_apply_v2 db $changeset xConflict] + execsql ROLLBACK + + uplevel [list do_test $tn [list changeset_to_list $blob] [list {*}$res]] +} + + +set ::lConflict [list] +proc xConflict {args} { + set res [lindex $::lConflict 0] + set ::lConflict [lrange $::lConflict 1 end] + return $res +} + +# Take a copy of database test.db in file test.db2. Execute $sql1 +# against test.db and $sql2 against test.db2. Capture a changeset +# for each. Then send the test.db2 changeset to test.db and apply +# it with the conflict handlers in $conflict_handler. Patch the +# test.db changeset and then execute it against test.db2. Test that +# the two databases come out the same. +# +proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} { + + for {set i 1} {$i <= 2} {incr i} { + forcedelete test.db2 test.db2-journal test.db2-wal + forcecopy test.db test.db2 + sqlite3 db2 test.db2 + + db eval BEGIN + + sqlite3session S1 db main + S1 attach * + execsql $sql1 db + set c1 [S1 changeset] + S1 delete + + if {$i==1} { + sqlite3session S2 db2 main + S2 attach * + execsql $sql2 db2 + set c2 [S2 changeset] + S2 delete + } else { + set c2 [list] + foreach sql [split $sql2 ";"] { + if {[string is space $sql]} continue + sqlite3session S2 db2 main + S2 attach * + execsql $sql db2 + lappend c2 [S2 changeset] + S2 delete + } + } + + set ::lConflict $conflict_handler + set rebase [list] + if {$i==1} { + lappend rebase [sqlite3changeset_apply_v2 db $c2 xConflict] + } else { + foreach c $c2 { +#puts "apply_v2: [changeset_to_list $c]" + lappend rebase [sqlite3changeset_apply_v2 db $c xConflict] + } + #puts "llength: [llength $rebase]" + } + #if {$tn=="2.1.4"} { puts [changeset_to_list $rebase] ; breakpoint } + #puts [changeset_to_list [lindex $rebase 0]] ; breakpoint + #puts [llength $rebase] + + sqlite3rebaser_create R + foreach r $rebase { +#puts [changeset_to_list $r] + R configure $r + } + set c1r [R rebase $c1] + R delete + #if {$tn=="2.1.4"} { puts [changeset_to_list $c1r] } + + sqlite3changeset_apply_v2 db2 $c1r xConflictAbort + + if {[string range $tn end end]!="*"} { + uplevel [list do_test $tn.$i.1 [list compare_db db db2] {}] + } + db2 close + + if {$testsql!=""} { + uplevel [list do_execsql_test $tn.$i.2 $testsql $testres] + } + + db eval ROLLBACK + } +} + +do_execsql_test 1.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, 'value A'); +} + +do_apply_v2_test 1.1.1 { + UPDATE t1 SET b = 'value B' WHERE a=1; +} { + UPDATE t1 SET b = 'value C' WHERE a=1; +} { + OMIT +} { + {INSERT t1 0 X. {} {i 1 t {value B}}} +} + +do_apply_v2_test 1.1.2 { + UPDATE t1 SET b = 'value B' WHERE a=1; +} { + UPDATE t1 SET b = 'value C' WHERE a=1; +} { + REPLACE +} { + {INSERT t1 1 X. {} {i 1 t {value B}}} +} + +do_apply_v2_test 1.2.1 { + INSERT INTO t1 VALUES(2, 'first'); +} { + INSERT INTO t1 VALUES(2, 'second'); +} { + OMIT +} { + {INSERT t1 0 X. {} {i 2 t first}} +} +do_apply_v2_test 1.2.2 { + INSERT INTO t1 VALUES(2, 'first'); +} { + INSERT INTO t1 VALUES(2, 'second'); +} { + REPLACE +} { + {INSERT t1 1 X. {} {i 2 t first}} +} + +do_apply_v2_test 1.3.1 { + DELETE FROM t1 WHERE a=1; +} { + UPDATE t1 SET b='value D' WHERE a=1; +} { + OMIT +} { + {DELETE t1 0 X. {i 1 t {value A}} {}} +} +do_apply_v2_test 1.3.2 { + DELETE FROM t1 WHERE a=1; +} { + UPDATE t1 SET b='value D' WHERE a=1; +} { + REPLACE +} { + {DELETE t1 1 X. {i 1 t {value A}} {}} +} + +#------------------------------------------------------------------------- +# Test cases 2.* - simple tests of rebasing actual changesets. +# +# 2.1.1 - 1u2u1r +# 2.1.2 - 1u2u2r +# 2.1.3 - 1d2d +# 2.1.4 - 1d2u1r +# 2.1.5 - 1d2u2r !! +# 2.1.6 - 1u2d1r +# 2.1.7 - 1u2d2r +# +# 2.1.8 - 1i2i2r +# 2.1.9 - 1i2i1r +# + +proc xConflictAbort {args} { + return "ABORT" +} + +reset_db +do_execsql_test 2.1.0 { + CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + INSERT INTO t1 VALUES(3, 'three'); +} +do_rebase_test 2.1.1 { + UPDATE t1 SET b = 'two.1' WHERE a=2 +} { + UPDATE t1 SET b = 'two.2' WHERE a=2; +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two.1 3 three} + +do_rebase_test 2.1.2 { + UPDATE t1 SET b = 'two.1' WHERE a=2 +} { + UPDATE t1 SET b = 'two.2' WHERE a=2; +} { + REPLACE +} { SELECT * FROM t1 } {1 one 2 two.2 3 three} + +do_rebase_test 2.1.3 { + DELETE FROM t1 WHERE a=3 +} { + DELETE FROM t1 WHERE a=3; +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two} + +do_rebase_test 2.1.4 { + DELETE FROM t1 WHERE a=1 +} { + UPDATE t1 SET b='one.2' WHERE a=1 +} { + OMIT +} { SELECT * FROM t1 } {2 two 3 three} + +#do_rebase_test 2.1.5 { +# DELETE FROM t1 WHERE a=1; +#} { +# UPDATE t1 SET b='one.2' WHERE a=1 +#} { +# REPLACE +#} { SELECT * FROM t1 } {2 two 3 three} + +do_rebase_test 2.1.6 { + UPDATE t1 SET b='three.1' WHERE a=3 +} { + DELETE FROM t1 WHERE a=3; +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two 3 three.1} + +do_rebase_test 2.1.7 { + UPDATE t1 SET b='three.1' WHERE a=3 +} { + DELETE FROM t1 WHERE a=3; +} { + REPLACE +} { SELECT * FROM t1 } {1 one 2 two} + +do_rebase_test 2.1.8 { + INSERT INTO t1 VALUES(4, 'four.1') +} { + INSERT INTO t1 VALUES(4, 'four.2'); +} { + REPLACE +} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.2} + +do_rebase_test 2.1.9 { + INSERT INTO t1 VALUES(4, 'four.1') +} { + INSERT INTO t1 VALUES(4, 'four.2'); +} { + OMIT +} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.1} + +do_execsql_test 2.2.0 { + CREATE TABLE t2(x, y, z PRIMARY KEY); + INSERT INTO t2 VALUES('i', 'a', 'A'); + INSERT INTO t2 VALUES('ii', 'b', 'B'); + INSERT INTO t2 VALUES('iii', 'c', 'C'); + + CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c); + INSERT INTO t3 VALUES(-1, 'z', 'Z'); + INSERT INTO t3 VALUES(-2, 'y', 'Y'); +} + +do_rebase_test 2.2.1 { + UPDATE t2 SET x=1 WHERE z='A' +} { + UPDATE t2 SET y='one' WHERE z='A'; +} { +} { SELECT * FROM t2 WHERE z='A' } { 1 one A } + +do_rebase_test 2.2.2 { + UPDATE t2 SET x=1, y='one' WHERE z='B' +} { + UPDATE t2 SET y='two' WHERE z='B'; +} { + REPLACE +} { SELECT * FROM t2 WHERE z='B' } { 1 two B } + +do_rebase_test 2.2.3 { + UPDATE t2 SET x=1, y='one' WHERE z='B' +} { + UPDATE t2 SET y='two' WHERE z='B'; +} { + OMIT +} { SELECT * FROM t2 WHERE z='B' } { 1 one B } + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 3.0 { + CREATE TABLE t3(a, b, c, PRIMARY KEY(b, c)); + CREATE TABLE abcdefghijkl(x PRIMARY KEY, y, z); + + INSERT INTO t3 VALUES(1, 2, 3); + INSERT INTO t3 VALUES(4, 2, 5); + INSERT INTO t3 VALUES(7, 2, 9); + + INSERT INTO abcdefghijkl VALUES('a', 'b', 'c'); + INSERT INTO abcdefghijkl VALUES('d', 'e', 'f'); + INSERT INTO abcdefghijkl VALUES('g', 'h', 'i'); +} + +breakpoint +# do_rebase_test 3.6.tn { +# UPDATE abcdefghijkl SET z='X', y='X' WHERE x='d'; +# } { +# UPDATE abcdefghijkl SET y=1 WHERE x='d'; +# UPDATE abcdefghijkl SET z=1 WHERE x='d'; +# } [list REPLACE REPLACE REPLACE] + +foreach {tn p} { + 1 OMIT 2 REPLACE +} { + do_rebase_test 3.1.$tn { + INSERT INTO t3 VALUES(1, 1, 1); + UPDATE abcdefghijkl SET y=2; + } { + INSERT INTO t3 VALUES(4, 1, 1); + DELETE FROM abcdefghijkl; + } [list $p $p $p $p $p $p $p $p] + + do_rebase_test 3.2.$tn { + INSERT INTO abcdefghijkl SELECT * FROM t3; + UPDATE t3 SET b=b+1; + } { + INSERT INTO t3 VALUES(3, 3, 3); + INSERT INTO abcdefghijkl SELECT * FROM t3; + } [list $p $p $p $p $p $p $p $p] + + do_rebase_test 3.3.$tn { + INSERT INTO abcdefghijkl VALUES(22, 23, 24); + } { + INSERT INTO abcdefghijkl VALUES(22, 25, 26); + UPDATE abcdefghijkl SET y=400 WHERE x=22; + } [list $p $p $p $p $p $p $p $p] + + do_rebase_test 3.4.$tn { + INSERT INTO abcdefghijkl VALUES(22, 23, 24); + } { + INSERT INTO abcdefghijkl VALUES(22, 25, 26); + UPDATE abcdefghijkl SET y=400 WHERE x=22; + } [list REPLACE $p] + + do_rebase_test 3.5.$tn* { + UPDATE abcdefghijkl SET y='X' WHERE x='d'; + } { + DELETE FROM abcdefghijkl WHERE x='d'; + INSERT INTO abcdefghijkl VALUES('d', NULL, NULL); + } [list $p $p $p] + do_rebase_test 3.5.$tn { + UPDATE abcdefghijkl SET y='X' WHERE x='d'; + } { + DELETE FROM abcdefghijkl WHERE x='d'; + INSERT INTO abcdefghijkl VALUES('d', NULL, NULL); + } [list REPLACE $p $p] + + do_rebase_test 3.6.$tn { + UPDATE abcdefghijkl SET z='X', y='X' WHERE x='d'; + } { + UPDATE abcdefghijkl SET y=1 WHERE x='d'; + UPDATE abcdefghijkl SET z=1 WHERE x='d'; + } [list REPLACE $p $p] +} + +#------------------------------------------------------------------------- +# Check that apply_v2() does not create a rebase buffer for a patchset. +# And that it is not possible to rebase a patchset. +# +do_execsql_test 4.0 { + CREATE TABLE t5(o PRIMARY KEY, p, q); + INSERT INTO t5 VALUES(1, 2, 3); + INSERT INTO t5 VALUES(4, 5, 6); +} +foreach {tn cmd rebasable} { + 1 patchset 0 + 2 changeset 1 +} { + proc xConflict {args} { return "OMIT" } + do_test 4.1.$tn { + execsql { + BEGIN; + DELETE FROM t5 WHERE o=4; + } + + sqlite3session S db main + S attach * + execsql { + INSERT INTO t5 VALUES(4, 'five', 'six'); + } + set P [S $cmd] + S delete + + execsql ROLLBACK; + + set ::rebase [sqlite3changeset_apply_v2 db $P xConflict] + expr [llength $::rebase]>0 + } $rebasable +} + +foreach {tn cmd rebasable} { + 1 patchset 0 + 2 changeset 1 +} { + do_test 4.2.$tn { + sqlite3session S db main + S attach * + execsql { + INSERT INTO t5 VALUES(5+$tn, 'five', 'six'); + } + set P [S $cmd] + S delete + + sqlite3rebaser_create R + R configure $::rebase + expr [catch {R rebase $P}]==0 + } $rebasable + + catch { R delete } +} +finish_test |