--- /dev/null
+Return-Path: <aclements@csail.mit.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by olra.theworths.org (Postfix) with ESMTP id 15AAB431FBC\r
+ for <notmuch@notmuchmail.org>; Fri, 3 Oct 2014 09:58:36 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -2.3\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-2.3 tagged_above=-999 required=5\r
+ tests=[RCVD_IN_DNSWL_MED=-2.3] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+ by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id QmaXSL60CxpO for <notmuch@notmuchmail.org>;\r
+ Fri, 3 Oct 2014 09:58:29 -0700 (PDT)\r
+Received: from dmz-mailsec-scanner-2.mit.edu (dmz-mailsec-scanner-2.mit.edu\r
+ [18.9.25.13])\r
+ (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
+ (No client certificate requested)\r
+ by olra.theworths.org (Postfix) with ESMTPS id 59798431FB6\r
+ for <notmuch@notmuchmail.org>; Fri, 3 Oct 2014 09:58:29 -0700 (PDT)\r
+X-AuditID: 1209190d-f79c06d000006f95-80-542ed5b40568\r
+Received: from mailhub-auth-1.mit.edu ( [18.9.21.35])\r
+ (using TLS with cipher AES256-SHA (256/256 bits))\r
+ (Client did not present a certificate)\r
+ by dmz-mailsec-scanner-2.mit.edu (Symantec Messaging Gateway) with SMTP\r
+ id 7E.EA.28565.4B5DE245; Fri, 3 Oct 2014 12:58:28 -0400 (EDT)\r
+Received: from outgoing.mit.edu (outgoing-auth-1.mit.edu [18.9.28.11])\r
+ by mailhub-auth-1.mit.edu (8.13.8/8.9.2) with ESMTP id s93GwQhN028669; \r
+ Fri, 3 Oct 2014 12:58:26 -0400\r
+Received: from drake.dyndns.org\r
+ (static-155-212-141-65.mas.onecommunications.net [155.212.141.65])\r
+ (authenticated bits=0)\r
+ (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+ by outgoing.mit.edu (8.13.8/8.12.4) with ESMTP id s93GwEBP019243\r
+ (version=TLSv1/SSLv3 cipher=AES128-SHA bits=128 verify=NOT);\r
+ Fri, 3 Oct 2014 12:58:24 -0400\r
+Received: from amthrax by drake.dyndns.org with local (Exim 4.84)\r
+ (envelope-from <aclements@csail.mit.edu>)\r
+ id 1Xa6BJ-00022m-AR; Fri, 03 Oct 2014 12:58:13 -0400\r
+From: Austin Clements <aclements@csail.mit.edu>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH] test: Port atomicity test to Python\r
+Date: Fri, 3 Oct 2014 12:58:03 -0400\r
+Message-Id: <1412355483-7670-1-git-send-email-aclements@csail.mit.edu>\r
+X-Mailer: git-send-email 2.1.0\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFlrLIsWRmVeSWpSXmKPExsUixCmqrLvlql6IwYIlahZT1jxmtbh+cyaz\r
+ A5PHkdeb2TyerbrFHMAUxWWTkpqTWZZapG+XwJXRO1+3YK9JxcnWJawNjJs1uxg5OCQETCSm\r
+ H5TtYuQEMsUkLtxbzwZiCwnMZpK4cjSni5ELyN7AKNHzeB8bhHOdSWLm7EWMEM4SRonJ+zYw\r
+ g7SwCehLrFg7iRXEFhGQlth5dzaYzSzgLPHw80Y2kG3CQNv2vVcHCbMIqEpsutDJCBLmFXCT\r
+ uPsgD+IIOYkNu/8zTmDkXcDIsIpRNiW3Sjc3MTOnODVZtzg5MS8vtUjXSC83s0QvNaV0EyM4\r
+ JCR5dzC+O6h0iFGAg1GJh/fDDd0QIdbEsuLK3EOMkhxMSqK8sVf0QoT4kvJTKjMSizPii0pz\r
+ UosPMUpwMCuJ8C5YCZTjTUmsrEotyodJSXOwKInzbvrBFyIkkJ5YkpqdmlqQWgSTleHgUJLg\r
+ LQUZKliUmp5akZaZU4KQZuLgBBnOAzQ8HaSGt7ggMbc4Mx0if4pRl2PSxve9TEIsefl5qVLi\r
+ vIsuAxUJgBRllObBzYHF8itGcaC3hHnzQUbxANMA3KRXQEuYgJa8s9cFWVKSiJCSamDMuJsr\r
+ bRp+TGrdofwLgdka97edEE3YeulqjXbsrmei78z/tb35rTxPRu8Y0yqhyc9LcpLNv7P9YSvR\r
+ yqhYsl5TzHeHcdheDkmRRzONf969L7b7qmVPf43uv63h2yd5fJzz3PK7rz1bCWPCxwsdPtf0\r
+ E4t+cLrUVc15frXfac+FyBLubYfOz2VWYinOSDTUYi4qTgQAc5E+acACAAA=\r
+Cc: Austin Clements <aclements@csail.mit.edu>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Fri, 03 Oct 2014 16:58:36 -0000\r
+\r
+Previously, this was implemented using a horrible GDB script (because\r
+there is no such thing as a non-horrible GDB script). This GDB script\r
+often broke with newer versions of GDB for mysterious reasons. Port\r
+the test script to GDB's Python API, which makes the code much cleaner\r
+and, hopefully, more stable.\r
+---\r
+\r
+Hi Amadeusz. Does this patch fix the problem for you? I don't have\r
+GDB 7.8, so I can't test against it, but the Python interface is less\r
+fragile than the GDB scripting language. (Even if this doesn't fix\r
+your problem, I think we should switch to the Python interface.)\r
+\r
+ test/T380-atomicity.sh | 2 +-\r
+ test/atomicity.gdb | 54 --------------------------------------\r
+ test/atomicity.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++\r
+ 3 files changed, 72 insertions(+), 55 deletions(-)\r
+ delete mode 100644 test/atomicity.gdb\r
+ create mode 100644 test/atomicity.py\r
+\r
+diff --git a/test/T380-atomicity.sh b/test/T380-atomicity.sh\r
+index 2daef90..ee1e2f4 100755\r
+--- a/test/T380-atomicity.sh\r
++++ b/test/T380-atomicity.sh\r
+@@ -64,7 +64,7 @@ if test_require_external_prereq gdb; then\r
+ # -tty /dev/null works around a conflict between the 'timeout' wrapper\r
+ # and gdb's attempt to control the TTY.\r
+ export MAIL_DIR\r
+- gdb -tty /dev/null -batch -x $TEST_DIRECTORY/atomicity.gdb notmuch 1>gdb.out 2>&1\r
++ gdb -tty /dev/null -batch -x $TEST_DIRECTORY/atomicity.py notmuch 1>gdb.out 2>&1\r
+ \r
+ # Get the final, golden output\r
+ notmuch search '*' > expected\r
+diff --git a/test/atomicity.gdb b/test/atomicity.gdb\r
+deleted file mode 100644\r
+index 15adb16..0000000\r
+--- a/test/atomicity.gdb\r
++++ /dev/null\r
+@@ -1,54 +0,0 @@\r
+-# This gdb script runs notmuch new and simulates killing and\r
+-# restarting notmuch new after every Xapian commit. To simulate this\r
+-# more efficiently, this script runs notmuch new and, immediately\r
+-# after every Xapian commit, it *pauses* the running notmuch new,\r
+-# copies the entire database and maildir to a snapshot directory, and\r
+-# executes a full notmuch new on that snapshot, comparing the final\r
+-# results with the expected output. It can then resume the paused\r
+-# notmuch new, which is still running on the original maildir, and\r
+-# repeat this process.\r
+-\r
+-set args new\r
+-\r
+-# Make Xapian commit after every operation instead of batching\r
+-set environment XAPIAN_FLUSH_THRESHOLD = 1\r
+-\r
+-# gdb can't keep track of a simple integer. This is me weeping.\r
+-shell echo 0 > outcount\r
+-\r
+-shell touch inodes\r
+-\r
+-# work around apparent issue with lazy library loading on some\r
+-# platforms\r
+-set breakpoint pending on\r
+-\r
+-break rename\r
+-commands\r
+-# As an optimization, only consider snapshots after a Xapian commit.\r
+-# Xapian overwrites record.base? as the last step in the commit.\r
+-shell echo > gdbcmd\r
+-shell stat -c %i $MAIL_DIR/.notmuch/xapian/record.base* > inodes.new\r
+-shell if cmp inodes inodes.new; then echo cont > gdbcmd; fi\r
+-shell mv inodes.new inodes\r
+-source gdbcmd\r
+-\r
+-# Save a backtrace in case the test does fail\r
+-set logging file backtrace\r
+-set logging on\r
+-backtrace\r
+-set logging off\r
+-shell mv backtrace backtrace.`cat outcount`\r
+-\r
+-# Snapshot the database\r
+-shell rm -r $MAIL_DIR.snap/.notmuch\r
+-shell cp -r $MAIL_DIR/.notmuch $MAIL_DIR.snap/.notmuch\r
+-# Restore the mtime of $MAIL_DIR.snap, which we just changed\r
+-shell touch -r $MAIL_DIR $MAIL_DIR.snap\r
+-# Run notmuch new to completion on the snapshot\r
+-shell NOTMUCH_CONFIG=${NOTMUCH_CONFIG}.snap XAPIAN_FLUSH_THRESHOLD=1000 notmuch new > /dev/null\r
+-shell NOTMUCH_CONFIG=${NOTMUCH_CONFIG}.snap notmuch search '*' > search.`cat outcount` 2>&1\r
+-shell echo $(expr $(cat outcount) + 1) > outcount\r
+-cont\r
+-end\r
+-\r
+-run\r
+diff --git a/test/atomicity.py b/test/atomicity.py\r
+new file mode 100644\r
+index 0000000..01a4205\r
+--- /dev/null\r
++++ b/test/atomicity.py\r
+@@ -0,0 +1,71 @@\r
++# This gdb Python script runs notmuch new and simulates killing and\r
++# restarting notmuch new after every Xapian commit. To simulate this\r
++# more efficiently, this script runs notmuch new and, immediately\r
++# after every Xapian commit, it *pauses* the running notmuch new,\r
++# copies the entire database and maildir to a snapshot directory, and\r
++# executes a full notmuch new on that snapshot, comparing the final\r
++# results with the expected output. It can then resume the paused\r
++# notmuch new, which is still running on the original maildir, and\r
++# repeat this process.\r
++\r
++import gdb\r
++import os\r
++import glob\r
++import shutil\r
++import subprocess\r
++\r
++gdb.execute('set args new')\r
++\r
++# Make Xapian commit after every operation instead of batching\r
++gdb.execute('set environment XAPIAN_FLUSH_THRESHOLD = 1')\r
++\r
++maildir = os.environ['MAIL_DIR']\r
++\r
++# Trap calls to rename, which happens just before Xapian commits\r
++class RenameBreakpoint(gdb.Breakpoint):\r
++ def __init__(self, *args, **kwargs):\r
++ super(RenameBreakpoint, self).__init__(*args, **kwargs)\r
++ self.last_inodes = {}\r
++ self.n = 0\r
++\r
++ def stop(self):\r
++ # As an optimization, only consider snapshots after a Xapian\r
++ # has really committed. Xapian overwrites record.base? as the\r
++ # last step in the commit, so keep an eye on their inumbers.\r
++ inodes = {}\r
++ for path in glob.glob('%s/.notmuch/xapian/record.base*' % maildir):\r
++ inodes[path] = os.stat(path).st_ino\r
++ if inodes == self.last_inodes:\r
++ # Continue\r
++ return False\r
++ self.last_inodes = inodes\r
++\r
++ # Save a backtrace in case the test does fail\r
++ backtrace = gdb.execute('backtrace', to_string=True)\r
++ open('backtrace.%d' % self.n, 'w').write(backtrace)\r
++\r
++ # Snapshot the database\r
++ shutil.rmtree('%s.snap/.notmuch' % maildir)\r
++ shutil.copytree('%s/.notmuch' % maildir, '%s.snap/.notmuch' % maildir)\r
++ # Restore the mtime of $MAIL_DIR.snap/\r
++ shutil.copystat('%s/.notmuch' % maildir, '%s.snap/.notmuch' % maildir)\r
++\r
++ # Run notmuch new to completion on the snapshot\r
++ env = os.environ.copy()\r
++ env.update(NOTMUCH_CONFIG=os.environ['NOTMUCH_CONFIG'] + '.snap',\r
++ XAPIAN_FLUSH_THRESHOLD='1000')\r
++ subprocess.check_call(\r
++ ['notmuch', 'new'], env=env, stdout=open('/dev/null', 'w'))\r
++ subprocess.check_call(\r
++ ['notmuch', 'search', '*'], env=env,\r
++ stdout=open('search.%d' % self.n, 'w'))\r
++\r
++ # Tell the shell how far we've gotten\r
++ open('outcount', 'w').write(str(self.n + 1))\r
++\r
++ # Continue\r
++ self.n += 1\r
++ return False\r
++RenameBreakpoint('rename')\r
++\r
++gdb.execute('run')\r
+-- \r
+2.1.0\r
+\r