def test_merge_servers(self):
     # merge_servers merges a list of upload_servers and a dict of
     # shareid -> serverid mappings.
     shares = {
         1: set(["server1"]),
         2: set(["server2"]),
         3: set(["server3"]),
         4: set(["server4", "server5"]),
         5: set(["server1", "server2"]),
     }
     # if not provided with a upload_servers argument, it should just
     # return the first argument unchanged.
     self.failUnlessEqual(shares, merge_servers(shares, set([])))
     trackers = []
     for (i, server) in [(i, "server%d" % i) for i in range(5, 9)]:
         t = FakeServerTracker(server, [i])
         trackers.append(t)
     expected = {
         1: set(["server1"]),
         2: set(["server2"]),
         3: set(["server3"]),
         4: set(["server4", "server5"]),
         5: set(["server1", "server2", "server5"]),
         6: set(["server6"]),
         7: set(["server7"]),
         8: set(["server8"]),
     }
     self.failUnlessEqual(expected, merge_servers(shares, set(trackers)))
     shares2 = {}
     expected = {
         5: set(["server5"]),
         6: set(["server6"]),
         7: set(["server7"]),
         8: set(["server8"]),
     }
     self.failUnlessEqual(expected, merge_servers(shares2, set(trackers)))
     shares3 = {}
     trackers = []
     expected = {}
     for (i, server) in [(i, "server%d" % i) for i in range(10)]:
         shares3[i] = set([server])
         t = FakeServerTracker(server, [i])
         trackers.append(t)
         expected[i] = set([server])
     self.failUnlessEqual(expected, merge_servers(shares3, set(trackers)))
    def test_servers_of_happiness_utility_function(self):
        # These tests are concerned with the servers_of_happiness()
        # utility function, and its underlying matching algorithm. Other
        # aspects of the servers_of_happiness behavior are tested
        # elsehwere These tests exist to ensure that
        # servers_of_happiness doesn't under or overcount the happiness
        # value for given inputs.

        # servers_of_happiness expects a dict of
        # shnum => set(serverids) as a preexisting shares argument.
        test1 = {
            1: set(["server1"]),
            2: set(["server2"]),
            3: set(["server3"]),
            4: set(["server4"])
        }
        happy = servers_of_happiness(test1)
        self.failUnlessEqual(4, happy)
        test1[4] = set(["server1"])
        # We've added a duplicate server, so now servers_of_happiness
        # should be 3 instead of 4.
        happy = servers_of_happiness(test1)
        self.failUnlessEqual(3, happy)
        # The second argument of merge_servers should be a set of objects with
        # serverid and buckets as attributes. In actual use, these will be
        # ServerTracker instances, but for testing it is fine to make a
        # FakeServerTracker whose job is to hold those instance variables to
        # test that part.
        trackers = []
        for (i, server) in [(i, "server%d" % i) for i in range(5, 9)]:
            t = FakeServerTracker(server, [i])
            trackers.append(t)
        # Recall that test1 is a server layout with servers_of_happiness
        # = 3.  Since there isn't any overlap between the shnum ->
        # set([serverid]) correspondences in test1 and those in trackers,
        # the result here should be 7.
        test2 = merge_servers(test1, set(trackers))
        happy = servers_of_happiness(test2)
        self.failUnlessEqual(7, happy)
        # Now add an overlapping server to trackers. This is redundant,
        # so it should not cause the previously reported happiness value
        # to change.
        t = FakeServerTracker("server1", [1])
        trackers.append(t)
        test2 = merge_servers(test1, set(trackers))
        happy = servers_of_happiness(test2)
        self.failUnlessEqual(7, happy)
        test = {}
        happy = servers_of_happiness(test)
        self.failUnlessEqual(0, happy)
        # Test a more substantial overlap between the trackers and the
        # existing assignments.
        test = {
            1: set(['server1']),
            2: set(['server2']),
            3: set(['server3']),
            4: set(['server4']),
        }
        trackers = []
        t = FakeServerTracker('server5', [4])
        trackers.append(t)
        t = FakeServerTracker('server6', [3, 5])
        trackers.append(t)
        # The value returned by servers_of_happiness is the size
        # of a maximum matching in the bipartite graph that
        # servers_of_happiness() makes between serverids and share
        # numbers. It should find something like this:
        # (server 1, share 1)
        # (server 2, share 2)
        # (server 3, share 3)
        # (server 5, share 4)
        # (server 6, share 5)
        #
        # and, since there are 5 edges in this matching, it should
        # return 5.
        test2 = merge_servers(test, set(trackers))
        happy = servers_of_happiness(test2)
        self.failUnlessEqual(5, happy)
        # Zooko's first puzzle:
        # (from http://allmydata.org/trac/tahoe-lafs/ticket/778#comment:156)
        #
        # server 1: shares 0, 1
        # server 2: shares 1, 2
        # server 3: share 2
        #
        # This should yield happiness of 3.
        test = {
            0: set(['server1']),
            1: set(['server1', 'server2']),
            2: set(['server2', 'server3']),
        }
        self.failUnlessEqual(3, servers_of_happiness(test))
        # Zooko's second puzzle:
        # (from http://allmydata.org/trac/tahoe-lafs/ticket/778#comment:158)
        #
        # server 1: shares 0, 1
        # server 2: share 1
        #
        # This should yield happiness of 2.
        test = {
            0: set(['server1']),
            1: set(['server1', 'server2']),
        }
        self.failUnlessEqual(2, servers_of_happiness(test))