public void testTransformDiamondProperty(int numIterations) { log.info("TESTING testTransformDiamondProperty"); Random r = new Random(0); for (int iteration = 0; iteration < numIterations; iteration++) { log.info("Iteration: " + iteration); D d1 = domain.initialState(); try { for (int i = 0; i < INITIAL_MUTATION_COUNT; i++) { O op = generator.randomOperation(d1, r); domain.apply(op, d1); } D d2 = copy(d1); for (int i = 0; i < FEATURE_ITERATION_COUNT; i++) { D original = copy(d1); O op1 = generator.randomOperation(original, r); O op2 = generator.randomOperation(original, r); domain.apply(op1, d1); domain.apply(op2, d2); D client = copy(d1); D server = copy(d2); OperationPair<O> pair = domain.transform(op1, op2); domain.apply(pair.serverOp(), d1); domain.apply(pair.clientOp(), d2); if (!domain.equivalent(d1, d2)) { log.inconsistent( "TRANSFORM BUG", "Subiteration: " + i, "Client: " + op1, "Server: " + op2, "Client': " + pair.clientOp(), "Server': " + pair.serverOp(), "Initial state: " + original, "Client state 1:" + client, "Client state 2:" + d1, "Server state 1:" + server, "Server state 2:" + d2); } } } catch (OperationException e) { logException("TRANSFORM BUG? Operation exception", e); } catch (TransformException e) { logException("TRANSFORM BUG? Transform exception", e); } catch (RuntimeException e) { logException("TRANSFORM BUG? Runtime exception", e); } } }
/** * Transforms the specified client operations against the specified server operations, returning * the transformed client operations in a new list. * * @param clientOps may be unmodifiable * @param clientAuthor * @param serverOps may be unmodifiable * @param serverAuthor * @return The transformed client ops */ private List<WaveletOperation> transformOps( List<WaveletOperation> clientOps, ParticipantId clientAuthor, List<WaveletOperation> serverOps, ParticipantId serverAuthor) throws OperationException { List<WaveletOperation> transformedClientOps = new ArrayList<WaveletOperation>(); for (WaveletOperation c : clientOps) { for (WaveletOperation s : serverOps) { OperationPair<WaveletOperation> pair; try { pair = Transform.transform(c, clientAuthor, s, serverAuthor); } catch (TransformException e) { throw new OperationException(e); } c = pair.clientOp(); } transformedClientOps.add(c); } return transformedClientOps; }
/** * Tests that transformation and composition are compatible * * <p>Assumes the diamond property of transformation * * <p>NOTE: This should be rewritten to do proper comparison of operations. * * @param numIterations */ public void testTransformationCompositionCompatible(int numIterations) { log.info("TESTING testTransformationCompositionCompatible"); Random r = new Random(0); for (int iteration = 0; iteration < numIterations; iteration++) { log.info("Iteration: " + iteration); D server = domain.initialState(); try { for (int i = 0; i < INITIAL_MUTATION_COUNT; i++) { O op = generator.randomOperation(server, r); domain.apply(op, server); } D client = copy(server); for (int i = 0; i < FEATURE_ITERATION_COUNT; i++) { D original = copy(server); if (!domain.equivalent(client, server)) { log.inconsistent( "Sanity check failed: client and server not the same at start of test"); } // Client is on the left for the first pass, but this // is reversed in the second pass (the meaning of the // variables "client" and "server" also reverses). // // original (o) // / \ // client a b server // / \ / // c d // \ / // end (e) // O oa = generator.randomOperation(client, r); O ob = generator.randomOperation(server, r); domain.apply(oa, client); domain.apply(ob, server); D a = copy(client); D b = copy(server); OperationPair<O> pair1 = domain.transform(oa, ob); O bd = pair1.clientOp(); O ad = pair1.serverOp(); O ac = generator.randomOperation(a, r); domain.apply(ac, client); D c = copy(client); domain.apply(bd, server); D d = copy(server); D test = copy(a); domain.apply(ad, test); OperationPair<O> pair2 = domain.transform(ac, ad); O ce = pair2.serverOp(); O de = pair2.clientOp(); domain.apply(de, server); domain.apply(ce, client); D end = copy(client); O oc = domain.compose(ac, oa); O be = domain.compose(de, bd); // The property we want to test is that ce = ce2 and be = be2 // OperationPair<O> pair3 = domain.transform(oc, ob); O ce2 = pair3.serverOp(); O be2 = pair3.clientOp(); D d1 = copy(c); domain.apply(ce2, d1); D d2 = copy(b); domain.apply(be2, d2); boolean ceOK = domain.equivalent(end, d1); boolean beOK = domain.equivalent(end, d2); if (!ceOK || !beOK) { log.inconsistent( "TRANSFORM AND COMPOSITION NOT COMPATIBLE", "Subiteration: " + i, ceOK ? "GOOD:" : "BAD:", "ce: " + ce, "ce2: " + ce2, beOK ? "GOOD:" : "BAD:", "be: " + be, "be2: " + be2, "-- States without compose: ", " original (o)", " / \\", " client a b server", " / \\ /", " c d", " \\ /", " end (e)", "o: " + original, "a: " + a, "b: " + b, "c: " + c, "d: " + d, "e: " + end); } } } catch (OperationException e) { logException("TRANSFORM BUG? Operation exception", e); } catch (TransformException e) { logException("TRANSFORM BUG? Transform exception", e); } catch (RuntimeException e) { logException("TRANSFORM BUG? Runtime exception", e); } } }