/rev/77f74e53f796
changeset: 1250:77f74e53f796
user: Marcel Keller <[email protected]>
date: Sat Sep 19 15:34:01 2009 +0200
summary: Merged with Martin.
diffstat:
doc/program-counters.txt | 72 ++++++++++++-----------------------
doc/runtime.txt | 8 +---
viff/active.py | 13 +-----
viff/aes.py | 3 +-
viff/comparison.py | 11 +-----
viff/equality.py | 3 -
viff/paillier.py | 5 +--
viff/passive.py | 39 +++++++++----------
viff/runtime.py | 26 +-----------
viff/test/test_basic_runtime.py | 71 +++++++----------------------------
viff/test/test_thresholds.py | 40 --------------------
11 files changed, 69 insertions(+), 222 deletions(-)
diffs (791 lines):
diff -r 0f35ae3f503b -r 77f74e53f796 doc/program-counters.txt
--- a/doc/program-counters.txt Thu Sep 17 17:59:08 2009 +0200
+++ b/doc/program-counters.txt Sat Sep 19 15:34:01 2009 +0200
@@ -88,63 +88,41 @@
point in the execution tree. The execution tree is never explicitly
constructed in VIFF, so a simple static numbering is not possible.
-Instead we mark methods that need to increment the program counter
-with the :func:`viff.runtime.increment_pc` decorator. The program
-counter starts at the value ``[0]`` and the decorated method will now
-begin by doing::
+The program counter starts at the value ``[0]``. It is changed in two
+cases:
- self.program_counter[-1] += 1
- self.program_counter.append(0)
+* when a callback is scheduled using
+ :meth:`viff.runtime.BasicRuntime.schedule_callback`, a new
+ sub-program counter is allocated. A sub-program counter is simply a
+ program counter with another digit. Because of the asynchronous
+ network, the callback will be invoked at an unknown later time. When
+ invoked, it sees the sub-program counter. This ensures that that the
+ parties agree on any network traffic produced in the callback.
-before it executes its body. When the body is finished, the method
-does::
+ When a piece of code like this::
- self.program_counter.pop()
+ def cb(ignored):
+ print "callback:", self.program_counter
+ d = Deferred()
-before it returns. A method :meth:`foo` defined like this::
+ print "main:", self.program_counter
+ self.schedule_callback(d, cb)
+ print "main:", self.program_counter
- @increment_pc
- def foo(self):
- print "foo:", self.program_counter
+ d.callback(None)
-is thus turned into this::
+ is executed, one will see output like this:
- def foo(self):
- self.program_counter[-1] += 1
- self.program_counter.append(0)
- print "foo:", self.program_counter
- self.program_counter.pop()
+ .. code-block:: none
-and when executed starting from the initial program counter of ``[0]``
-we see that it prints ``foo: [1, 0]`` and leaves the program counter
-at ``[1]`` after it returns. It is very important that the program
-counter is left changed like this, for this means that the next call
-to :meth:`foo` will print ``foo: [2, 0]`` and increment the program
-counter to ``[2]``.
+ main: [0]
+ main: [1]
+ callback: [0, 0]
-If we have a method :meth:`bar` which calls :meth:`foo` several times::
+* some functions depend on a unique program counter. These functions
+ simply increase the last digit in the current program counter::
- @increment_pc
- def bar(self):
- print "bar:", self.program_counter
- self.foo()
- print "bar:", self.program_counter
- self.foo()
- print "bar:", self.program_counter
-
-then the result of calling :meth:`bar` will be:
-
-.. code-block:: none
-
- bar: [1, 0]
- foo: [1, 1, 0]
- bar: [1, 1]
- foo: [1, 2, 0]
- bar: [1, 2]
-
-Notice how each sub-call adds another digit to the counter and how it
-increments the counter used at the level of the caller. This system
-ensures that all program counters are unique.
+ self.program_counter[-1] += 1
Alternatives
diff -r 0f35ae3f503b -r 77f74e53f796 doc/runtime.txt
--- a/doc/runtime.txt Thu Sep 17 17:59:08 2009 +0200
+++ b/doc/runtime.txt Sat Sep 19 15:34:01 2009 +0200
@@ -31,8 +31,6 @@
and other messages. They serve to distinguish messages sent with
the same program counter from one another.
- .. autofunction:: increment_pc
-
.. autofunction:: preprocess
See also :ref:`preprocessing` for more background information.
@@ -88,9 +86,7 @@
different parts of the program execution never reuses the
same program counter for different variables.
- The :func:`increment_pc` decorator is responsible for
- dynamically building the tree as the execution unfolds and
- :meth:`schedule_callback` is responsible for scheduling
- callbacks with the correct program counter.
+ The :meth:`schedule_callback` method is responsible for
+ scheduling callbacks with the correct program counter.
See :ref:`program-counters` for more background information.
diff -r 0f35ae3f503b -r 77f74e53f796 viff/active.py
--- a/viff/active.py Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/active.py Sat Sep 19 15:34:01 2009 +0200
@@ -27,7 +27,7 @@
from viff.util import rand
from viff.matrix import Matrix, hyper
from viff.passive import PassiveRuntime
-from viff.runtime import Share, increment_pc, preprocess, gather_shares
+from viff.runtime import Share, preprocess, gather_shares
from viff.runtime import ECHO, READY, SEND
@@ -37,7 +37,6 @@
broadcast.
"""
- @increment_pc
def _broadcast(self, sender, message=None):
"""Perform a Bracha broadcast.
@@ -47,6 +46,8 @@
protocol" by G. Bracha in Proc. 3rd ACM Symposium on
Principles of Distributed Computing, 1984, pages 154-162.
"""
+ # We need a unique program counter for each call.
+ self.program_counter[-1] += 1
result = Deferred()
pc = tuple(self.program_counter)
@@ -141,7 +142,6 @@
return result
- @increment_pc
def broadcast(self, senders, message=None):
"""Perform one or more Bracha broadcast(s).
@@ -186,7 +186,6 @@
#: to :const:`None` here and update it as necessary.
_hyper = None
- @increment_pc
def single_share_random(self, T, degree, field):
"""Share a random secret.
@@ -273,7 +272,6 @@
self.schedule_callback(result, exchange)
return result
- @increment_pc
def double_share_random(self, T, d1, d2, field):
"""Double-share a random secret using two polynomials.
@@ -376,7 +374,6 @@
self.schedule_callback(result, exchange)
return result
- @increment_pc
@preprocess("generate_triples")
def get_triple(self, field):
# This is a waste, but this function is only called if there
@@ -385,7 +382,6 @@
result.addCallback(lambda triples: triples[0])
return result
- @increment_pc
def generate_triples(self, field):
"""Generate multiplication triples.
@@ -425,14 +421,12 @@
class TriplesPRSSMixin:
"""Mixin class for generating multiplication triples using PRSS."""
- @increment_pc
@preprocess("generate_triples")
def get_triple(self, field):
count, result = self.generate_triples(field, quantity=1)
result.addCallback(lambda triples: triples[0])
return result
- @increment_pc
def generate_triples(self, field, quantity=20):
"""Generate *quantity* multiplication triples using PRSS.
@@ -470,7 +464,6 @@
:class:`ActiveRuntime` instead.
"""
- @increment_pc
def mul(self, share_x, share_y):
"""Multiplication of shares.
diff -r 0f35ae3f503b -r 77f74e53f796 viff/aes.py
--- a/viff/aes.py Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/aes.py Sat Sep 19 15:34:01 2009 +0200
@@ -24,7 +24,7 @@
import operator
from viff.field import GF256
-from viff.runtime import Share, gather_shares, increment_pc
+from viff.runtime import Share, gather_shares
from viff.matrix import Matrix
@@ -360,7 +360,6 @@
"or of shares thereof."
return input
- @increment_pc
def encrypt(self, cleartext, key, benchmark=False, prepare_at_once=False):
"""Rijndael encryption.
diff -r 0f35ae3f503b -r 77f74e53f796 viff/comparison.py
--- a/viff/comparison.py Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/comparison.py Sat Sep 19 15:34:01 2009 +0200
@@ -25,7 +25,7 @@
import math
from viff.util import rand, profile
-from viff.runtime import Share, gather_shares, increment_pc
+from viff.runtime import Share, gather_shares
from viff.passive import PassiveRuntime
from viff.active import ActiveRuntime
from viff.field import GF256, FieldElement
@@ -34,7 +34,6 @@
class ComparisonToft05Mixin:
"""Comparison by Tomas Toft, 2005."""
- @increment_pc
def convert_bit_share(self, share, dst_field):
"""Convert a 0/1 share into dst_field."""
bit = rand.randint(0, 1)
@@ -67,7 +66,6 @@
return int_b, bit_bits
@profile
- @increment_pc
def greater_than_equal(self, share_a, share_b):
"""Compute ``share_a >= share_b``.
@@ -100,7 +98,6 @@
self.schedule_callback(result, self._finish_greater_than_equal, l)
return result
- @increment_pc
def _finish_greater_than_equal(self, results, l):
"""Finish the calculation."""
T = results[0]
@@ -128,7 +125,6 @@
return GF256(T.bit(l)) ^ (bit_bits[l] ^ vec[0][1])
- @increment_pc
def _diamond(self, (top_a, bot_a), (top_b, bot_b)):
"""The "diamond-operator".
@@ -160,7 +156,6 @@
elements and gives a secret result shared over Zp.
"""
- @increment_pc
def convert_bit_share(self, share, dst_field):
"""Convert a 0/1 share into *dst_field*."""
l = self.options.security_parameter + math.log(dst_field.modulus, 2)
@@ -188,7 +183,6 @@
return tmp - full_mask
@profile
- @increment_pc
def greater_than_equal_preproc(self, field, smallField=None):
"""Preprocessing for :meth:`greater_than_equal`."""
if smallField is None:
@@ -243,7 +237,6 @@
##################################################
@profile
- @increment_pc
def greater_than_equal_online(self, share_a, share_b, preproc, field):
"""Compute ``share_a >= share_b``. Result is secret shared."""
# increment l as a, b are increased
@@ -272,7 +265,6 @@
r_modl, r_bits, z)
return c
- @increment_pc
def _finish_greater_than_equal(self, c, field, smallField, s_bit, s_sign,
mask, r_modl, r_bits, z):
"""Finish the calculation."""
@@ -316,7 +308,6 @@
return (z - result) * ~field(2**l)
# END _finish_greater_than
- @increment_pc
def greater_than_equal(self, share_a, share_b):
"""Compute ``share_a >= share_b``.
diff -r 0f35ae3f503b -r 77f74e53f796 viff/equality.py
--- a/viff/equality.py Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/equality.py Sat Sep 19 15:34:01 2009 +0200
@@ -20,13 +20,10 @@
is mixed with.
"""
-from viff.runtime import increment_pc
-
class ProbabilisticEqualityMixin:
"""This class implements probabilistic constant-round secure
equality-testing of secret shared numbers."""
- @increment_pc
def equal(self, share_x, share_y):
"""Equality testing with secret shared result.
diff -r 0f35ae3f503b -r 77f74e53f796 viff/paillier.py
--- a/viff/paillier.py Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/paillier.py Sat Sep 19 15:34:01 2009 +0200
@@ -27,7 +27,7 @@
from twisted.internet.defer import Deferred, gatherResults
import gmpy
-from viff.runtime import Runtime, increment_pc, Share, gather_shares
+from viff.runtime import Runtime, Share, gather_shares
from viff.runtime import PAILLIER
from viff.util import rand, find_random_prime
@@ -78,7 +78,6 @@
else:
self.peer = player
- @increment_pc
def prss_share_random(self, field):
"""Generate a share of a uniformly random element."""
prfs = self.players[self.id].prfs(field.modulus)
@@ -94,7 +93,6 @@
"""
return self.share(inputters, field, number)
- @increment_pc
def share(self, inputters, field, number=None):
"""Share *number* additively."""
assert number is None or self.id in inputters
@@ -121,7 +119,6 @@
def output(self, share, receivers=None):
return self.open(share, receivers)
- @increment_pc
def open(self, share, receivers=None):
"""Open *share* to *receivers* (defaults to both players)."""
diff -r 0f35ae3f503b -r 77f74e53f796 viff/passive.py
--- a/viff/passive.py Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/passive.py Sat Sep 19 15:34:01 2009 +0200
@@ -22,8 +22,7 @@
import operator
from viff import shamir
-from viff.runtime import Runtime, increment_pc, Share, ShareList, \
- gather_shares, preprocess
+from viff.runtime import Runtime, Share, ShareList, gather_shares, preprocess
from viff.prss import prss, prss_lsb, prss_zero, prss_multi
from viff.field import GF256, FieldElement
from viff.util import rand, profile
@@ -57,7 +56,6 @@
def output(self, share, receivers=None, threshold=None):
return self.open(share, receivers, threshold)
- @increment_pc
def open(self, share, receivers=None, threshold=None):
"""Open a secret sharing.
@@ -175,7 +173,6 @@
return result
@profile
- @increment_pc
def mul(self, share_a, share_b):
"""Multiplication of shares.
@@ -232,7 +229,6 @@
else:
return share * (share ** (exponent-1))
- @increment_pc
def xor(self, share_a, share_b):
field = share_a.field
if not isinstance(share_b, Share):
@@ -245,7 +241,18 @@
else:
return share_a + share_b - 2 * share_a * share_b
- @increment_pc
+ def prss_key(self):
+ """Create unique key for PRSS.
+
+ This increments the program counter and returns it as a tuple.
+ Each straight-line program (typically a callback attached to
+ some :class:`Deferred`) is executed in a context with unique
+ starting program counter. This ensures that consequetive calls
+ to PRSS-related methods will use unique program counters.
+ """
+ self.program_counter[-1] += 1
+ return tuple(self.program_counter)
+
def prss_share(self, inputters, field, element=None):
"""Creates pseudo-random secret sharings.
@@ -273,7 +280,7 @@
n = self.num_players
# Key used for PRSS.
- key = tuple(self.program_counter)
+ key = self.prss_key()
# The shares for which we have all the keys.
all_shares = []
@@ -314,7 +321,6 @@
else:
return result
- @increment_pc
def prss_share_random(self, field, binary=False):
"""Generate shares of a uniformly random element from the field given.
@@ -329,7 +335,7 @@
modulus = field.modulus
# Key used for PRSS.
- prss_key = tuple(self.program_counter)
+ prss_key = self.prss_key()
prfs = self.players[self.id].prfs(modulus)
share = prss(self.num_players, self.id, field, prfs, prss_key)
@@ -354,7 +360,6 @@
self.schedule_callback(result, finish, share, binary)
return result
- @increment_pc
def prss_share_random_multi(self, field, quantity, binary=False):
"""Does the same as calling *quantity* times :meth:`prss_share_random`,
but with less calls to the PRF. Sampling of a binary element is only
@@ -371,13 +376,12 @@
modulus = field.modulus
# Key used for PRSS.
- prss_key = tuple(self.program_counter)
+ prss_key = self.prss_key()
prfs = self.players[self.id].prfs(modulus ** quantity)
shares = prss_multi(self.num_players, self.id, field, prfs, prss_key,
modulus, quantity)
return [Share(self, field, share) for share in shares]
- @increment_pc
def prss_share_zero(self, field, quantity):
"""Generate *quantity* shares of the zero element from the
field given.
@@ -385,13 +389,12 @@
Communication cost: none.
"""
# Key used for PRSS.
- prss_key = tuple(self.program_counter)
+ prss_key = self.prss_key()
prfs = self.players[self.id].prfs(field.modulus)
zero_share = prss_zero(self.num_players, self.threshold, self.id,
field, prfs, prss_key, quantity)
return [Share(self, field, zero_share[i]) for i in range(quantity)]
- @increment_pc
def prss_double_share(self, field, quantity):
"""Make *quantity* double-sharings using PRSS.
@@ -402,7 +405,6 @@
z_2t = self.prss_share_zero(field, quantity)
return (r_t, [r_t[i] + z_2t[i] for i in range(quantity)])
- @increment_pc
def prss_share_bit_double(self, field):
"""Share a random bit over *field* and GF256.
@@ -414,7 +416,7 @@
n = self.num_players
k = self.options.security_parameter
prfs = self.players[self.id].prfs(2**k)
- prss_key = tuple(self.program_counter)
+ prss_key = self.prss_key()
b_p = self.prss_share_random(field, binary=True)
r_p, r_lsb = prss_lsb(n, self.id, field, prfs, prss_key)
@@ -427,13 +429,12 @@
# Use r_lsb to flip b as needed.
return (b_p, b ^ r_lsb)
- @increment_pc
def prss_shamir_share_bit_double(self, field):
"""Shamir share a random bit over *field* and GF256."""
n = self.num_players
k = self.options.security_parameter
prfs = self.players[self.id].prfs(2**k)
- prss_key = tuple(self.program_counter)
+ prss_key = self.prss_key()
inputters = range(1, self.num_players + 1)
ri = rand.randint(0, 2**k - 1)
@@ -461,7 +462,6 @@
result.append(share)
return result
- @increment_pc
@preprocess("prss_powerchains")
def prss_powerchain(self, max=7):
"""Generate a random secret share in GF256 and returns
@@ -483,7 +483,6 @@
"""
return self.shamir_share(inputters, field, number, threshold)
- @increment_pc
def shamir_share(self, inputters, field, number=None, threshold=None):
"""Secret share *number* over *field* using Shamir's method.
diff -r 0f35ae3f503b -r 77f74e53f796 viff/runtime.py
--- a/viff/runtime.py Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/runtime.py Sat Sep 19 15:34:01 2009 +0200
@@ -407,25 +407,6 @@
reason.trap(ConnectionDone)
-def increment_pc(method):
- """Make *method* automatically increment the program counter.
-
- Adding this decorator to a :class:`Runtime` method will ensure
- that the program counter is incremented correctly when entering
- the method.
- """
-
- @wrapper(method)
- def inc_pc_wrapper(self, *args, **kwargs):
- try:
- self.program_counter[-1] += 1
- self.program_counter.append(0)
- return method(self, *args, **kwargs)
- finally:
- self.program_counter.pop()
- return inc_pc_wrapper
-
-
def preprocess(generator):
"""Track calls to this method.
@@ -623,7 +604,6 @@
dl = DeferredList(vars)
self.schedule_callback(dl, lambda _: self.shutdown())
- @increment_pc
def schedule_callback(self, deferred, func, *args, **kwargs):
"""Schedule a callback on a deferred with the correct program
counter.
@@ -637,7 +617,9 @@
Any extra arguments are passed to the callback as with
:meth:`addCallback`.
"""
+ self.program_counter[-1] += 1
saved_pc = self.program_counter[:]
+ saved_pc.append(0)
@wrapper(func)
def callback_wrapper(*args, **kwargs):
@@ -673,7 +655,6 @@
deferred.addCallback(queue_callback, self, fork)
return self.schedule_callback(fork, func, *args, **kwargs)
- @increment_pc
def synchronize(self):
"""Introduce a synchronization point.
@@ -729,7 +710,6 @@
self._expect_data(peer_id, SHARE, share)
return share
- @increment_pc
def preprocess(self, program):
"""Generate preprocess material.
@@ -868,7 +848,7 @@
self.depth_counter -= 1
self.activation_counter = 0
- def print_transferred_data():
+ def print_transferred_data(self):
"""Print the amount of transferred data for all connections."""
for protocol in self.protocols.itervalues():
diff -r 0f35ae3f503b -r 77f74e53f796 viff/test/test_basic_runtime.py
--- a/viff/test/test_basic_runtime.py Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/test/test_basic_runtime.py Sat Sep 19 15:34:01 2009 +0200
@@ -18,7 +18,6 @@
from twisted.internet.defer import Deferred, gatherResults
from viff.test.util import RuntimeTestCase, protocol
-from viff.runtime import increment_pc
class ProgramCounterTest(RuntimeTestCase):
@@ -32,26 +31,14 @@
def test_simple_operation(self, runtime):
"""Test an operation which makes no further calls.
- Each call should increment the program counter by one.
+ No callbacks are scheduled, and so the program counter is not
+ increased.
"""
+ self.assertEquals(runtime.program_counter, [0])
runtime.synchronize()
- self.assertEquals(runtime.program_counter, [1])
+ self.assertEquals(runtime.program_counter, [0])
runtime.synchronize()
- self.assertEquals(runtime.program_counter, [2])
-
- @protocol
- def test_complex_operation(self, runtime):
- """Test an operation which makes nested calls.
-
- This verifies that the program counter is only incremented by
- one, even for a complex operation.
- """
- # Exclusive-or is calculated as x + y - 2 * x * y, so add,
- # sub, and mul are called.
- runtime.xor(self.Zp(0), self.Zp(1))
- self.assertEquals(runtime.program_counter, [1])
- runtime.xor(self.Zp(0), self.Zp(1))
- self.assertEquals(runtime.program_counter, [2])
+ self.assertEquals(runtime.program_counter, [0])
@protocol
def test_callback(self, runtime):
@@ -62,62 +49,32 @@
"""
def verify_program_counter(_):
+ # The callback is run with its own sub-program counter.
self.assertEquals(runtime.program_counter, [1, 0])
d = Deferred()
+
+ self.assertEquals(runtime.program_counter, [0])
+
+ # Scheduling a callback increases the program counter.
runtime.schedule_callback(d, verify_program_counter)
-
- runtime.synchronize()
- self.assertEquals(runtime.program_counter, [2])
+ self.assertEquals(runtime.program_counter, [1])
# Now trigger verify_program_counter.
d.callback(None)
@protocol
- def test_nested_calls(self, runtime):
- """Test Runtime methods that call other methods.
-
- We create a couple of functions that are used as fake methods.
- """
-
- @increment_pc
- def method_a(runtime):
- # First top-level call, so first entry is 1. No calls to
- # other methods decorated with increment_pc has been made,
- # so the second entry is 0.
- self.assertEquals(runtime.program_counter, [1, 0])
- method_b(runtime, 1)
-
- self.assertEquals(runtime.program_counter, [1, 1])
- method_b(runtime, 2)
-
- # At this point two sub-calls has been made:
- self.assertEquals(runtime.program_counter, [1, 2])
-
- @increment_pc
- def method_b(runtime, count):
- # This method is called twice from method_a:
- self.assertEquals(runtime.program_counter, [1, count, 0])
-
- # Zero top-level calls:
- self.assertEquals(runtime.program_counter, [0])
- method_a(runtime)
-
- # One top-level call:
- self.assertEquals(runtime.program_counter, [1])
-
- @protocol
def test_multiple_callbacks(self, runtime):
d1 = Deferred()
d2 = Deferred()
def verify_program_counter(_, count):
- self.assertEquals(runtime.program_counter, [1, count, 0])
+ self.assertEquals(runtime.program_counter, [count, 0])
- @increment_pc
def method_a(runtime):
- self.assertEquals(runtime.program_counter, [1, 0])
+ # No calls to schedule_callback yet.
+ self.assertEquals(runtime.program_counter, [0])
runtime.schedule_callback(d1, verify_program_counter, 1)
runtime.schedule_callback(d2, verify_program_counter, 2)
diff -r 0f35ae3f503b -r 77f74e53f796 viff/test/test_thresholds.py
--- a/viff/test/test_thresholds.py Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/test/test_thresholds.py Sat Sep 19 15:34:01 2009 +0200
@@ -91,66 +91,26 @@
num_players = 3
threshold = 1
-
class Players4Threshold1Test(Tests, RuntimeTestCase):
num_players = 4
threshold = 1
-
-class Players5Threshold1Test(Tests, RuntimeTestCase):
- num_players = 5
- threshold = 1
-
class Players5Threshold2Test(Tests, RuntimeTestCase):
num_players = 5
threshold = 2
-
-class Players6Threshold1Test(Tests, RuntimeTestCase):
- num_players = 6
- threshold = 1
-
class Players6Threshold2Test(Tests, RuntimeTestCase):
num_players = 6
threshold = 2
-
-class Players7Threshold1Test(Tests, RuntimeTestCase):
- num_players = 7
- threshold = 1
-
-class Players7Threshold2Test(Tests, RuntimeTestCase):
- num_players = 7
- threshold = 2
-
class Players7Threshold3Test(Tests, RuntimeTestCase):
num_players = 7
threshold = 3
-class Players8Threshold1Test(Tests, RuntimeTestCase):
- num_players = 8
- threshold = 1
-
-class Players8Threshold2Test(Tests, RuntimeTestCase):
- num_players = 8
- threshold = 2
-
class Players8Threshold3Test(Tests, RuntimeTestCase):
num_players = 8
threshold = 3
-class Players9Threshold1Test(Tests, RuntimeTestCase):
- num_players = 9
- threshold = 1
-
-class Players9Threshold2Test(Tests, RuntimeTestCase):
- num_players = 9
- threshold = 2
-
-class Players9Threshold3Test(Tests, RuntimeTestCase):
- num_players = 9
- threshold = 3
-
class Players9Threshold4Test(Tests, RuntimeTestCase):
num_players = 9
threshold = 4
_______________________________________________
viff-commits mailing list
[email protected]
http://lists.viff.dk/listinfo.cgi/viff-commits-viff.dk