1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2003,2008 Oracle.  All rights reserved.
4#
5# $Id: fop006.tcl,v 12.14 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST	fop006
8# TEST	Test file system operations in multiple simultaneous
9# TEST	transactions.  Start one transaction, do a file operation.
10# TEST	Start a second transaction, do a file operation.  Abort
11# TEST	or commit txn1, then abort or commit txn2, and check for
12# TEST	appropriate outcome.
13proc fop006 { method { inmem 0 } args } {
14	source ./include.tcl
15
16	# The variable inmem determines whether the test is being
17	# run on regular named databases or named in-memory databases.
18	if { $inmem == 0 } {
19		set tnum "006"
20		set string "regular named databases"
21		set operator do_op
22	} else {
23		set tnum "008"
24		set string "in-memory named databases"
25		set operator do_inmem_op
26	}
27
28	if { [is_btree $method] != 1 } {
29		puts "Skipping fop$tnum for method $method"
30		return
31	}
32
33	set args [convert_args $method $args]
34	set omethod [convert_method $method]
35
36	env_cleanup $testdir
37	puts "\nFop$tnum ($method): Two file system ops,\
38	    each in its own transaction, for $string."
39
40	set exists {a b}
41	set noexist {foo bar}
42	set open {}
43	set cases {}
44	set ops {open open_create open_excl rename remove truncate}
45
46	# Set up cases where op1 is successful.
47	foreach retval { 0 "file exists" "no such file" } {
48		foreach end1 {abort commit} {
49			foreach op1 $ops {
50				foreach op2 $ops {
51					append cases " " [create_tests\
52					    $op1 $op2 $exists $noexist\
53					    $open $retval $end1]
54				}
55			}
56		}
57	}
58
59	# Set up evil two-op cases (op1 fails).  Omit open_create
60	# and truncate from op1 list -- open_create always succeeds
61	# and truncate requires a successful open.
62	foreach retval { 0 "file exists" "no such file" } {
63		foreach op1 { rename remove open open_excl } {
64			foreach op2 $ops {
65				append cases " " [create_badtests $op1 $op2 \
66					$exists $noexist $open $retval $end1]
67			}
68		}
69	}
70
71	# The structure of each case is:
72	# {{op1 {args} result end} {op2 {args} result}}
73	# A result of "0" indicates no error is expected.  Otherwise,
74	# the result is the expected error message.  The value of "end"
75	# indicates whether the transaction will be aborted or committed.
76	#
77	# Comment this loop out to remove the list of cases.
78#	set i 1
79#	foreach case $cases {
80#		puts "\tFop$tnum.$i: $case"
81#		incr i
82#	}
83
84	# To run a particular case, add the case in this format and
85	# uncomment.
86#	set cases {
87#		{{open_excl {foo} 0 abort} {rename {b foo} 0}}
88#	}
89
90	set testid 0
91
92	# Run all the cases
93	foreach case $cases {
94		incr testid
95
96		# Extract elements of the case
97		set op1 [lindex [lindex $case 0] 0]
98		set names1 [lindex [lindex $case 0] 1]
99		set res1 [lindex [lindex $case 0] 2]
100		set end1 [lindex [lindex $case 0] 3]
101
102		set op2 [lindex [lindex $case 1] 0]
103		set names2 [lindex [lindex $case 1] 1]
104		set res2 [lindex [lindex $case 1] 2]
105
106		puts "\tFop$tnum.$testid: $op1 ($names1) $res1 $end1;\
107		    $op2 ($names2) $res2."
108
109		foreach end2 { abort commit } {
110			# Create transactional environment.
111			set env [berkdb_env -create -home $testdir -txn]
112			error_check_good is_valid_env [is_valid_env $env] TRUE
113
114			# Create databases
115			if { $inmem == 0 } {
116				set db [eval {berkdb_open -create} \
117				    $omethod $args -env $env -auto_commit a]
118			} else {
119				set db [eval {berkdb_open -create} \
120				    $omethod $args -env $env -auto_commit {""} a]
121			}
122			error_check_good db_open [is_valid_db $db] TRUE
123			error_check_good db_put \
124			    [$db put 1 [chop_data $method a]] 0
125			error_check_good db_close [$db close] 0
126
127			if { $inmem == 0 } {
128				set db [eval {berkdb_open -create} \
129				    $omethod $args -env $env -auto_commit b]
130			} else {
131				set db [eval {berkdb_open -create} \
132				    $omethod $args -env $env -auto_commit {""} b]
133			}
134			error_check_good db_open [is_valid_db $db] TRUE
135			error_check_good db_put \
136			    [$db put 1 [chop_data $method a]] 0
137			error_check_good db_close [$db close] 0
138
139			# Start transaction 1 and perform a file op.
140			set txn1 [$env txn]
141			error_check_good \
142			    txn_begin [is_valid_txn $txn1 $env] TRUE
143			set result1 [$operator $omethod $op1 $names1 $txn1 $env $args]
144			if { $res1 == 0 } {
145				error_check_good \
146				    op1_should_succeed $result1 $res1
147			} else {
148				set error [extract_error $result1]
149				error_check_good op1_wrong_failure $error $res1
150			}
151
152			# Start transaction 2 before ending transaction 1.
153			set pid [exec $tclsh_path $test_path/wrap.tcl \
154			    fopscript.tcl $testdir/fop$tnum.log \
155			    $operator $omethod $op2 $end2 $res2 $names2 &]
156
157			# Sleep a bit to give txn2 a chance to block.
158			tclsleep 2
159
160			# End transaction 1 and close any open db handles.
161			# Txn2 will now unblock and finish.
162			error_check_good txn1_$end1 [$txn1 $end1] 0
163			set handles [berkdb handles]
164			foreach handle $handles {
165				if {[string range $handle 0 1] == "db" } {
166					error_check_good \
167					    db_close [$handle close] 0
168				}
169			}
170			watch_procs $pid 1 60 1
171
172			# Clean up for next case
173			error_check_good env_close [$env close] 0
174			catch { [berkdb envremove -home $testdir] } res
175
176			# Check for errors in log file.
177			set errstrings [eval findfail $testdir/fop$tnum.log]
178			foreach str $errstrings {
179				puts "FAIL: error message in log file: $str"
180			}
181			env_cleanup $testdir
182		}
183	}
184}
185
186