From 0c25628deda34694917b0e2928ccc4e9d9ed566c Mon Sep 17 00:00:00 2001
From: robk-tahoe <robk-tahoe@allmydata.com>
Date: Fri, 3 Oct 2008 15:48:33 -0700
Subject: [PATCH] fuse/runtests: add overlapping write tests

using both small and large blocksizes for writes, write a 1Mb file to fuse
where every write overlaps another.

This serves a useful purpose - in manual testing of blackmatch some time ago
most operations e.g. bulk copies, worked fine, but using rsync caused data
corruption on most files.  it turned out to be that rsync writes in 64K blocks,
but rather than making the last block short, the last block instead overlaps
the preceding (already written) block.  This revealed a problem where cache
files were being opened 'append' rather than 'write' and hence the overlapping
write to the fuse layer caused the overlapping portion of the file to be
duplicated in cache, leading to oversized and corrupt files being uploaded.
---
 contrib/fuse/runtests.py | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/contrib/fuse/runtests.py b/contrib/fuse/runtests.py
index 54c12a2e..cf009705 100644
--- a/contrib/fuse/runtests.py
+++ b/contrib/fuse/runtests.py
@@ -464,6 +464,34 @@ class SystemTest (object):
             tmpl = 'Expected file contents %r but found %r'
             raise TestFailure(tmpl, expected_body, uploaded_body)
 
+    def test_write_overlapping_small_writes(self, testcap, testdir):
+        self._write_test_overlap(testcap, testdir, name='large_overlap', bs=2**9, sz=2**20)
+
+    def test_write_overlapping_large_writes(self, testcap, testdir):
+        self._write_test_overlap(testcap, testdir, name='small_overlap', bs=2**18, sz=2**20)
+
+    def _write_test_overlap(self, testcap, testdir, name, bs, sz):
+        body = os.urandom(sz)
+        try:
+            path = os.path.join(testdir, name)
+            f = file(path, 'w')
+        except Exception, err:
+            tmpl = 'Could not open file for write at %r: %r'
+            raise TestFailure(tmpl, path, err)
+        try:
+            for posn in range(0,sz,bs):
+                start = max(0, posn-bs)
+                end = min(sz, posn+bs)
+                f.seek(start)
+                f.write(body[start:end])
+            f.close()
+        except Exception, err:
+            tmpl = 'Could not write to file %r: %r'
+            raise TestFailure(tmpl, path, err)
+
+        self._check_write(testcap, name, body)
+
+
     # Utilities:
     def run_tahoe(self, *args):
         realargs = ('tahoe',) + args
-- 
2.45.2