OPENDJ-1192 Modify request replay failures
Added a set of tests for modify replay.
It produces series of changes and replay them out of order
by generating all possible permutations.
The goal for replay to end up in the same final state whatever the replay order.
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * Copyright 2015 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import static org.forgerock.opendj.ldap.ModificationType.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | import static org.opends.server.util.CollectionUtils.*; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.ModifyContext; |
| | | import org.opends.server.replication.protocol.OperationContext; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.Attributes; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.operation.PreOperationModifyOperation; |
| | | import org.testng.Assert; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | /** |
| | | * Tests the modify replay logic. |
| | | * <p> |
| | | * It produces series of changes and replay them out of order by generating all possible |
| | | * permutations. The goal is to end up in the same final state whatever the order. |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class ModifyReplayTest extends ReplicationTestCase |
| | | { |
| | | private static final String ATTRIBUTE_NAME = "displayName"; |
| | | private static final String SYNCHIST = "ds-sync-hist"; |
| | | |
| | | private Entry entry; |
| | | |
| | | private static class Mod |
| | | { |
| | | private final int time; |
| | | private final Modification modification; |
| | | |
| | | private Mod(ModificationType modType, int t) |
| | | { |
| | | this(newModification(modType), t); |
| | | } |
| | | |
| | | private Mod(ModificationType modType, String value, int t) |
| | | { |
| | | this(newModification(modType, value), t); |
| | | } |
| | | |
| | | private Mod(Modification modification, int time) |
| | | { |
| | | this.modification = modification; |
| | | this.time = time; |
| | | } |
| | | |
| | | private PreOperationModifyOperation toOperation() |
| | | { |
| | | final ModifyContext value = new ModifyContext(new CSN(0, time, 0), null); |
| | | |
| | | PreOperationModifyOperation op = mock(PreOperationModifyOperation.class); |
| | | when(op.getModifications()).thenReturn(newArrayList(modification)); |
| | | when(op.getAttachment(eq(OperationContext.SYNCHROCONTEXT))).thenReturn(value); |
| | | return op; |
| | | } |
| | | |
| | | /** Implemented to get a nice display for each tests in Eclipse UI. */ |
| | | @Override |
| | | public String toString() |
| | | { |
| | | String modType = modification.getModificationType().toString().toUpperCase(); |
| | | Iterator<ByteString> it = modification.getAttribute().iterator(); |
| | | String attrValue = it.hasNext() ? "\"" + it.next().toString() + "\" " : ""; |
| | | return modType + " " + attrValue + "t" + time; |
| | | } |
| | | } |
| | | |
| | | private static Object[][] generatePermutations(Object[][] scenarios) |
| | | { |
| | | List<Object[]> results = new ArrayList<Object[]>(); |
| | | for (Object[] scenario : scenarios) |
| | | { |
| | | generate((Object[]) scenario[0], (Attribute) scenario[1], results); |
| | | } |
| | | |
| | | return results.toArray(new Object[results.size()][]); |
| | | } |
| | | |
| | | private static void generate(Object[] array, Attribute dsSyncHist, List<Object[]> results) |
| | | { |
| | | generate(array.length, array, dsSyncHist, results); |
| | | } |
| | | |
| | | private static void generate(int n, Object[] array, Attribute dsSyncHist, List<Object[]> results) |
| | | { |
| | | if (n == 1) |
| | | { |
| | | results.add(new Object[] { Arrays.asList(Arrays.copyOf(array, array.length)), dsSyncHist, }); |
| | | return; |
| | | } |
| | | |
| | | for (int i = 0; i < n - 1; i += 1) |
| | | { |
| | | generate(n - 1, array, dsSyncHist, results); |
| | | if (n % 2 == 0) |
| | | { |
| | | swap(array, i, n - 1); |
| | | } |
| | | else |
| | | { |
| | | swap(array, 0, n - 1); |
| | | } |
| | | } |
| | | generate(n - 1, array, dsSyncHist, results); |
| | | } |
| | | |
| | | private static <E> void swap(E[] array, int i, int j) |
| | | { |
| | | E tmp = array[i]; |
| | | array[i] = array[j]; |
| | | array[j] = tmp; |
| | | } |
| | | |
| | | @DataProvider |
| | | public Object[][] add_data() |
| | | { |
| | | // @formatter:off |
| | | return generatePermutations(new Object[][] { |
| | | { mods(new Mod(ADD, "X", 1)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(ADD, "X", 1), new Mod(ADD, "X", 2)), dsSyncHist(2, ":add:X"), }, |
| | | { mods(new Mod(ADD, "X", 1), new Mod(ADD, "Y", 2)), dsSyncHist(2, ":add:Y"), }, |
| | | { mods(new Mod(ADD, "X", 1), new Mod(DELETE, "X", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(ADD, "X", 1), new Mod(DELETE, "Y", 2)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(ADD, "X", 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(ADD, "X", 1), new Mod(REPLACE, "X", 2)), dsSyncHist(2, ":repl:X"), }, |
| | | { mods(new Mod(ADD, "X", 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(2, ":repl:Y"), }, |
| | | { mods(new Mod(ADD, "X", 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), } |
| | | }); |
| | | // @formatter:off |
| | | } |
| | | |
| | | @DataProvider |
| | | public Object[][] delete_noInitialValue_data() |
| | | { |
| | | // @formatter:off |
| | | return generatePermutations(new Object[][] { |
| | | { mods(new Mod(DELETE, "X", 1)), dsSyncHist(1, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(ADD, "X", 2)), dsSyncHist(2, ":add:X"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(ADD, "Y", 2)), dsSyncHist(2, ":add:Y"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(DELETE, "X", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(DELETE, "Y", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(REPLACE, "X", 2)), dsSyncHist(2, ":repl:X"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(2, ":repl:Y"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | |
| | | { mods(new Mod(DELETE, 1)), dsSyncHist(1, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(ADD, "X", 2)), dsSyncHist(2, ":add:X"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(ADD, "Y", 2)), dsSyncHist(2, ":add:Y"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(DELETE, "X", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(DELETE, "Y", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(REPLACE, "X", 2)), dsSyncHist(2, ":repl:X"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(2, ":repl:Y"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | }); |
| | | // @formatter:off |
| | | } |
| | | |
| | | @DataProvider |
| | | public Object[][] delete_initialValueX_data() |
| | | { |
| | | // @formatter:off |
| | | return generatePermutations(new Object[][] { |
| | | { mods(new Mod(DELETE, "X", 1)), dsSyncHist(1, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(ADD, "X", 2)), dsSyncHist(2, ":add:X"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(ADD, "Y", 2)), dsSyncHist(2, ":add:Y"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(DELETE, "X", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(DELETE, "Y", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(REPLACE, "X", 2)), dsSyncHist(2, ":repl:X"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(2, ":repl:Y"), }, |
| | | { mods(new Mod(DELETE, "X", 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | |
| | | { mods(new Mod(DELETE, "Y", 1)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(DELETE, "Y", 1), new Mod(ADD, "X", 2)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(DELETE, "Y", 1), new Mod(ADD, "Y", 2)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(DELETE, "Y", 1), new Mod(DELETE, "X", 2)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(DELETE, "Y", 1), new Mod(DELETE, "Y", 2)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(DELETE, "Y", 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, "Y", 1), new Mod(REPLACE, "X", 2)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(DELETE, "Y", 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(DELETE, "Y", 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | |
| | | { mods(new Mod(DELETE, 1)), dsSyncHist(1, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(ADD, "X", 2)), dsSyncHist(2, ":add:X"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(ADD, "Y", 2)), dsSyncHist(2, ":add:Y"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(DELETE, "X", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(DELETE, "Y", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(REPLACE, "X", 2)), dsSyncHist(2, ":repl:X"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(2, ":repl:Y"), }, |
| | | { mods(new Mod(DELETE, 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | }); |
| | | // @formatter:off |
| | | } |
| | | |
| | | @DataProvider |
| | | public Object[][] replace_noInitialValue_data() |
| | | { |
| | | // @formatter:off |
| | | return generatePermutations(new Object[][] { |
| | | { mods(new Mod(REPLACE, "X", 1)), dsSyncHist(1, ":repl:X"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(ADD, "X", 2)), dsSyncHist(2, ":add:X"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(ADD, "Y", 2)), dsSyncHist(2, ":add:Y"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(DELETE, "X", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(DELETE, "Y", 2)), dsSyncHist(1, ":repl:X"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(REPLACE, "X", 2)), dsSyncHist(2, ":repl:X"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(2, ":repl:Y"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | |
| | | { mods(new Mod(REPLACE, 1)), dsSyncHist(1, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(ADD, "X", 2)), dsSyncHist(2, ":add:X"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(ADD, "Y", 2)), dsSyncHist(2, ":add:Y"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(DELETE, "X", 2)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(DELETE, "Y", 2)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(REPLACE, "X", 2)), dsSyncHist(2, ":repl:X"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(2, ":repl:Y"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | }); |
| | | // @formatter:off |
| | | } |
| | | |
| | | @DataProvider |
| | | public Object[][] replace_initialValueX_data() |
| | | { |
| | | // @formatter:off |
| | | return generatePermutations(new Object[][] { |
| | | { mods(new Mod(REPLACE, "X", 1)), dsSyncHist(1, ":repl:X"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(ADD, "X", 2)), dsSyncHist(2, ":add:X"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(ADD, "Y", 2)), dsSyncHist(2, ":add:Y"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(DELETE, "X", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(DELETE, "Y", 2)), dsSyncHist(1, ":add:X"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(REPLACE, "X", 2)), dsSyncHist(2, ":repl:X"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(2, ":repl:Y"), }, |
| | | { mods(new Mod(REPLACE, "X", 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | |
| | | { mods(new Mod(REPLACE, "Y", 1)), dsSyncHist(1, ":repl:Y"), }, |
| | | { mods(new Mod(REPLACE, "Y", 1), new Mod(ADD, "X", 2)), dsSyncHist(2, ":add:X"), }, |
| | | { mods(new Mod(REPLACE, "Y", 1), new Mod(ADD, "Y", 2)), dsSyncHist(2, ":add:Y"), }, |
| | | { mods(new Mod(REPLACE, "Y", 1), new Mod(DELETE, "X", 2)), dsSyncHist(1, ":repl:Y"), }, |
| | | { mods(new Mod(REPLACE, "Y", 1), new Mod(DELETE, "Y", 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, "Y", 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, "Y", 1), new Mod(REPLACE, "X", 2)), dsSyncHist(2, ":repl:X"), }, |
| | | { mods(new Mod(REPLACE, "Y", 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(2, ":repl:Y"), }, |
| | | { mods(new Mod(REPLACE, "Y", 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | |
| | | { mods(new Mod(REPLACE, 1)), dsSyncHist(1, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(ADD, "X", 2)), dsSyncHist(2, ":add:X"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(ADD, "Y", 2)), dsSyncHist(2, ":add:Y"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(DELETE, "X", 2)), dsSyncHist(1, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(DELETE, "Y", 2)), dsSyncHist(1, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(DELETE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(REPLACE, "X", 2)), dsSyncHist(2, ":repl:X"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(REPLACE, "Y", 2)), dsSyncHist(2, ":repl:Y"), }, |
| | | { mods(new Mod(REPLACE, 1), new Mod(REPLACE, 2)), dsSyncHist(2, ":attrDel"), }, |
| | | }); |
| | | // @formatter:off |
| | | } |
| | | |
| | | @Test(dataProvider = "add_data", enabled=false) |
| | | public void add_noInitialValue(List<Mod> mods, Attribute expectedDsSyncHist) throws Exception |
| | | { |
| | | noValue(); // also covers the initialValue("X"); case |
| | | replay(mods, expectedDsSyncHist); |
| | | } |
| | | |
| | | @Test(dataProvider = "delete_noInitialValue_data", enabled=false) |
| | | public void delete_noInitialValue(List<Mod> mods, Attribute expectedDsSyncHist) throws Exception |
| | | { |
| | | noValue(); |
| | | replay(mods, expectedDsSyncHist); |
| | | } |
| | | |
| | | @Test(dataProvider = "delete_initialValueX_data", enabled=false) |
| | | public void delete_initialValueX(List<Mod> mods, Attribute expectedDsSyncHist) throws Exception |
| | | { |
| | | initialValue("X"); |
| | | replay(mods, expectedDsSyncHist); |
| | | } |
| | | |
| | | @Test(dataProvider = "replace_noInitialValue_data", enabled=false) |
| | | public void replace_noInitialValue(List<Mod> mods, Attribute expectedDsSyncHist) throws Exception |
| | | { |
| | | noValue(); |
| | | replay(mods, expectedDsSyncHist); |
| | | } |
| | | |
| | | @Test(dataProvider = "replace_initialValueX_data", enabled=false) |
| | | public void replace_initialValueX(List<Mod> mods, Attribute expectedDsSyncHist) throws Exception |
| | | { |
| | | initialValue("X"); |
| | | replay(mods, expectedDsSyncHist); |
| | | } |
| | | |
| | | private void noValue() throws Exception |
| | | { |
| | | entry = TestCaseUtils.makeEntry( |
| | | "dn: uid=test.user", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "uid: test.user", |
| | | "givenName: Test", |
| | | "sn: User", |
| | | "cn: Test User", |
| | | "userPassword: password"); |
| | | } |
| | | |
| | | private void initialValue(String attrValue) throws Exception |
| | | { |
| | | noValue(); |
| | | entry.applyModification(newModification(REPLACE, attrValue)); |
| | | } |
| | | |
| | | private void replay(List<Mod> mods, Attribute expectedDsSyncHist) throws DirectoryException |
| | | { |
| | | for (Mod op : mods) |
| | | { |
| | | EntryHistorical entryHistorical = EntryHistorical.newInstanceFromEntry(entry); |
| | | entryHistorical.replayOperation(op.toOperation(), entry); |
| | | entry.applyModification(new Modification(REPLACE, entryHistorical.encodeAndPurge())); |
| | | } |
| | | |
| | | AttributeType attrType = expectedDsSyncHist.getAttributeType(); |
| | | Attribute actual = entry.getExactAttribute(attrType, Collections.<String> emptySet()); |
| | | |
| | | Assert.assertEquals(actual, expectedDsSyncHist, "wrong final value for ds-sync-hist attribute"); |
| | | } |
| | | |
| | | private static Object[] mods(Mod... mods) |
| | | { |
| | | return mods; |
| | | } |
| | | |
| | | private static Object dsSyncHist(int t, String partialDsSyncHist) |
| | | { |
| | | String value = ATTRIBUTE_NAME + ":000000000000000000000000000" + t + partialDsSyncHist; |
| | | return Attributes.create(SYNCHIST, value); |
| | | } |
| | | |
| | | private static Modification newModification(ModificationType modType) |
| | | { |
| | | return new Modification(modType, Attributes.empty(ATTRIBUTE_NAME)); |
| | | } |
| | | |
| | | private static Modification newModification(ModificationType modType, String value) |
| | | { |
| | | return new Modification(modType, Attributes.create(ATTRIBUTE_NAME, value)); |
| | | } |
| | | } |