aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rwxr-xr-xtest/T380-atomicity.sh2
-rw-r--r--test/atomicity.gdb54
-rw-r--r--test/atomicity.py71
3 files changed, 72 insertions, 55 deletions
diff --git a/test/T380-atomicity.sh b/test/T380-atomicity.sh
index 2daef906..ee1e2f43 100755
--- a/test/T380-atomicity.sh
+++ b/test/T380-atomicity.sh
@@ -64,7 +64,7 @@ if test_require_external_prereq gdb; then
# -tty /dev/null works around a conflict between the 'timeout' wrapper
# and gdb's attempt to control the TTY.
export MAIL_DIR
- gdb -tty /dev/null -batch -x $TEST_DIRECTORY/atomicity.gdb notmuch 1>gdb.out 2>&1
+ gdb -tty /dev/null -batch -x $TEST_DIRECTORY/atomicity.py notmuch 1>gdb.out 2>&1
# Get the final, golden output
notmuch search '*' > expected
diff --git a/test/atomicity.gdb b/test/atomicity.gdb
deleted file mode 100644
index 15adb16c..00000000
--- a/test/atomicity.gdb
+++ /dev/null
@@ -1,54 +0,0 @@
-# This gdb script runs notmuch new and simulates killing and
-# restarting notmuch new after every Xapian commit. To simulate this
-# more efficiently, this script runs notmuch new and, immediately
-# after every Xapian commit, it *pauses* the running notmuch new,
-# copies the entire database and maildir to a snapshot directory, and
-# executes a full notmuch new on that snapshot, comparing the final
-# results with the expected output. It can then resume the paused
-# notmuch new, which is still running on the original maildir, and
-# repeat this process.
-
-set args new
-
-# Make Xapian commit after every operation instead of batching
-set environment XAPIAN_FLUSH_THRESHOLD = 1
-
-# gdb can't keep track of a simple integer. This is me weeping.
-shell echo 0 > outcount
-
-shell touch inodes
-
-# work around apparent issue with lazy library loading on some
-# platforms
-set breakpoint pending on
-
-break rename
-commands
-# As an optimization, only consider snapshots after a Xapian commit.
-# Xapian overwrites record.base? as the last step in the commit.
-shell echo > gdbcmd
-shell stat -c %i $MAIL_DIR/.notmuch/xapian/record.base* > inodes.new
-shell if cmp inodes inodes.new; then echo cont > gdbcmd; fi
-shell mv inodes.new inodes
-source gdbcmd
-
-# Save a backtrace in case the test does fail
-set logging file backtrace
-set logging on
-backtrace
-set logging off
-shell mv backtrace backtrace.`cat outcount`
-
-# Snapshot the database
-shell rm -r $MAIL_DIR.snap/.notmuch
-shell cp -r $MAIL_DIR/.notmuch $MAIL_DIR.snap/.notmuch
-# Restore the mtime of $MAIL_DIR.snap, which we just changed
-shell touch -r $MAIL_DIR $MAIL_DIR.snap
-# Run notmuch new to completion on the snapshot
-shell NOTMUCH_CONFIG=${NOTMUCH_CONFIG}.snap XAPIAN_FLUSH_THRESHOLD=1000 notmuch new > /dev/null
-shell NOTMUCH_CONFIG=${NOTMUCH_CONFIG}.snap notmuch search '*' > search.`cat outcount` 2>&1
-shell echo $(expr $(cat outcount) + 1) > outcount
-cont
-end
-
-run
diff --git a/test/atomicity.py b/test/atomicity.py
new file mode 100644
index 00000000..01a42051
--- /dev/null
+++ b/test/atomicity.py
@@ -0,0 +1,71 @@
+# This gdb Python script runs notmuch new and simulates killing and
+# restarting notmuch new after every Xapian commit. To simulate this
+# more efficiently, this script runs notmuch new and, immediately
+# after every Xapian commit, it *pauses* the running notmuch new,
+# copies the entire database and maildir to a snapshot directory, and
+# executes a full notmuch new on that snapshot, comparing the final
+# results with the expected output. It can then resume the paused
+# notmuch new, which is still running on the original maildir, and
+# repeat this process.
+
+import gdb
+import os
+import glob
+import shutil
+import subprocess
+
+gdb.execute('set args new')
+
+# Make Xapian commit after every operation instead of batching
+gdb.execute('set environment XAPIAN_FLUSH_THRESHOLD = 1')
+
+maildir = os.environ['MAIL_DIR']
+
+# Trap calls to rename, which happens just before Xapian commits
+class RenameBreakpoint(gdb.Breakpoint):
+ def __init__(self, *args, **kwargs):
+ super(RenameBreakpoint, self).__init__(*args, **kwargs)
+ self.last_inodes = {}
+ self.n = 0
+
+ def stop(self):
+ # As an optimization, only consider snapshots after a Xapian
+ # has really committed. Xapian overwrites record.base? as the
+ # last step in the commit, so keep an eye on their inumbers.
+ inodes = {}
+ for path in glob.glob('%s/.notmuch/xapian/record.base*' % maildir):
+ inodes[path] = os.stat(path).st_ino
+ if inodes == self.last_inodes:
+ # Continue
+ return False
+ self.last_inodes = inodes
+
+ # Save a backtrace in case the test does fail
+ backtrace = gdb.execute('backtrace', to_string=True)
+ open('backtrace.%d' % self.n, 'w').write(backtrace)
+
+ # Snapshot the database
+ shutil.rmtree('%s.snap/.notmuch' % maildir)
+ shutil.copytree('%s/.notmuch' % maildir, '%s.snap/.notmuch' % maildir)
+ # Restore the mtime of $MAIL_DIR.snap/
+ shutil.copystat('%s/.notmuch' % maildir, '%s.snap/.notmuch' % maildir)
+
+ # Run notmuch new to completion on the snapshot
+ env = os.environ.copy()
+ env.update(NOTMUCH_CONFIG=os.environ['NOTMUCH_CONFIG'] + '.snap',
+ XAPIAN_FLUSH_THRESHOLD='1000')
+ subprocess.check_call(
+ ['notmuch', 'new'], env=env, stdout=open('/dev/null', 'w'))
+ subprocess.check_call(
+ ['notmuch', 'search', '*'], env=env,
+ stdout=open('search.%d' % self.n, 'w'))
+
+ # Tell the shell how far we've gotten
+ open('outcount', 'w').write(str(self.n + 1))
+
+ # Continue
+ self.n += 1
+ return False
+RenameBreakpoint('rename')
+
+gdb.execute('run')