From 56282613c5108df825929ad914448c9e97a15b77 Mon Sep 17 00:00:00 2001
From: Zooko O'Whielacronx <zooko@zooko.com>
Date: Mon, 28 Jul 2008 16:43:17 -0700
Subject: [PATCH] tests: add test_system.Checker which tests basic checking
 (without verification) functionality

---
 src/allmydata/test/test_system.py | 129 ++++++++++++++++++++++++++++--
 1 file changed, 121 insertions(+), 8 deletions(-)

diff --git a/src/allmydata/test/test_system.py b/src/allmydata/test/test_system.py
index 797826c6..74071766 100644
--- a/src/allmydata/test/test_system.py
+++ b/src/allmydata/test/test_system.py
@@ -1,6 +1,5 @@
-
 from base64 import b32encode
-import os, sys, time, re, simplejson
+import os, random, sys, time, re, simplejson
 from cStringIO import StringIO
 from twisted.trial import unittest
 from twisted.internet import defer
@@ -9,7 +8,7 @@ from twisted.internet.error import ConnectionDone, ConnectionLost
 import allmydata
 from allmydata import uri, storage, offloaded
 from allmydata.immutable import download, upload, filenode
-from allmydata.util import idlib, mathutil
+from allmydata.util import idlib, mathutil, testutil
 from allmydata.util import log, base32
 from allmydata.scripts import runner, cli
 from allmydata.interfaces import IDirectoryNode, IFileNode, IFileURI
@@ -1708,8 +1707,122 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         return d
 
 
-class Fast(SystemTestMixin, unittest.TestCase):
-    # this is the beginning of a faster system-test framework
-    def test_setup(self):
-        self.basedir = "system/Fast/test_foo"
-        return self.set_up_nodes(5)
+class Checker(SystemTestMixin, unittest.TestCase):
+    def setUp(self):
+        self.basedir = "system/SystemTest/Checker"
+        TEST_DATA="\x02"*1000
+
+        d = defer.maybeDeferred(SystemTestMixin.setUp, self)
+        d.addCallback(lambda x: self.set_up_nodes())
+
+        def _upload_a_file(ignored):
+            d2 = self.clients[0].upload(upload.Data(TEST_DATA, convergence=""))
+            d2.addCallback(lambda u: self.clients[0].create_node_from_uri(u.uri))
+            return d2
+        d.addCallback(_upload_a_file)
+
+        def _stash_it(filenode):
+            self.filenode = filenode
+        d.addCallback(_stash_it)
+        return d
+
+    def _find_shares(self, unused=None):
+        shares = {} # k: (i, sharenum), v: data
+
+        for i, c in enumerate(self.clients):
+            sharedir = c.getServiceNamed("storage").sharedir
+            for (dirp, dirns, fns) in os.walk(sharedir):
+                for fn in fns:
+                    try:
+                        sharenum = int(fn)
+                    except TypeError:
+                        # Whoops, I guess that's not a share file then.
+                        pass
+                    else:
+                        data = open(os.path.join(sharedir, dirp, fn), "r").read()
+                        shares[(i, sharenum)] = data
+
+        return shares
+
+    def _replace_shares(self, newshares):
+        for i, c in enumerate(self.clients):
+            sharedir = c.getServiceNamed("storage").sharedir
+            for (dirp, dirns, fns) in os.walk(sharedir):
+                for fn in fns:
+                    try:
+                        sharenum = int(fn)
+                    except TypeError:
+                        # Whoops, I guess that's not a share file then.
+                        pass
+                    else:
+                        pathtosharefile = os.path.join(sharedir, dirp, fn)
+                        os.unlink(pathtosharefile)
+                        newdata = newshares.get((i, sharenum))
+                        if newdata is not None:
+                            open(pathtosharefile, "w").write(newdata)
+
+    def _corrupt_a_share(self, unused=None):
+        """ Exactly one bit of exactly one share on disk will be flipped (randomly selected). """
+        shares = self._find_shares()
+        ks = shares.keys()
+        k = random.choice(ks)
+        data = shares[k]
+        shares[k]  = testutil.flip_one_bit(shares[k])
+        self._replace_shares(shares)
+
+    def test_test_code(self):
+        # The following process of stashing the shares, running
+        # _replace_shares, and asserting that the new set of shares equals the
+        # old is more to test this test code than to test the Tahoe code...
+        d = defer.succeed(None)
+        d.addCallback(self._find_shares)
+        stash = [None]
+        def _stash_it(res):
+            stash[0] = res
+            return res
+
+        d.addCallback(_stash_it)
+        d.addCallback(self._replace_shares)
+
+        def _compare(res):
+            oldshares = stash[0]
+            self.failUnless(isinstance(oldshares, dict), oldshares)
+            self.failUnlessEqual(oldshares, res)
+
+        d.addCallback(self._find_shares)
+        d.addCallback(_compare)
+
+        d.addCallback(lambda ignore: self._replace_shares({}))
+        d.addCallback(self._find_shares)
+        d.addCallback(lambda x: self.failUnlessEqual(x, {}))
+
+        return d
+
+    def test_check_without_verify(self):
+        """ Check says the file is healthy when none of the shares have been
+        touched.  It says that the file is unhealthy when all of them have
+        been removed. It says that the file is healthy if one bit of one share
+        has been flipped."""
+        d = defer.succeed(self.filenode)
+        def _check1(filenode):
+            d2 = filenode.check(verify=False, repair=False)
+            d2.addCallback(lambda checkres: self.failUnless(checkres.is_healthy()))
+            return d2
+        d.addCallback(_check1)
+
+        d.addCallback(self._corrupt_a_share)
+        def _check2(ignored):
+            d2 = self.filenode.check(verify=False, repair=False)
+            d2.addCallback(lambda checkres: self.failUnless(checkres.is_healthy()))
+            return d2
+        d.addCallback(_check2)
+        return d
+
+        d.addCallback(lambda ignore: self._replace_shares({}))
+        def _check3(ignored):
+            d2 = self.filenode.check(verify=False, repair=False)
+            d2.addCallback(lambda checkres: self.failIf(checkres.is_healthy()))
+            return d2
+        d.addCallback(_check3)
+
+        return d
-- 
2.45.2